feat: Add MFA (Multi-Factor Authentication) API support#79
feat: Add MFA (Multi-Factor Authentication) API support#79kishore7snehil merged 23 commits intomainfrom
Conversation
|
@subhankarmaiti please also add a corresponding |
…tor_types' for enrollment and challenge
…lient and test files
…ation enrollment and challenge
Resolve conflicts to incorporate MCD support, issuer validation, OIDC/JWKS caching, and release 1.0.0b9 changes from main. MFA feature code is placed last in each section.
MfaClient now accepts a callable domain (Union[str, Callable, None]) matching ServerClient's pattern, and resolves the domain per-request via _resolve_base_url(). All API methods take store_options as a top-level parameter instead of burying it inside the options dict.
Chained MFA from verify() returns a raw mfa_token by design. Encryption is the framework SDK's responsibility, matching the separation of concerns between MfaClient and ServerClient.
| # MFA (Multi-Factor Authentication) | ||
| # ============================================================================ | ||
|
|
||
| @property |
There was a problem hiding this comment.
This .mfa property exposes the entire MfaClient instance directly to SDK users, which is a different pattern from how MyAccountClient is handled there, the sub-client stays private and ServerClient wraps each operation in its own method (e.g. list_connected_accounts, connect_account).
Was this intentional ? A few things to consider:
- Methods like
encrypt_mfa_token()anddecrypt_mfa_token()are now publicly callable viaserver_client.mfa.encrypt_mfa_token(...). Is this intentional ? - The full
MfaClientclass (method names, parameter shapes, return types) becomes a public contract. Any future rename or refactor is a breaking change. - Should we either wrap the MFA operations directly on
ServerClient(likemy_account), or at minimum markencrypt_mfa_token/decrypt_mfa_tokenas private (prefix with_) ?
There was a problem hiding this comment.
Was this intentional?
Yes. The .mfa sub-client pattern matches auth0-auth-js where MFA is accessed as authClient.mfa.listAuthenticators(),authClient.mfa.challengeAuthenticator(), etc. MFA is a multi-step conversational flow (list -> enroll/challenge -> verify) that shares state (mfa_token) across steps, so a dedicated sub-client fits better than wrapping 5+ methods on ServerClient.
MyAccountClient's operations are independent request/response pairs that naturally map to single ServerClient methods , MFA doesn't have that shape.
Is encrypt_mfa_token() / decrypt_mfa_token() being publicly callable intentional?
encrypt_mfa_token is only called internally by ServerClient.get_access_token(), so it shouldn't be public. Will rename to _encrypt_mfa_token in future PR.
The full MfaClient class becomes a public contract. Any future rename or refactor is a breaking change.
The 4 API methods + decrypt_mfa_token are intentionally public and mirror Auth0's MFA API directly, so there's little reason they'd change. This matches the JS SDK's approach.
Should we wrap MFA operations on ServerClient (like my_account), or mark encrypt/decrypt as private?
Wrapping on ServerClient would add 5+ methods and obscure the multi-step flow. Keeping the sub-client is the better fit. Will mark encrypt_mfa_token as private (_encrypt_mfa_token) since it's internal only. decrypt_mfa_token remains public as it's part of the caller's workflow.
| scope=merged_scope or "", | ||
| mfa_requirements=mfa_requirements | ||
| ) | ||
| raise MfaRequiredError( |
There was a problem hiding this comment.
Before this PR, get_access_token() raised AccessTokenError when MFA was required. Now it raises MfaRequiredError, which is a completely different exception type.
Any existing app that catches AccessTokenError around get_access_token() will start getting unhandled exceptions in production after upgrading with no warning.
Adds MFA support to the
auth0-server-pythonSDK via a newMfaClient(server_client.mfa) that handles the full MFA lifecycle — enrollment, challenge, and verification — for all supported factor types: OTP, SMS, Voice, Email, Push Notification (Auth0 Guardian), and Recovery Code.What's Included
MfaClientwith 4 methods:list_authenticators,enroll_authenticator,challenge_authenticator,verifyverify(persist=True)auto-updates the session store with new tokensmfa_tokenbetween requestsverify()returnsrecovery_codeon first enrollment and after recovery code useAuthenticatorResponse,ChallengeResponse,OtpEnrollmentResponse,OobEnrollmentResponse,MfaVerifyResponseMfaRequiredError,MfaChallengeError,MfaEnrollmentError,MfaVerifyError,MfaTokenExpiredErrorexamples/MFA.mdQuick Example