Skip to content

Commit 7b9de6b

Browse files
committed
Trimmed comments and removed a dead code block
1 parent 88e6ecb commit 7b9de6b

2 files changed

Lines changed: 6 additions & 37 deletions

File tree

src/auth0_server_python/auth_server/mfa_client.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -471,13 +471,9 @@ async def verify(
471471
- 'audience': str (optional, required if persist=True) - Audience for token_set
472472
- 'scope': str (optional) - Scope for token_set
473473
store_options: Optional options passed to the State Store (e.g. request/response).
474-
dpop_key: Optional EC P-256 JWK for DPoP-bound token exchange. Pass the
475-
same key the login flow was bound to (e.g. the dpop_key given to
476-
signin_with_passkey) so the MFA step-up preserves the sender
477-
constraint. When provided, attaches a DPoP proof so Auth0 issues a
478-
DPoP-bound token (token_type: DPoP); the SDK never stores this
479-
Tier 0 key — the caller re-supplies it, consistent with every other
480-
DPoP entry point.
474+
dpop_key: Optional EC P-256 JWK to DPoP-bind the token. Pass the same
475+
key used at login (e.g. given to signin_with_passkey) to preserve
476+
the sender constraint through step-up. Never stored by the SDK.
481477
482478
Returns:
483479
MfaVerifyResponse with access_token, token_type, etc.
@@ -530,9 +526,7 @@ async def verify(
530526
headers=headers
531527
)
532528

533-
# RFC 9449 §8.2 — the authorization server signals a required
534-
# nonce with HTTP 400/401 + a DPoP-Nonce header. Rebuild the proof
535-
# with the nonce and retry once.
529+
# Rebuild the proof with the nonce and retry once.
536530
if (
537531
dpop_key is not None
538532
and response.status_code in (400, 401)
@@ -569,24 +563,14 @@ async def verify(
569563
token_response = response.json()
570564
verify_response = MfaVerifyResponse(**token_response)
571565

572-
# DPoP binding must be consistent in both directions. token_type
573-
# is the documented OAuth response field; per RFC 9449 a
574-
# sender-constrained token is returned as token_type "DPoP".
566+
# Reject a Bearer downgrade when a DPoP token was requested
567+
# (RFC 9449: a bound token has token_type "DPoP").
575568
token_is_dpop = verify_response.token_type.lower() == "dpop"
576569
if dpop_key is not None and not token_is_dpop:
577-
# We asked for a bound token but got Bearer — cannot prove
578-
# possession on later calls; reject rather than downgrade.
579570
raise MfaVerifyError(
580571
"DPoP token binding failed: expected token_type 'DPoP', "
581572
f"got '{verify_response.token_type}'"
582573
)
583-
if dpop_key is None and token_is_dpop:
584-
# Server issued a DPoP-bound token but no key was supplied —
585-
# we cannot prove possession, so accepting it would fail open.
586-
raise MfaVerifyError(
587-
"Server returned a DPoP-bound token but no dpop_key was "
588-
"provided; pass dpop_key to verify to bind it"
589-
)
590574

591575
# Clear the in-progress MFA state after successful verification.
592576
if self._state_store:

src/auth0_server_python/tests/test_mfa_client.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -974,21 +974,6 @@ async def test_verify_dpop_rejects_bearer_downgrade(self, mocker):
974974
dpop_key=dpop_key,
975975
)
976976

977-
@pytest.mark.asyncio
978-
async def test_verify_dpop_bound_token_without_key_rejected(self, mocker):
979-
"""Server returns a DPoP-bound token but no key supplied: fail closed, don't accept."""
980-
client = _make_client()
981-
response = AsyncMock()
982-
response.status_code = 200
983-
response.headers = {}
984-
response.json = MagicMock(return_value={
985-
"access_token": "bound_at", "token_type": "DPoP", "expires_in": 3600
986-
})
987-
mocker.patch("httpx.AsyncClient.post", new_callable=AsyncMock, return_value=response)
988-
989-
with pytest.raises(MfaVerifyError, match="no dpop_key was"):
990-
await client.verify({"mfa_token": _enc(), "otp": "123456"})
991-
992977
@pytest.mark.asyncio
993978
async def test_verify_without_dpop_no_dpop_header(self, mocker):
994979
"""Without dpop_key the request carries no DPoP header and Bearer is accepted."""

0 commit comments

Comments
 (0)