Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.15.2.
Configure OAuth 2.0 client credentials when a reviewed HTTP source requires a bearer token before Relay can perform an authorized source request.
When to use this
Section titled “When to use this”Use this authentication type only for a source whose token endpoint, request encoding, response shape, scopes, and token lifetime have source-owner approval.
OAuth changes source authentication, not source or claim ownership. Relay owns the token exchange and source authorization header. Rhai scripts and callers receive no client secret, token, or token response.
Before you start
Section titled “Before you start”You need:
- A project created with the
httptemplate - The reviewed token endpoint and source endpoint
- The required token request encoding
- The exact accepted token response shape
- Operator-owned client id and client secret values
- Synthetic token success and failure observations
Use project configuration for the complete generated field contract. This page covers the decisions and verification path.
Select the token request encoding
Section titled “Select the token request encoding”In the integration’s source.auth, select JSON or form encoding.
Use JSON when the token endpoint requires an application/json body:
source: auth: type: oauth2_client_credentials request: json response_profile: oauth2_bearerUse form when the endpoint requires an
application/x-www-form-urlencoded body:
source: auth: type: oauth2_client_credentials request: form response_profile: oauth2_bearerBoth encodings send grant_type=client_credentials plus the client id and client secret.
The request body remains Relay-owned and unavailable to Rhai.
Select the response profile
Section titled “Select the response profile”Choose one closed response profile.
oauth2_bearer accepts exactly:
{ "access_token": "SYNTHETIC_FIXTURE_TOKEN", "token_type": "Bearer", "expires_in": 300}access_token must be non-empty and bounded.
token_type must be exactly Bearer.
expires_in must be an integer in the supported range.
Relay limits cache lifetime by the token expiry and the optional authored refresh_skew.
oauth2_bearer_no_expiry accepts exactly:
source: auth: type: oauth2_client_credentials request: json response_profile: oauth2_bearer_no_expiryIts token response contains only:
{ "access_token": "SYNTHETIC_FIXTURE_TOKEN", "token_type": "Bearer"}The no-expiry profile rejects expires_in and refresh_skew.
Token caching is disabled.
Relay performs a fresh bounded exchange for every authorized execution.
Both profiles reject unknown or duplicate members, refresh tokens, identity tokens, redirects, non-success status, unexpected content type, and oversized responses.
Declare scope and audience
Section titled “Declare scope and audience”Add only values required by the reviewed authorization server:
source: auth: type: oauth2_client_credentials request: form response_profile: oauth2_bearer scope: records.read records.verify audience: https://registry.invalid refresh_skew: 20sscope is one bounded space-separated string.
audience is one bounded token request value.
Neither value can come from caller input or a source response.
Registryctl 1.0 project authoring does not expose a separate OAuth resource field.
If an authorization server requires the OAuth resource parameter rather than audience,
record that as an unsupported contract instead of relabeling the value or adding an arbitrary
request member.
Bind private destinations and secrets
Section titled “Bind private destinations and secrets”In environments/<environment>.yaml, bind the source and token destinations:
integrations: person-record: source: origin: https://registry.invalid credential: client_id: { secret: REGISTRY_CLIENT_ID } client_secret: { secret: REGISTRY_CLIENT_SECRET } generation: 1 oauth: origin: https://identity.invalid path: /oauth/token generation: 1The integration id must match the project. The environment contains secret references, not values. The private source origin and token endpoint may differ, but both are fixed private bindings owned by Relay. Redirects and destination fallback fail closed.
The operator supplies the referenced client values only to consultation Relay. Keep them out of project YAML, fixtures, scripts, generated portable artifacts, logs, and command arguments.
When rotating either credential value, update the operator secret and increment
credential.generation.
When changing the token destination binding, increment oauth.generation.
Then rerun test, check, review compare, and build; review and sign every lane Registryctl
reports as affected.
Author strict token fixtures
Section titled “Author strict token fixtures”For each source fixture, place the token interaction before the source interaction:
interactions: - expect: method: POST path: /oauth/token body: grant_type: client_credentials scope: records.read records.verify audience: https://registry.invalid respond: status: 200 headers: { Content-Type: application/json } body: access_token: SYNTHETIC_FIXTURE_TOKEN token_type: Bearer expires_in: 300Match the expected body to the selected JSON or form encoding. Keep the token synthetic.
Add fixtures that reject:
- Non-success token status
- Redirect
- Missing or unexpected media type
- Missing, duplicate, or unknown response members
- Empty or oversized access token
- Token type other than exact
Bearer - Missing, non-integer, or invalid
expires_inforoauth2_bearer - Any
expires_inforoauth2_bearer_no_expiry - Oversized response body
- Authorization denial before the token request
Do not copy an upstream error body into retained fixture results or logs.
Verify the boundary
Section titled “Verify the boundary”Run the complete offline fixture set:
registryctl testThen inspect the redacted plan:
registryctl check --explainConfirm the request encoding, response profile, scope, audience, token destination, secret consumers, generations, source destination, and bounds. The plan must keep credentials and token acquisition in consultation Relay.
Use the generic local environment only after the fixtures pass:
registryctl dev --detachregistryctl dev smokeregistryctl dev downThe denial scenario must report zero token and source calls. The authorized scenario reports one token call for the no-expiry profile. For the expiry profile, its first authorized execution reports one token exchange; Relay owns subsequent expiry-bound cache behavior.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
| The token fixture rejects a success response | The body differs from the selected closed profile. | Correct the fixture or select the profile that matches the reviewed endpoint. |
refresh_skew is rejected | The no-expiry profile cannot cache or refresh. | Remove the field or use oauth2_bearer with required integer expiry. |
The server requires resource | Project authoring does not expose that token parameter. | Keep the integration unsupported until a reviewed contract adds it. |
| A redirect fails | Token destination redirects are outside the fixed authority. | Bind the final reviewed HTTPS origin and path directly. |
| A token or credential appears outside Relay | The secret boundary failed. | Stop testing, rotate the credential, and report the failure through the repository security process. |