Adding custom claims to the tokens
The Hydra OAuth2 and OpenID Connect server comes with a mechanism that allows updating id_token
and access_token
when a
registered client sends a token request. The flow is realized by calling the defined token hook endpoint which
returns updated data.
If the data provided by the webhook is different from the data the client sends, the webhook overwrites the session data with a new set.
The hook is called before any other logic is executed. If the hook execution fails, the entire token flow fails.
Configuration
Grant types
The hooks feature is enabled for the following grant types:
authorization_code
client_credentials
refresh_token
urn:ietf:params:oauth:client-assertion-type:jwt-bearer
- see RFC7523
To enable the token webhooks, configure the following keys in the Hydra configuration:
oauth2:
# authorization_code grant type
authorization_code_hook: https://my-example.app/authorization-code-hook
# client_credentials grant type
client_credentials_hook: https://my-example.app/client-credentials-hook
# refresh_token grant type
refresh_token_hook: https://my-example.app/token-refresh-hook
# urn:ietf:params:oauth:client-assertion-type:jwt-bearer grant type
jwt_bearer_hook: https://my-example.app/jwt-bearer-hook
If you're running Hydra locally, you can set these values by exporting the token hook endpoint URLs as environment variables. Run this command:
- macOS
- Linux
- Windows
export OAUTH2_AUTHORIZATION_CODE_HOOK=value
export OAUTH2_CLIENT_CREDENTIALS_HOOK=value
export OAUTH2_REFRESH_TOKEN_HOOK=value
export OAUTH2_JWT_BEARER_HOOK=value
export OAUTH2_AUTHORIZATION_CODE_HOOK=value
export OAUTH2_CLIENT_CREDENTIALS_HOOK=value
export OAUTH2_REFRESH_TOKEN_HOOK=value
export OAUTH2_JWT_BEARER_HOOK=value
set OAUTH2_AUTHORIZATION_CODE_HOOK=value
set OAUTH2_CLIENT_CREDENTIALS_HOOK=value
set OAUTH2_REFRESH_TOKEN_HOOK=value
set OAUTH2_JWT_BEARER_HOOK=value
Webhook configuration
The token hook endpoint must accept the following payload format:
{
"subject": "foo",
"client_id": "bar",
"session": {
"id_token": {
"id_token_claims": {
"jti": "jti",
"iss": "http://localhost:4444/",
"sub": "foo",
"aud": ["bar"],
"iat": 1234567,
"exp": 1234567,
"rat": 1234567,
"auth_time": 1234567,
"nonce": "",
"at_hash": "",
"acr": "1",
"amr": [],
"c_hash": "",
"ext": {}
},
"headers": {
"extra": {
"kid": "key-id"
}
},
"username": "username",
"subject": "foo",
"expires_at": 1234567
},
"extra": {},
"client_id": "bar",
"consent_challenge": "",
"exclude_not_before_claim": false,
"allowed_top_level_claims": [],
"kid": "key-id"
},
"requester": {
"client_id": "bar",
"granted_scopes": ["openid", "offline"],
"granted_audience": [],
"grant_types": ["refresh_token"],
"payload": {}
},
"granted_scopes": ["openid", "offline"],
"granted_audience": []
}
session
represents the consent session, along with the data that was passed to the
Accept Consent Request in the id_token
field.
requester
is the token request context.
Requester payload
For client_credentials
and urn:ietf:params:oauth:client-assertion-type:jwt-bearer
grant types, Hydra will also send an entire payload that was sent to the /token
endpoint by the client.
Here's the format of the requester.payload
field for each grant type:
- client_credentials
- urn:ietf:params:oauth:client-assertion-type:jwt-bearer
{
"grant_type": [
"client_credentials"
],
"audience": ["my-api"],
"scope": ["user:profile:read"]
}
{
"grant_type": [
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
],
"assertion": ["eyJhbGciOiJIUzI..."],
"scope": ["user:profile:read"]
}
For authorization_code
and refresh_token
grant types, the requester.payload
is always empty.
Webhook responses
To update the data, the webhook must return a 200 OK
response and the updated session data in the following format:
{
"session": {
"access_token": {
"foo": "bar"
},
"id_token": {
"bar": "baz"
}
}
}
Alternatively, you can choose not to update the session data by returning a 204 No Content
response.
The token subject is never overridden.
Updated tokens
The following examples show fragments of tokens issued after the webhook call:
- id_token
- access_token
{
"aud": [
"my_client"
],
"auth_time": 1647427485,
"bar": "baz",
"iss": "http://ory.hydra.example/",
"sub": "foo@bar.com"
}
{
"active": true,
"scope": "openid offline",
"client_id": "my_client",
"sub": "foo@bar.com",
"aud": [],
"iss": "http://ory.hydra.example/",
"token_type": "Bearer",
"token_use": "access_token",
"ext": {
"foo": "bar"
}
}
Rejecting token claims update
To gracefully reject token contents update, the hook must return a 403 Forbidden
response. Any other response results in a
failure of the token update and, as a result, failure of the entire token flow.
Refresh token
If a webhook for refresh_token
grant type fails with a non-graceful result, the refresh flow will also fail and the supplied refresh_token
will remain unused.