API Authentication#
Note
This document is about API authentication for anyone except robots. For robots, refer to HMAC Auth.
Quickstart#
For Python users, the quickest way to get started is with the SDK, which will handle authentication for you. Just create a client with the appropriate environment and you’re good to go:
# Requires destiny-sdk>=0.13.0
from destiny_sdk.client import OAuthClient
client = OAuthClient(env="staging")
response = client.search(query="example")
print(response)
Read on for the underlying OAuth2 flow, alternative authentication methods (Postman, curl, service accounts), and overriding configuration.
Background#
Interaction with the DESTINY Repository API requires first obtaining an authentication token from the DESTINY authentication server (a Keycloak instance). This token must then be included in the Authorization header of each API request.
sequenceDiagram
actor Client
participant Auth Server
participant API
Client->>Auth Server: Request token (client credentials)
Auth Server-->>Client: Return access token
Client->>API: API request with Authorization: Bearer <token>
API-->>Client: Return requested data
Provisioning#
In order to obtain a token from the DESTINY authentication server, you will need to be enrolled in our auth server. Please reach out if you need access.
Everyone enrolled will have reference.reader, but please reach out if you need additional permissions, naming the environments they apply to. You can see which permission each API resource requires in the API documentation - it is listed under each sub-category.
Obtaining a token#
Interactive user authentication#
For human users logging in with their own credentials. Uses the OAuth 2.0 authorization code flow with PKCE; the user is redirected to a browser to authenticate.
Using the SDK#
This is the recommended way to obtain tokens, as the SDK will handle token caching and refreshing for you, and will be kept up to date with any changes to the API authentication process.
The only information you need to authenticate is the environment you want to access (development, staging, or production). Other configuration is overwritable but will default to the correct values for each environment.
from destiny_sdk.client import OAuthMiddleware
auth = OAuthMiddleware(env="production")
See OAuthMiddleware for the full set of configuration options.
Using another method#
If you are not using Python, or want to authenticate from a tool like Postman, you can drive Keycloak’s OAuth 2.0 authorization code flow with PKCE directly. The values below configure any standards-compliant OAuth 2.0 client.
Field |
Value |
|---|---|
Authorization endpoint |
|
Token endpoint |
|
Realm |
|
Client ID (development) |
|
Client ID (staging) |
|
Client ID (production) |
|
Grant type |
Authorization Code (with PKCE) |
Code challenge method |
|
Scopes |
|
Request no other scopes - your permissions are already carried in the token.
The redirect URI you supply must be registered on the Keycloak client. At present we allow localhost and postman redirect URIs, but if you want to use a different one, please reach out so we can add it.
Service-to-service authentication#
For backend services, scheduled jobs, or anything else that runs without a human user. Uses the OAuth 2.0 client credentials flow. This requires a dedicated Keycloak client with Service Accounts enabled — please reach out so we can provision one, telling us which environments it needs and what it needs to do there.
Using the SDK#
from pydantic import SecretStr
from destiny_sdk.client import OAuthMiddleware
auth = OAuthMiddleware(
client_id="my-service-client",
client_secret=SecretStr("..."),
)
The SDK detects client_secret and switches to the client credentials flow automatically. env is not used in this mode.
Using another method#
A single POST against the token endpoint:
curl -X POST https://auth.evidence-repository.org/realms/destiny/protocol/openid-connect/token \
-d grant_type=client_credentials \
-d client_id=$DESTINY_CLIENT_ID \
-d client_secret=$DESTINY_CLIENT_SECRET \
-d scope=openid
The response contains access_token. Unlike the interactive flow there is no refresh_token — re-POST the same request when the token expires.
Using the token#
The API base URL for each environment is as follows:
Environment |
API URL |
|---|---|
Development |
|
Staging |
|
Production |
Using the SDK#
Again, we recommend using the SDK to make API requests, as it will handle including the token for you. Some endpoints will have convenience methods available, otherwise you can access the underlying httpx client directly.
- class destiny_sdk.client.OAuthClient(base_url: HttpUrl | str | None = None, auth: Auth | None = None, timeout: int = 10, *, env: Literal['development', 'staging', 'production'] | None = None)[source]
Client for interaction with the Destiny API using OAuth2.
This will apply the provided authentication, usually
OAuthMiddleware, to all requests. Some API endpoints are supported directly through methods on this class, while others can be accessed through the underlyinghttpxclient.Example usage:
from destiny_sdk.client import OAuthClient # Either use env for defaults or provide explicit auth and base_url client = OAuthClient(env="production") client = OAuthClient( base_url="https://destiny-repository.example.com", auth=OAuthMiddleware(...), ) # Supported method response = client.search(query="example") # Unsupported method, use underlying httpx client response = client.get_client().get("/system/healthcheck/")
Using directly#
When making API requests, include the token in the Authorization header following Bearer, eg:
curl https://api.evidence-repository.org/v1/references/search/?q=example \
-H "Authorization: Bearer $ACCESS_TOKEN"
The tokens will expire after a certain period (usually two hours). After expiration, you will need to obtain a new token using the same method as before.
Troubleshooting#
A 401 means the token was rejected - most often expired, or obtained for a different environment than the API you called. A 403 means you lack the permission that resource requires.
Please reach out if you experience any issues either obtaining or using tokens - most likely, we need to update some permissions.