You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: examples/DPoP.md
-7Lines changed: 0 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,7 +16,6 @@ This SDK supports DPoP for **passkey sign-in** (`ServerClient.signin_with_passke
16
16
-[4. Generating a proof manually](#4-generating-a-proof-manually)
17
17
-[Key lifecycle and security](#key-lifecycle-and-security)
18
18
-[Error Handling](#error-handling)
19
-
-[Additional Resources](#additional-resources)
20
19
21
20
## `dpop_key` vs `dpop_proof`
22
21
@@ -125,9 +124,3 @@ except PasskeyError as e:
125
124
```
126
125
127
126
On the My Account surface, a key mismatch or a DPoP-required endpoint reached without binding surfaces as `MyAccountApiError` (typically `status=401`). Catch `Auth0Error` for uniform handling.
@@ -141,50 +138,14 @@ result = await server_client.signin_with_passkey(
141
138
> [!NOTE]
142
139
> The SDK is transparent to the signup-vs-login difference in the credential `response` — both flow through the same `PasskeyAuthResponse.response` dict. Send exactly the keys the browser produced.
143
140
144
-
## 3. Organizations
141
+
## 3. DPoP-bound passkey tokens (optional)
145
142
146
-
Pass an `organization` (ID or name) on the challenge to scope the passkey ceremony to an organization. The resulting `id_token` carries the `org_id` claim, validated automatically at session creation.
If tenant policy requires a second factor, `signin_with_passkey` raises `MfaRequiredError` — the login does **not** complete silently. The raw MFA token is encrypted by the SDK before it reaches you, and stored server-side so your challenge/verify routes can retrieve it without a client round-trip.
159
-
160
-
```python
161
-
from auth0_server_python.error import MfaRequiredError
# e.mfa_token is ENCRYPTED — hand it straight to MfaClient.
171
-
# See examples/MFA.md for the challenge/verify flow.
172
-
...
173
-
```
174
-
175
-
See [examples/MFA.md](MFA.md) for the full challenge → verify continuation.
176
-
177
-
> [!NOTE]
178
-
> A passkey is supported as a **first** factor today. WebAuthn as a **second** factor is currently only available through Universal Login (hosted), not as a headless API a server SDK can drive — so this SDK does not implement it. The response models are forward-tolerant for when that capability ships.
179
-
180
-
## 5. DPoP-bound passkey tokens
181
-
182
-
Pass a `dpop_key` to bind the issued tokens to a key you hold (RFC 9449). When supplied, the SDK attaches a DPoP proof to the token exchange and Auth0 issues a DPoP-bound token; if the server returns an unbound (`Bearer`) token instead, `signin_with_passkey` raises rather than accept the downgrade.
143
+
Pass an optional `dpop_key` to bind the issued tokens to a key your server holds ([RFC 9449](https://www.rfc-editor.org/rfc/rfc9449)), so a stolen token alone cannot be replayed. DPoP is **opt-in**: omit `dpop_key` and sign-in returns ordinary Bearer tokens with no behaviour change.
183
144
184
145
```python
185
146
from jwcrypto import jwk
186
147
187
-
dpop_key = jwk.JWK.generate(kty="EC", crv="P-256") # you create and keep this key
148
+
dpop_key = jwk.JWK.generate(kty="EC", crv="P-256") # you generate and keep this key (Tier 0)
188
149
189
150
result =await server_client.signin_with_passkey(
190
151
auth_session=challenge.auth_session,
@@ -194,7 +155,10 @@ result = await server_client.signin_with_passkey(
194
155
)
195
156
```
196
157
197
-
Reuse the **same**`dpop_key` for any My Account API calls made with the resulting token. See [examples/DPoP.md](DPoP.md) for the full picture.
158
+
When `dpop_key` is supplied, the SDK attaches a token-endpoint proof so Auth0 issues a DPoP-bound token, transparently handles the server-nonce challenge, and **rejects a Bearer downgrade** — if the server returns an unbound token, `signin_with_passkey` raises `PasskeyError` rather than silently accepting a token bound to a key it never used.
159
+
160
+
> [!TIP]
161
+
> Reuse the **same**`dpop_key` for any subsequent My Account API calls made with the resulting token — the token is bound to that one key. See [examples/DPoP.md](DPoP.md) for the `dpop_key` vs `dpop_proof` distinction, key lifecycle, and nonce handling.
198
162
199
163
## Error Handling
200
164
@@ -242,11 +206,3 @@ except Auth0Error as e:
242
206
243
207
> [!NOTE]
244
208
> `auth_session` is a short-lived (typically ~5 min) Tier 1 credential. It is redacted in the SDK's model `repr()`, and you should never log or persist it. If the ceremony takes too long, re-request the challenge.
245
-
246
-
## Additional Resources
247
-
248
-
-[Managing passkeys via My Account API](MyAccountAuthenticationMethods.md) — enroll/list/delete a logged-in user's passkeys
0 commit comments