Identity and access control
Enduro optionally supports external OpenID Connect (OIDC) compatible providers for authentication and access control. Users can authenticate against the external provider from the dashboard to receive an access token that will be sent to the API on each request.
Enduro uses Attribute Based Access Control (ABAC) to determine the actions and resources to which an authenticated user has access. It looks for a configurable claim in the access token to know the attributes assigned to the user in their external provider.
This section explains the identity provider requirements and the authentication configuration for Enduro's API, dashboard, and ingest storage client.
Identity provider requirements
Enduro supports OIDC for user authentication. Dashboard login and authentication between services use different OAuth 2.0 flows and can use different identity providers.
Dashboard login
The dashboard uses the Authorization Code flow with Proof Key for Code
Exchange (PKCE), using the S256 challenge method. It is a public browser
application, also called a single-page application (SPA), and does not use a
client secret.
The identity provider's dashboard registration must allow this flow and include the sign-in and post-logout redirect URIs described in Dashboard configuration.
The dashboard requests access tokens for the Enduro API. Depending on the
provider, this may require API scopes or additional authorization request
parameters beyond the default openid email profile scopes.
Authentication between services
When ingest-to-storage authentication is enabled, ingest uses the Client Credentials flow to obtain access tokens for the storage API without user interaction. This requires a confidential client with a client ID and client secret. A separate client registration is recommended so that service credentials and permissions can be managed independently of dashboard login.
The service client can use the same identity provider as the dashboard or a different provider. At least one of the API's configured OIDC verifiers must accept its tokens. When ABAC is enabled for that verifier, the tokens must also contain the attributes required by the storage endpoints used by ingest.
The provider, credentials, and any required scopes or audience parameter are
configured under [ingest.storage.oidc], as described in
Ingest storage client configuration.
Access-token requirements
The API validates signed JWT access tokens, including their signature, issuer,
audience, and expiry. The issuer must match a configured OIDC provider, and the
aud claim must include the corresponding API clientID value.
User access tokens must include a stable sub claim uniquely identifying the
user within the issuer. Enduro uses the combination of iss and sub to
identify users when recording ingest actions and handling deletion requests.
Requesting, reviewing, or canceling an AIP deletion also requires at least one
nonempty email, preferred_username, or name claim, checked in that order,
for the display name. These identity claims are also required in service tokens
used for automatic AIP deletion, such as during batch cancellation.
When attribute-based access control (ABAC) is enabled, the configured
permissions claim must contain an array ofstrings in the access token. These
values can be Enduro attributes or roles mapped to attributes through
rolesMapping. The dashboard and API must use matching access control settings
for user tokens. See API configuration and Required
attributes for the configuration and supported
permissions.
By default, the API also requires email_verified: true in the access token.
The skipEmailVerifiedCheck setting disables this requirement for an OIDC
verifier, for example when a provider does not supply the claim or when service
identities do not have an email address. Claims used by the API must be present
in the access token; providing them only in an ID token or UserInfo response is
insufficient.
API configuration
The following example configures enduro for dashboard users and enduro-s2s
for service authentication. ABAC is enabled for dashboard users only:
[api]
# TCP address for the server to listen on, in the form "host:port".
listen = "0.0.0.0:9000"
# Allowed CORS origin URL.
corsOrigin = "https://enduro.example.com"
[api.auth]
# Verify the access token submitted with each request.
enabled = true
# User access tokens obtained by the dashboard.
[[api.auth.oidc]]
providerURL = "https://idp.example.com"
clientID = "enduro"
skipEmailVerifiedCheck = false
[api.auth.oidc.abac]
enabled = true
claimPath = "roles"
claimPathSeparator = ""
claimValuePrefix = ""
useRoles = true
rolesMapping = '{"admin": ["*"]}'
# Service access tokens obtained by ingest.
[[api.auth.oidc]]
providerURL = "https://idp.example.com"
clientID = "enduro-s2s"
# Skip the email verification check for service tokens.
skipEmailVerifiedCheck = true
[api.auth.ticket.redis]
# Redis URI to store tickets used for browser download handoffs.
address = "redis://redis:6379"
# Prefix used as part of the ticket keys in Redis.
prefix = "enduro"
The API authentication reference describes all verifier and ABAC settings. The required attributes determine the permissions to map for additional user roles or service operations.
Ingest storage client configuration
Use the following section to configure ingest as an authenticated client of the storage API using the OAuth 2.0 Client Credentials flow. Tokens generated by this OIDC provider must be verified by at least one of the providers from the full API OIDC configuration.
[ingest.storage]
# Storage API host:port for ingest client requests.
address = "enduro-api:9000"
# Default destination location for permanent storage workflows.
defaultPermanentLocationId = "f2cc963f-c14d-4eaa-b950-bd207189a1f1"
[ingest.storage.oidc]
# Enable service to service OIDC authentication for storage API requests.
enabled = true
# OIDC provider URL used for token endpoint discovery.
providerURL = "https://idp.example.com"
# Optional token endpoint URL. If set, discovery is skipped.
tokenURL = ""
# OIDC client credentials used for client_credentials token requests.
clientID = "enduro-s2s"
clientSecret = "replace-me"
# Optional scopes requested during token retrieval.
scopes = ""
# Optional audience to include in token endpoint params.
audience = ""
# Refresh token before expiry to avoid edge races.
tokenExpiryLeeway = "30s"
# Retry settings for transient token endpoint failures.
retryMaxAttempts = 3
retryInitialInterval = "500ms"
retryMaxInterval = "2s"
retryBackoffCoefficient = 2.0
Dashboard configuration
The dashboard's OIDC settings configure authentication and access control. This section describes how those settings relate to the identity provider and API configuration.
Important
The VITE_OIDC_AUTHORITY environment variable is included in the Content
Security Policy's connect-src directive. If this URL contains a path,
ensure it ends with a slash (/) to allow connections to the necessary
OIDC endpoints for authentication.
VITE_OIDC_AUTHORITY identifies the provider used for dashboard login and must
correspond to one of the API's configured OIDC providers. VITE_OIDC_CLIENT_ID
identifies the public dashboard client. The API's corresponding clientID
setting must be included in the aud claim of the resulting access token and
can differ from VITE_OIDC_CLIENT_ID.
VITE_OIDC_BASE_URL will be used to generate the signin and signout callback
URLs, to set them in the OIDC provider for this client, they will be:
- Signin:
VITE_OIDC_BASE_URL+/user/signin-callback - Signout:
VITE_OIDC_BASE_URL+/user/signout-callback
The Authorization Code flow with PKCE requests the openid email profile
scopes by default. VITE_OIDC_SCOPES replaces that list with a space-separated
list of scopes. When additional API scopes are required, the configured list
must also retain openid and any required profile scopes.
VITE_OIDC_EXTRA_QUERY_PARAMS can be set to specify further query string
parameters required by the provider in the authorization request. The expected
format is comma-separated key=value pairs, for example
audience=api-audience,key=value.
The ABAC variables work in the same way as the API's ABAC settings. In this example, they match the user token verifier:
VITE_OIDC_ENABLED=true
VITE_OIDC_BASE_URL=https://enduro.example.com
VITE_OIDC_AUTHORITY=https://idp.example.com
VITE_OIDC_CLIENT_ID=enduro
VITE_OIDC_ABAC_ENABLED=true
VITE_OIDC_ABAC_CLAIM_PATH=roles
VITE_OIDC_ABAC_USE_ROLES=true
VITE_OIDC_ABAC_ROLES_MAPPING='{"admin": ["*"]}'
See Building and serving the dashboard for applying these settings at build time, or Environment variable injection for configuring pre-built assets.
Required attributes
The following table shows the attributes required for each API endpoint. The
attributes allow a wildcard hierarchical declaration. For example,
ingest:sips:* will give access to endpoints requiring ingest:sips:list,
ingest:sips:read, etc. The * attribute will provide full access to the API.
The ingest and storage monitor streams are SSE endpoints authenticated with the same bearer token used by the rest of the API. User claims are checked before sending events to the stream.
Similarly, to be able to stream a SIP, AIP or AIP deletion report download from
the browser, the GET endpoints require a cookie obtained from the POST
endpoints.
| Method | Endpoint | Attributes |
|---|---|---|
| GET | /about | - |
| POST | /ingest/batches | ingest:batches:create |
| GET | /ingest/batches | ingest:batches:list |
| GET | /ingest/batches/{uuid} | ingest:batches:read |
| POST | /ingest/batches/{uuid}/review | ingest:batches:review |
| GET | /ingest/monitor | - |
| GET | /ingest/sip-sources/{uuid}/objects | ingest:sipsources:objects:list |
| GET | /ingest/sips | ingest:sips:list |
| POST | /ingest/sips | ingest:sips:create |
| POST | /ingest/sips/upload | ingest:sips:upload |
| GET | /ingest/sips/{uuid} | ingest:sips:read |
| POST | /ingest/sips/{uuid}/confirm | ingest:sips:review |
| GET | /ingest/sips/{uuid}/decision | ingest:sips:decision |
| POST | /ingest/sips/{uuid}/decision | ingest:sips:decision |
| GET | /ingest/sips/{uuid}/download | - |
| POST | /ingest/sips/{uuid}/download | ingest:sips:download |
| POST | /ingest/sips/{uuid}/reject | ingest:sips:review |
| GET | /ingest/sips/{uuid}/workflows | ingest:sips:workflows:list |
| GET | /ingest/users | ingest:users:list |
| GET | /storage/aips | storage:aips:list |
| POST | /storage/aips | storage:aips:create |
| GET | /storage/aips/{uuid} | storage:aips:read |
| POST | /storage/aips/{uuid}/deletion-auto | storage:aips:deletion:auto |
| POST | /storage/aips/{uuid}/deletion-cancel | storage:aips:deletion:request |
| GET | /storage/aips/{uuid}/deletion-report | - |
| POST | /storage/aips/{uuid}/deletion-report | storage:aips:deletion:report |
| POST | /storage/aips/{uuid}/deletion-request | storage:aips:deletion:request |
| POST | /storage/aips/{uuid}/deletion-review | storage:aips:deletion:review |
| GET | /storage/aips/{uuid}/download | - |
| POST | /storage/aips/{uuid}/download | storage:aips:download |
| POST | /storage/aips/{uuid}/reject | storage:aips:review |
| GET | /storage/aips/{uuid}/store | storage:aips:move |
| POST | /storage/aips/{uuid}/store | storage:aips:move |
| GET | /storage/aips/{uuid}/workflows | storage:aips:workflows:list |
| GET | /storage/locations | storage:locations:list |
| POST | /storage/locations | storage:locations:create |
| GET | /storage/locations/{uuid} | storage:locations:read |
| GET | /storage/locations/{uuid}/aips | storage:locations:aips:list |
| GET | /storage/monitor | - |