The My Account API lets a logged-in user manage their own account. This guide covers the authentication-methods and factors surface: enrolling a new passkey (or other factor), and listing, reading, renaming, and deleting a user's enrolled methods.
Note
This is a different My Account resource from Connected Accounts (Token Vault). Connected-accounts management is exposed as convenience methods on ServerClient; authentication-method management is on MyAccountClient directly, because each call takes a user access token you obtain yourself. The two share the same My Account setup (activation, MRRT, scopes, MyAccountApiError) — see ConnectedAccounts.md → Pre-requisites for that common setup.
Note
To sign in with a passkey (rather than manage one), see examples/Passkeys.md. To bind these calls to a held key, pass an optional dpop_key — see DPoP below.
- Prerequisites
- Obtaining a scoped token
- 1. List factors available for enrollment
- 2. Enroll an authentication method (passkey)
- 3. List authentication methods
- 4. Get a single authentication method
- 5. Update (rename) an authentication method
- 6. Delete an authentication method
- DPoP
- Error Handling
- Activate the My Account API on your tenant and enable access for your application.
- Configure MRRT so your refresh-token policy can mint tokens for the My Account audience (
https://{yourDomain}/me/) with the authentication-methods scopes. - Passkey enrollment additionally requires a Custom Domain and the native passkey feature on your tenant.
See Auth0 Docs → Scope for the full scope reference. The scopes for this surface (note the hyphens):
| Operation | Scope |
|---|---|
| List factors | read:me:factors |
| List / get methods | read:me:authentication-methods |
| Enroll / verify | create:me:authentication-methods |
| Update | update:me:authentication-methods |
| Delete | delete:me:authentication-methods |
Tip
As with Connected Accounts, set the default scope for the My Account audience when constructing ServerClient to avoid a fresh token request per scope. See ConnectedAccounts.md → A note about scopes.
MyAccountClient is stateless — it takes a correctly-scoped user access token on every call. Obtain that token from your ServerClient session via MRRT, then construct the client. See Auth0 Docs → Get an access token for the underlying token requirements.
from auth0_server_python.auth_server.my_account_client import MyAccountClient
# Fresh My Account-scoped token for the current session (MRRT exchange)
access_token = await server_client.get_access_token(
store_options={"request": request, "response": response},
audience=f"https://{YOUR_CUSTOM_DOMAIN}/me/",
scope="create:me:authentication-methods read:me:authentication-methods read:me:factors",
)
my_account = MyAccountClient(domain=YOUR_CUSTOM_DOMAIN)factors = await my_account.get_factors(access_token=access_token)
for factor in factors.factors:
print(factor.type, factor.usage)Enrollment is a two-step ceremony, mirroring sign-in: request a challenge, sign it in the browser, then verify. See Auth0 Docs → Enrollment flow (and, for passkeys specifically, Embedded login with native passkeys) for the platform-level description of this ceremony.
from auth0_server_python.auth_types import EnrollAuthenticationMethodRequest
challenge = await my_account.enroll_authentication_method(
access_token=access_token,
request=EnrollAuthenticationMethodRequest(type="passkey"),
)
# challenge.authentication_method_id -> id of the new (unverified) method
# challenge.auth_session -> Tier 1 session credential (do not log)
# challenge.authn_params_public_key -> pass to navigator.credentials.create()EnrollAuthenticationMethodRequest.type is a closed set: passkey, email, phone, totp, push-notification, recovery-code, password. For non-passkey types, supply the relevant fields (email, phone_number, preferred_authentication_method). An invalid type fails at construction with a clear ValidationError.
Pass challenge.authn_params_public_key to navigator.credentials.create() and collect the resulting credential.
from auth0_server_python.auth_types import (
VerifyAuthenticationMethodRequest,
PasskeyAuthResponse,
)
method = await my_account.verify_authentication_method(
access_token=access_token,
authentication_method_id=challenge.authentication_method_id,
request=VerifyAuthenticationMethodRequest(
auth_session=challenge.auth_session,
authn_response=PasskeyAuthResponse(
id=credential["id"],
raw_id=credential["rawId"],
type="public-key",
response={
"clientDataJSON": credential["response"]["clientDataJSON"],
"attestationObject": credential["response"]["attestationObject"],
},
),
),
)
print(f"Enrolled: {method.id} ({method.type})")Note
For non-passkey types, set the matching field on VerifyAuthenticationMethodRequest instead of authn_response: otp_code (email/phone/totp), recovery_code, or password. A push enrollment needs only auth_session.
See Auth0 Docs → List authentication methods.
all_methods = await my_account.list_authentication_methods(access_token=access_token)
# Filter by type
passkeys = await my_account.list_authentication_methods(
access_token=access_token,
type_filter="passkey",
)
for m in passkeys.authentication_methods:
print(m.id, m.type, m.created_at)Note
AuthenticationMethod and Factor are forward-tolerant (extra="allow"): fields or method/factor types Auth0 adds later still deserialize. Don't switch exhaustively on type — handle unknown types gracefully.
method = await my_account.get_authentication_method(
access_token=access_token,
authentication_method_id="passkey|abc123",
)Note
Method IDs (e.g. passkey|abc123) can contain characters like |. The SDK URL-encodes every ID it places in a path, so pass the raw ID exactly as returned — do not pre-encode it.
from auth0_server_python.auth_types import UpdateAuthenticationMethodRequest
method = await my_account.update_authentication_method(
access_token=access_token,
authentication_method_id="totp|123",
request=UpdateAuthenticationMethodRequest(name="My Authenticator App"),
)See Auth0 Docs → Delete an authentication method.
await my_account.delete_authentication_method(
access_token=access_token,
authentication_method_id="passkey|abc123",
)
# Returns None on success (HTTP 204).Every method above accepts an optional dpop_key to present a sender-constrained token (Authorization: DPoP + a per-request proof) instead of a Bearer token (RFC 9449). Pass the same key the access token was bound to at sign-in:
from jwcrypto import jwk
dpop_key = jwk.JWK.generate(kty="EC", crv="P-256") # the key the token was bound to
methods = await my_account.list_authentication_methods(
access_token=access_token,
dpop_key=dpop_key,
)Warning
The dpop_key private key is a Tier 0 secret. Keep it in your secret store (KMS/HSM), never log it (repr() is redacted, but key.export_private() is not), use one key per user/session (never share across principals), and use EC P-256 only — any other key type fails closed with a ValueError.
All errors inherit from Auth0Error. My Account API errors are MyAccountApiError (RFC 7807 problem-details, carrying status, detail, and optional validation_errors); missing arguments raise MissingRequiredArgumentError; transport or non-JSON responses surface as ApiError.
from auth0_server_python.error import Auth0Error
try:
methods = await my_account.list_authentication_methods(access_token=access_token)
except Auth0Error as e:
return {"error": str(e)}from auth0_server_python.error import Auth0Error, MyAccountApiError
try:
await my_account.enroll_authentication_method(
access_token=access_token,
request=EnrollAuthenticationMethodRequest(type="passkey"),
)
except MyAccountApiError as e:
if e.status == 401:
return redirect_to_login() # token expired
if e.status == 403:
return {"error": "Missing required scope"} # e.g. create:me:authentication-methods
if e.status == 400 and e.validation_errors:
return {"error": "Validation failed", "details": e.validation_errors}
raise
except Auth0Error as e:
return {"error": str(e)}Note
Enrollment raises MyAccountApiError/ApiError, whereas passkey sign-in (ServerClient) raises PasskeyError. They are two distinct API surfaces — an auth grant versus a My Account resource — so write the except that matches the call you made.
Auth0Error(base): catch for general handlingMyAccountApiError: My Account API errors withstatus,detail, optionalvalidation_errorsMissingRequiredArgumentError: a required parameter (access_token,authentication_method_id,request) was not providedApiError: transport failure or a non-JSON error body