Made PKCE and nonce optional, only when client wants them.
This commit is contained in:
+3
-3
@@ -23,15 +23,15 @@ AUTH_CODE_LIFETIME = timedelta(seconds=60)
|
|||||||
class OIDCCode(msgspec.Struct):
|
class OIDCCode(msgspec.Struct):
|
||||||
"""An OIDC authorization code pending token exchange.
|
"""An OIDC authorization code pending token exchange.
|
||||||
|
|
||||||
PKCE uses S256 only (verified at auth time).
|
PKCE uses S256 only when provided (verified at token exchange).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
session_key: str
|
session_key: str
|
||||||
created: datetime
|
created: datetime
|
||||||
redirect_uri: str
|
redirect_uri: str
|
||||||
scope: str
|
scope: str
|
||||||
nonce: str
|
nonce: str | None = None
|
||||||
code_challenge: str
|
code_challenge: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class CookieCode(msgspec.Struct):
|
class CookieCode(msgspec.Struct):
|
||||||
|
|||||||
+18
-17
@@ -196,23 +196,24 @@ async def _handle_authorization_code(
|
|||||||
status_code=400,
|
status_code=400,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify PKCE (S256 only, enforced at auth time)
|
# Verify PKCE if code_challenge was provided at authorization time
|
||||||
if not code_verifier:
|
if oidc_code.code_challenge:
|
||||||
return JSONResponse(
|
if not code_verifier:
|
||||||
{
|
return JSONResponse(
|
||||||
"error": "invalid_grant",
|
{
|
||||||
"error_description": "Missing code_verifier",
|
"error": "invalid_grant",
|
||||||
},
|
"error_description": "Missing code_verifier",
|
||||||
status_code=400,
|
},
|
||||||
)
|
status_code=400,
|
||||||
if not _verify_pkce(code_verifier, oidc_code.code_challenge):
|
)
|
||||||
return JSONResponse(
|
if not _verify_pkce(code_verifier, oidc_code.code_challenge):
|
||||||
{
|
return JSONResponse(
|
||||||
"error": "invalid_grant",
|
{
|
||||||
"error_description": "Invalid code_verifier",
|
"error": "invalid_grant",
|
||||||
},
|
"error_description": "Invalid code_verifier",
|
||||||
status_code=400,
|
},
|
||||||
)
|
status_code=400,
|
||||||
|
)
|
||||||
|
|
||||||
# Get user from session
|
# Get user from session
|
||||||
user = db.data().users.get(session.user_uuid)
|
user = db.data().users.get(session.user_uuid)
|
||||||
|
|||||||
+13
-19
@@ -158,25 +158,19 @@ async def websocket_authenticate(
|
|||||||
await ws.send_json({"status": 400, "detail": "Scope must include openid"})
|
await ws.send_json({"status": 400, "detail": "Scope must include openid"})
|
||||||
return
|
return
|
||||||
|
|
||||||
# PKCE is required with S256
|
# PKCE: when code_challenge is present, only S256 is supported
|
||||||
if not code_challenge:
|
# If method is omitted, default to S256 per best practice (not "plain" per RFC 7636)
|
||||||
await ws.send_json(
|
# When code_challenge is absent, ignore code_challenge_method entirely
|
||||||
{"status": 400, "detail": "PKCE code_challenge is required"}
|
if code_challenge:
|
||||||
)
|
if code_challenge_method and code_challenge_method != "S256":
|
||||||
return
|
await ws.send_json(
|
||||||
if code_challenge_method and code_challenge_method != "S256":
|
{
|
||||||
await ws.send_json(
|
"status": 400,
|
||||||
{
|
"detail": "Only S256 code_challenge_method is supported",
|
||||||
"status": 400,
|
}
|
||||||
"detail": "Only S256 code_challenge_method is supported",
|
)
|
||||||
}
|
return
|
||||||
)
|
# Default to S256 when method not specified (implicit)
|
||||||
return
|
|
||||||
|
|
||||||
# Nonce is required for OIDC
|
|
||||||
if not nonce:
|
|
||||||
await ws.send_json({"status": 400, "detail": "nonce is required for OIDC"})
|
|
||||||
return
|
|
||||||
|
|
||||||
# Validate state parameter if provided (defensive against injection)
|
# Validate state parameter if provided (defensive against injection)
|
||||||
if state:
|
if state:
|
||||||
|
|||||||
Reference in New Issue
Block a user