Skip to content

Commit dd7f4c3

Browse files
alnrvinckr
andauthored
feat: rework OAuth2 webhook docs (#1712)
* feat: rework OAuth2 webhook docs * qUpdate docs/hydra/guides/oauth2-webhooks.mdx Co-authored-by: Vincent <[email protected]> * Update docs/hydra/guides/oauth2-webhooks.mdx Co-authored-by: Vincent <[email protected]> * Update docs/oauth2-oidc/claims-scope.mdx Co-authored-by: Vincent <[email protected]> * Update docs/oauth2-oidc/claims-scope.mdx Co-authored-by: Vincent <[email protected]> * Update docs/oauth2-oidc/claims-scope.mdx Co-authored-by: Vincent <[email protected]> * chore: format --------- Co-authored-by: Vincent <[email protected]>
1 parent 7963f41 commit dd7f4c3

File tree

4 files changed

+302
-276
lines changed

4 files changed

+302
-276
lines changed

docs/hydra/guides/oauth2-webhooks.mdx

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
---
2+
id: claims-at-refresh
3+
title: Customize claims with OAuth2 webhooks
4+
sidebar_label: OAuth2 webhooks
5+
keywords:
6+
- webhooks
7+
- custom
8+
- customize
9+
- claims
10+
- jwt
11+
- access
12+
- token
13+
---
14+
15+
# Customizing claims with OAuth2 webhooks
16+
17+
```mdx-code-block
18+
import CodeBlock from '@theme/CodeBlock';
19+
import Tabs from '@theme/Tabs';
20+
import TabItem from '@theme/TabItem'
21+
```
22+
23+
You can modify aspects of the OpenID Connect and access tokens returned from Hydra's OAuth2 token endpoint. A typical use case is
24+
adding custom claims to the tokens issued by Ory OAuth2/Ory Hydra.
25+
26+
Customize the token response by registering a webhook endpoint in your OAuth2 configuration. Before the token is issued to the
27+
client, Ory will call your HTTPS endpoint with information about the OAuth client requesting the token.
28+
29+
When performing an Authorization Code flow information about the resource owner's session is also included in the webhook payload.
30+
31+
Your endpoint's response to the webhook will be used to customize the token that Ory issues to the OAuth client, and optionally
32+
overwrite the session data stored for the resource owner.
33+
34+
Using webhooks is supported for all grant types (flows).
35+
36+
:::note
37+
38+
The webhook is called before any other logic is executed. If the webhook execution fails -- for example if your endpoint is
39+
unreachable or responds with an HTTP error code -- the token exchange will fail for the OAuth client.
40+
41+
:::
42+
43+
## Configuration
44+
45+
Use the Ory CLI to register your webhook endpoint:
46+
47+
```mdx-code-block
48+
<Tabs>
49+
<TabItem value="header-auth" label="with authentication in header" default >
50+
<CodeBlock language="shell">{
51+
`ory patch oauth2-config $project_id \\
52+
--add '/oauth2/token_hook/url="https://my-example.app/token-hook"' \\
53+
--add '/oauth2/token_hook/auth/type="api_key"' \\
54+
--add '/oauth2/token_hook/auth/config/in="header"' \\
55+
--add '/oauth2/token_hook/auth/config/name="X-API-Key"' \\
56+
--add '/oauth2/token_hook/auth/config/value="MY API KEY"' \\
57+
--format yaml`}
58+
</CodeBlock>
59+
</TabItem>
60+
<TabItem value="cookie-auth" label="with authentication in cookie">
61+
<CodeBlock language="shell">{
62+
`ory patch oauth2-config $project_id \\
63+
--add '/oauth2/token_hook/url="https://my-example.app/token-hook"' \\
64+
--add '/oauth2/token_hook/auth/type="api_key"' \\
65+
--add '/oauth2/token_hook/auth/config/in="cookie"' \\
66+
--add '/oauth2/token_hook/auth/config/name="X-Cookie-Name"' \\
67+
--add '/oauth2/token_hook/auth/config/value="MY SECRET COOKIE"' \\
68+
--format yaml`}
69+
</CodeBlock>
70+
</TabItem>
71+
<TabItem value="no-auth" label="no authentication">
72+
<CodeBlock language="shell">{
73+
`ory patch oauth2-config $project_id \\
74+
--add '/oauth2/token_hook="https://my-example.app/token-hook"' \\
75+
--format yaml`}
76+
</CodeBlock>
77+
</TabItem>
78+
</Tabs>
79+
```
80+
81+
## Webhook payload
82+
83+
Ory will perform a POST request with a JSON payload towards your endpoint.
84+
85+
```json title="Example OAuth2 token webhook request payload"
86+
{
87+
"session": {
88+
"id_token": {
89+
"id_token_claims": {
90+
"jti": "",
91+
"iss": "http://your-slug-xyz.projects.oryapis.com",
92+
"sub": "subject",
93+
"aud": ["app-client"],
94+
"nonce": "",
95+
"at_hash": "",
96+
"acr": "1",
97+
"amr": null,
98+
"c_hash": "",
99+
"ext": {}
100+
},
101+
"headers": {
102+
"extra": {}
103+
},
104+
"username": "",
105+
"subject": "foo"
106+
},
107+
"extra": {},
108+
"client_id": "app-client",
109+
"consent_challenge": "",
110+
"exclude_not_before_claim": false,
111+
"allowed_top_level_claims": []
112+
},
113+
"request": {
114+
"client_id": "app-client",
115+
"granted_scopes": ["offline", "openid", "hydra.*"],
116+
"granted_audience": [],
117+
"grant_types": ["authorization_code"],
118+
"payload": {
119+
"assertion": ["eyJhbGciOiJIUzI..."]
120+
}
121+
}
122+
}
123+
```
124+
125+
`session` represents the OAuth2 session, along with the data that was passed to the
126+
[Accept Consent Request](https://www.ory.sh/docs/hydra/reference/api#operation/acceptConsentRequest) in the `id_token` field (only
127+
applicable to Authorization code flows).
128+
129+
`request` contains information from the OAuth client's request to the token endpoint.
130+
131+
The `request.payload.assertion` field will be populated for flows of the
132+
[`urn:ietf:params:oauth:grant-type:jwt-bearer`](jwt.mdx#using-jwts-as-authorization-grants) grant type only, and contains the JWT
133+
the client passed as the `assertion` in their call to the token endpoint.
134+
135+
## Responding to the webhook
136+
137+
When handling the webhook in your endpoint, use the request payload to decide how Ory should proceed in the token exchange with
138+
the client.
139+
140+
To accept the token exchange without modification, return a `204` or `200` HTTP status code without a response body.
141+
142+
To deny the token exchange, reply with a `403` HTTP status code.
143+
144+
To modify the claims of the issued tokens and instruct Hydra to proceed with the token exchange, return `200` with a JSON response
145+
body:
146+
147+
```json
148+
{
149+
"session": {
150+
"access_token": {
151+
"your:custom:access-token-claim": "any value you like",
152+
"your:second:access-token-claim": 124390123
153+
},
154+
"id_token": {
155+
"your:custom:id-token-claim": "another value",
156+
"your:second:id-token-claim": 2394123
157+
}
158+
}
159+
}
160+
```
161+
162+
Responding with any other HTTP status code will abort the token exchange toward the OAuth2 client with an error message.
163+
164+
## Updated tokens
165+
166+
Tokens issued by Ory to the OAuth2 client will contain the data from your webhook response:
167+
168+
```mdx-code-block
169+
<Tabs>
170+
<TabItem value="id_token" label="id_token" default>
171+
<CodeBlock language="json">{`
172+
{
173+
"aud": [
174+
"my_client"
175+
],
176+
"auth_time": 1647427485,
177+
"your:custom:id-token-claim": "another value",
178+
"your:second:id-token-claim": 2394123,
179+
"iss": "http://ory.hydra.example/",
180+
181+
}
182+
`}</CodeBlock>
183+
</TabItem>
184+
<TabItem value="access_token" label="access_token">
185+
<CodeBlock language="json">{`
186+
{
187+
"active": true,
188+
"scope": "openid offline",
189+
"client_id": "my_client",
190+
191+
"aud": [],
192+
"iss": "http://ory.hydra.example/",
193+
"token_type": "Bearer",
194+
"token_use": "access_token",
195+
"ext": {
196+
"your:custom:access-token-claim": "any value you like",
197+
"your:second:access-token-claim": 124390123,
198+
}
199+
}
200+
`}</CodeBlock>
201+
</TabItem>
202+
</Tabs>
203+
```
204+
205+
:::note
206+
207+
You cannot override the token subject.
208+
209+
:::
210+
211+
### Refresh token
212+
213+
If a webhook for `refresh_token` grant type fails with a non-graceful result, the refresh flow will fail and the supplied
214+
`refresh_token` will remain unused.
215+
216+
## Legacy webhook implementation (deprecated)
217+
218+
:::warning
219+
220+
This mechanism is deprecated and no longer supported
221+
222+
:::
223+
224+
There is an old version of the webhook feature built specifically for the `refresh_token` grant type. We recommend using the
225+
generic webhook feature because the old one will soon be deprecated.
226+
227+
Use the Ory CLI with following keys to enable this feature:
228+
229+
```shell title="Enable the refresh token hook"
230+
ory patch oauth2-config {project.id} \
231+
--add '/oauth2/refresh_token_hook/url="https://my-example.app/token-refresh-hook"' \
232+
--format yaml
233+
```
234+
235+
### Legacy webhook payload
236+
237+
The legacy webhook feature works the same way as the new one, but has a different payload that is sent to the webhook URL.
238+
239+
The `refresh_token` hook endpoint must accept the following payload format:
240+
241+
```json
242+
{
243+
"subject": "foo",
244+
"client_id": "bar",
245+
"session": {
246+
"id_token": {
247+
"id_token_claims": {
248+
"jti": "jti",
249+
"iss": "http://localhost:4444/",
250+
"sub": "foo",
251+
"aud": ["bar"],
252+
"iat": 1234567,
253+
"exp": 1234567,
254+
"rat": 1234567,
255+
"auth_time": 1234567,
256+
"nonce": "",
257+
"at_hash": "",
258+
"acr": "1",
259+
"amr": [],
260+
"c_hash": "",
261+
"ext": {}
262+
},
263+
"headers": {
264+
"extra": {
265+
"kid": "key-id"
266+
}
267+
},
268+
"username": "username",
269+
"subject": "foo",
270+
"expires_at": 1234567
271+
},
272+
"extra": {},
273+
"client_id": "bar",
274+
"consent_challenge": "",
275+
"exclude_not_before_claim": false,
276+
"allowed_top_level_claims": [],
277+
"kid": "key-id"
278+
},
279+
"requester": {
280+
"client_id": "bar",
281+
"granted_scopes": ["openid", "offline"],
282+
"granted_audience": [],
283+
"grant_types": ["refresh_token"]
284+
},
285+
"granted_scopes": ["openid", "offline"],
286+
"granted_audience": []
287+
}
288+
```
289+
290+
:::note
291+
292+
If you enable both legacy and the new webhook features, both will be executed for the `refresh_token` grant type. The results of
293+
both webhooks will be applied onto the session. In case of conflict, result of the new webhook will take priority.
294+
295+
:::

0 commit comments

Comments
 (0)