diff --git a/docs/hydra/guides/updating-claims-at-refresh.mdx b/docs/hydra/guides/updating-claims-at-refresh.mdx index 9219b353a..55ee8424c 100644 --- a/docs/hydra/guides/updating-claims-at-refresh.mdx +++ b/docs/hydra/guides/updating-claims-at-refresh.mdx @@ -1,6 +1,6 @@ --- id: claims-at-refresh -title: Hook into the OAuth2 refresh token flow +title: Adding custom claims to tokens sidebar_label: OAuth2 webhooks --- @@ -12,79 +12,70 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem' ``` -Ory OAuth2 and OpenID Connect comes with a mechanism that allows updating `id_token` and `access_token` when a registered client -sends a token refresh request. The flow is realized by calling the defined refresh token hook endpoint which returns updated data. +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. :::note -The hook is called before any other logic is executed. If the hook execution fails, the entire token refresh flow fails and the -`refresh_token` remains unused. +The hook is called before any other logic is executed. If the hook execution fails, the entire token flow fails. ::: ## Configuration -Use the Ory CLI with key `/oauth2/refresh_token_hook` to enable this feature: +You can use hooks feature with all grant types. -```shell title="Enable the refresh token hook" +Use the Ory CLI with following keys to enable this feature: + +```shell title="Enable the generic token hook" ory patch oauth2-config {project.id} \ - --add "/oauth2/refresh_token_hook=\"https://my-example.app/token-refresh-hook\"" \ + --add "/oauth2/token_hook=\"https://my-example.app/token-hook\"" \ --format yaml ``` ### Webhook payload -The refresh token hook endpoint must accept the following payload format: +The token hook endpoint must accept the following payload format: ```json { - "subject": "foo", - "client_id": "bar", "session": { "id_token": { "id_token_claims": { - "jti": "jti", + "jti": "", "iss": "http://localhost:4444/", - "sub": "foo", - "aud": ["bar"], - "iat": 1234567, - "exp": 1234567, - "rat": 1234567, - "auth_time": 1234567, + "sub": "subject", + "aud": ["app-client"], "nonce": "", "at_hash": "", "acr": "1", - "amr": [], + "amr": null, "c_hash": "", "ext": {} }, "headers": { - "extra": { - "kid": "key-id" - } + "extra": {} }, - "username": "username", - "subject": "foo", - "expires_at": 1234567 + "username": "", + "subject": "foo" }, "extra": {}, - "client_id": "bar", + "client_id": "app-client", "consent_challenge": "", "exclude_not_before_claim": false, - "allowed_top_level_claims": [], - "kid": "key-id" + "allowed_top_level_claims": [] }, - "requester": { - "client_id": "bar", - "granted_scopes": ["openid", "offline"], + "request": { + "client_id": "app-client", + "granted_scopes": ["offline", "openid", "hydra.*"], "granted_audience": [], - "grant_types": ["refresh_token"] - }, - "granted_scopes": ["openid", "offline"], - "granted_audience": [] + "grant_types": ["authorization_code"], + "payload": {} + } } ``` @@ -92,10 +83,30 @@ The refresh token hook endpoint must accept the following payload format: `session` represents the consent session, along with the data that was passed to the [Accept Consent Request](https://www.ory.sh/docs/hydra/reference/api#operation/acceptConsentRequest) in the `id_token` field. -`requester` is the token request context. +`request` is the token request context. + +::: + +### Requester payload + +For `jwt-bearer` grant type the `assertion` value is also sent to the webhook URL. + +Here's the format of the `request.payload: + +```json +{ + "assertion": ["eyJhbGciOiJIUzI..."] +} +``` + +:::note + +For `authorization_code` and `refresh_token` grant types, the `request.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: ```json @@ -121,7 +132,7 @@ The token subject is never overridden. ### Updated tokens -The following examples show fragments of tokens issued after refreshing: +The following examples show fragments of tokens issued after the webhook call: ```mdx-code-block @@ -158,7 +169,87 @@ The following examples show fragments of tokens issued after refreshing: ``` -## Rejecting token refresh +## 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 refresh flow. +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. + +## Legacy webhook implementation + +There is an old version of the webhook feature built specifically for the `refresh_token` grant type. We recommend using the +generic webhook feature because the old one will soon be deprecated. + +Use the Ory CLI with following keys to enable this feature: + +```shell title="Enable the refresh token hook" +ory patch oauth2-config {project.id} \ + --add "/oauth2/refresh_token_hook=\"https://my-example.app/token-refresh-hook\"" \ + --format yaml +``` + +### Webhook payload + +The legacy webhook feature works the same way as the new one, but has a different payload that is sent to the webhook URL. + +The `refresh_token` hook endpoint must accept the following payload format: + +```json +{ + "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"] + }, + "granted_scopes": ["openid", "offline"], + "granted_audience": [] +} +``` + +:::note + +If you enable both legacy and the new webhook features, both will be executed for the `refresh_token` grant type. The results of +both webhooks will be applied onto the session. In case of conflict, result of the new webhook will take priority. + +:::