feat: add mTLS (RFC 8705) client authentication - #159
Open
cschetan77 wants to merge 9 commits into
Open
Conversation
cschetan77
force-pushed
the
feat/mtls-client-authentication
branch
from
August 21, 2026 09:37
9ef2878 to
0915865
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Mutual TLS (RFC 8705) client authentication to
auth0-server-python. When enabled, the SDK presents a TLS client certificate during the Auth0 token-endpoint handshake instead of a client secret — no credential travels in the request body.ServerClientconstructor params:use_mtls: bool = Falseandssl_context: Optional[ssl.SSLContext] = None. The caller builds theSSLContext(ssl.create_default_context()+load_cert_chain); the SDK forwards it asverify=ssl_contextto everyhttpx.AsyncClientit constructs (including the authlib client used for the authorization-code exchange)._resolve_token_endpoint(metadata)helper returnsmtls_endpoint_aliases.token_endpointfrom the discovery document when mTLS is on, raisingConfigurationErrorif the alias is absent. All six token-endpoint call sites inserver_client.pyare routed through it._apply_client_authenticationreturnsNoneunder mTLS — the certificate in the TLS handshake is the sole credential; noclient_secretorclient_assertionis added to the body.MfaClientreceivesuse_mtlsandssl_contextfromServerClient(cert on all MFA calls).verify()accepts an optionaltoken_endpoint_overrideso the mTLS alias can be passed in for the token exchange while challenge/enroll calls remain on the standard host.signin_with_passkeyandMfaClient.verifyraiseConfigurationErrorwhen bothdpop_keyanduse_mtlsare active — DPoP would bind the token to its own key and suppresscnf.x5t#S256, silently defeating mTLS token binding.cnf.x5t#S256advisory warning: after obtaining a token under mTLS,_warn_if_not_cert_boundchecks whether the access token is certificate-bound and emits aUserWarningif not (silent on opaque tokens; never raises).ConfigurationError, fail-fast):use_mtls=Truewithoutssl_context; combined withclient_secret; combined withclient_assertion_signing_key.Changed files
auth_server/server_client.pyuse_mtls/ssl_contextparams, validation,_resolve_token_endpoint,_apply_client_authenticationmTLS branch,_warn_if_not_cert_bound, DPoP guard, 6 call-site routings,_get_http_client+AsyncOAuth2Clientverify=injectionauth_server/mfa_client.pyuse_mtls/ssl_contextparams,_get_http_clientverify=injection,verify()token_endpoint_overrideparam + DPoP guardtests/test_server_client.py_resolve_token_endpoint,_apply_client_authenticationmTLS branch, SSLContext threading,_warn_if_not_cert_bound, DPoP exclusion, end-to-end alias-routing testtests/test_mfa_client.pytoken_endpoint_override, DPoP exclusionexamples/MutualTLS.mdcnfverificationREADME.mdreferences/flow-map.mdTest plan
poetry run pytest— all tests pass (491 tests)poetry run ruff check .— no lint errorsConfigurationErrorfor: missingssl_contextwithuse_mtls=True;client_secret+use_mtls;client_assertion_signing_key+use_mtls_resolve_token_endpointreturns the mTLS alias when present, raisesConfigurationErrorwhen absent under mTLS, returns the standard endpoint when mTLS is offcomplete_interactive_loginunder mTLS hitsmtls_endpoint_aliases.token_endpointsignin_with_passkeyraisesConfigurationErrorwhendpop_key+use_mtlsmfa.verifyraisesConfigurationErrorwhendpop_key+use_mtls; usestoken_endpoint_overridewhen provided_warn_if_not_cert_boundwarns on JWT withoutcnf.x5t#S256, silent on JWT with it, silent on opaque token