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: README.md
+6-2Lines changed: 6 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -196,9 +196,13 @@ Sign users up or in with [WebAuthn](https://www.w3.org/TR/webauthn-2/) passkeys
196
196
197
197
Let a logged-in user manage their own enrolled authentication methods — enroll a new passkey (or other factor), list, rename, and delete — via the [My Account API](https://auth0.com/docs/manage-users/my-account-api). For obtaining a scoped token, the enroll/verify ceremony, listing, updating, deleting, and error handling, see [examples/MyAccountAuthenticationMethods.md](examples/MyAccountAuthenticationMethods.md).
Bind 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 supported for Passkey sign-in (`signin_with_passkey`) and the authentication-methods/factors methods on `MyAccountClient`. For key generation and usage, see [examples/Passkeys.md](examples/Passkeys.md#3-dpop-bound-passkey-tokens-optional) and [examples/MyAccountAuthenticationMethods.md](examples/MyAccountAuthenticationMethods.md#dpop).
201
+
Bind 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 supported for Passkey sign-in (`signin_with_passkey`), the authentication-methods/factors methods on `MyAccountClient`, and passwordless OTP verification (`passwordless.verify`). For key generation and usage, see [examples/Passkeys.md](examples/Passkeys.md#3-dpop-bound-passkey-tokens-optional), [examples/MyAccountAuthenticationMethods.md](examples/MyAccountAuthenticationMethods.md#dpop), and [examples/Passwordless.md](examples/Passwordless.md#7-dpop--sender-constrained-tokens-optional).
202
+
203
+
### 10. Passwordless Authentication
204
+
205
+
Sign users in with a one-time code sent by email or SMS, or with a magic link sent by email, via [Auth0 embedded passwordless login](https://auth0.com/docs/authenticate/passwordless/implement-login/embedded-login/relevant-api-endpoints). OTP verification and the magic-link callback each establish a server-side session like every other login path. For prerequisites, both flows, custom scopes/audiences, organizations, step-up MFA, DPoP, and error handling, see [examples/Passwordless.md](examples/Passwordless.md).
This is required for **any** server-side SDK completing magic link (this
61
+
one, Express, Next.js, etc.) — the browser that opens the emailed link is
62
+
not guaranteed to be the same browser/session that started the flow.
63
+
Without it, the user sees: *"The link must be opened on the same device
64
+
and browser from which you submitted your email address."* This flag is
65
+
not documented in the public Auth0 API reference, so if you don't set it
66
+
here you will not discover it from a 400 error message.
67
+
35
68
```python
36
69
from auth0_server_python.auth_server.server_client import ServerClient
37
70
@@ -152,6 +185,8 @@ user = result["state_data"]["user"]
152
185
153
186
> [!WARNING]
154
187
> Do not let callers override `redirect_uri`, `state`, `response_type`, `nonce`, or PKCE fields in magic-link `auth_params`. The SDK owns these values so the emailed authorization code and state cannot be redirected to an attacker-controlled URL.
188
+
>
189
+
> This matters more than it looks: Auth0 treats magic-link `state` as a pure echo and does not validate it server-side, and the clicked link's query string can overwrite whatever the browser originally stored. The SDK's single-use, `state`-keyed transaction plus the exact-match `redirect_uri` are therefore the *entire* CSRF / authorization-code-interception defense on this flow — Auth0 will not catch a bypass for you.
155
190
156
191
## 4. Custom scopes and audiences
157
192
@@ -218,9 +253,9 @@ result = await server_client.passwordless.verify(
218
253
> [!WARNING]
219
254
> Only forward a trusted, normalized end-user IP from your edge/proxy layer. Do not blindly copy arbitrary client-supplied headers into `client_ip`.
220
255
221
-
## 6. Organizations
256
+
## 6. Organizations (magic link only)
222
257
223
-
Magic links can carry an organization through `authParams`; the SDK stores the expected organization in the transaction and validates the callback token claims.
258
+
Magic links can carry an organization through `authParams`; the SDK stores the expected organization in the transaction and validates the claims returned by the callback.
For OTP verification, pass `organization` only when your Auth0 passwordless OTP configuration returns organization claims for that flow. The SDK validates the ID token's `org_id` or `org_name` claim against the supplied value.
271
+
If the callback's ID token does not include a matching organization claim, verification fails before a session is persisted, raising `OrganizationTokenValidationError`.
272
+
273
+
> [!NOTE]
274
+
> `VerifyPasswordlessOtpOptions` (the OTP `verify()` path) has no `organization`
275
+
> field. Auth0 does not attach an organization claim to tokens issued by the
276
+
> passwordless-OTP grant, so there is nothing for the SDK to validate against
277
+
> — an OTP flow that needs organization-scoped login should use magic link
278
+
> instead.
279
+
280
+
## 7. DPoP — sender-constrained tokens (optional)
281
+
282
+
`verify()` accepts an optional `dpop_key` — an EC P-256 JWK — to bind the
283
+
issued access token to that key ([RFC 9449](https://www.rfc-editor.org/rfc/rfc9449)),
284
+
so a stolen token alone cannot be replayed. The passwordless-OTP grant only
285
+
*requires* DPoP for public clients; this SDK is a confidential RWA client, so
286
+
DPoP here is opt-in, not required by default. Use it if a downstream resource
287
+
server independently mandates sender-constrained tokens.
If the ID token does not include a matching organization claim, verification fails before a session is persisted.
305
+
Keep the same `dpop_key` around for any later step-up (`server_client.mfa.verify(..., dpop_key=dpop_key)`) to preserve the sender constraint through MFA. If a resource server requires DPoP for *all* clients (not just public ones), Auth0 rejects the request with `invalid_request` / *"Resource server requires the use of DPoP to issue sender-constrained access tokens"* — that case is not specific to this SDK and surfaces as a `PasswordlessVerifyError` with that description.
251
306
252
307
## Completing MFA during passwordless login
253
308
@@ -293,11 +348,11 @@ except MfaRequiredError as e:
-`PasswordlessVerifyError` - OTP token exchange or ID-token verification failed
351
+
-`PasswordlessVerifyError` - OTP token exchange or ID-token verification failed, including an issuer or audience mismatch (`invalid_issuer` / `invalid_audience`)
297
352
-`MfaRequiredError` - Auth0 requires MFA before completing login
298
353
-`MissingRequiredArgumentError` - required SDK input is missing, such as magic-link `store_options`
299
354
-`InvalidArgumentError` - caller input is rejected before a network call
300
-
-`OrganizationTokenValidationError` - requested organization does not match returned token claims
355
+
-`OrganizationTokenValidationError` - magic-link callback only: requested organization does not match the returned token claims
301
356
302
357
### Basic handling
303
358
@@ -351,8 +406,9 @@ except Auth0Error as e:
351
406
-`bad.connection` - the passwordless connection is disabled or invalid
352
407
-`bad.email` - the email address is invalid or rejected by Auth0
353
408
-`sms_provider_error` - Auth0 could not send the SMS
354
-
-`too_many_requests` - rate limiting or attack protection blocked the request
409
+
-`too_many_requests` - rate limiting or attack protection blocked the request (`start()` or `verify()`)
355
410
-`invalid_grant` - the OTP is invalid, expired, or already used
411
+
-`invalid_issuer` - returned ID token issuer does not match your configured Auth0 domain
356
412
-`invalid_audience` - returned ID token audience does not match the SDK client
357
413
-`discovery_error` - the SDK could not load authorization server metadata
0 commit comments