Enhance OAuth security, PKCE, and state validation

- Enforced stricter PKCE requirements by rejecting insecure 'plain' method for public clients.
- Transitioned client secret hashing to bcrypt for improved security and migration compatibility.
- Added constant-time comparison for state parameter validation to prevent timing attacks.
- Improved error handling and logging for OAuth workflows, including malformed headers and invalid scopes.
- Upgraded Google OIDC token validation to verify both signature and nonce.
- Refactored OAuth service methods and schemas for better readability, consistency, and compliance with RFC specifications.
This commit is contained in:
Felipe Cardoso
2025-11-26 00:14:26 +01:00
parent 0ea428b718
commit dc875c5c95
6 changed files with 284 additions and 159 deletions

View File

@@ -339,9 +339,7 @@ class OAuthTokenResponse(BaseModel):
token_type: str = Field(
default="Bearer", description="The type of token (typically 'Bearer')"
)
expires_in: int | None = Field(
None, description="Token lifetime in seconds"
)
expires_in: int | None = Field(None, description="Token lifetime in seconds")
refresh_token: str | None = Field(
None, description="Refresh token for obtaining new access tokens"
)
@@ -365,39 +363,21 @@ class OAuthTokenResponse(BaseModel):
class OAuthTokenIntrospectionResponse(BaseModel):
"""OAuth 2.0 Token Introspection Response (RFC 7662)."""
active: bool = Field(
..., description="Whether the token is currently active"
)
scope: str | None = Field(
None, description="Space-separated list of scopes"
)
client_id: str | None = Field(
None, description="Client identifier for the token"
)
active: bool = Field(..., description="Whether the token is currently active")
scope: str | None = Field(None, description="Space-separated list of scopes")
client_id: str | None = Field(None, description="Client identifier for the token")
username: str | None = Field(
None, description="Human-readable identifier for the resource owner"
)
token_type: str | None = Field(
None, description="Type of the token (e.g., 'Bearer')"
)
exp: int | None = Field(
None, description="Token expiration time (Unix timestamp)"
)
iat: int | None = Field(
None, description="Token issue time (Unix timestamp)"
)
nbf: int | None = Field(
None, description="Token not-before time (Unix timestamp)"
)
sub: str | None = Field(
None, description="Subject of the token (user ID)"
)
aud: str | None = Field(
None, description="Intended audience of the token"
)
iss: str | None = Field(
None, description="Issuer of the token"
)
exp: int | None = Field(None, description="Token expiration time (Unix timestamp)")
iat: int | None = Field(None, description="Token issue time (Unix timestamp)")
nbf: int | None = Field(None, description="Token not-before time (Unix timestamp)")
sub: str | None = Field(None, description="Subject of the token (user ID)")
aud: str | None = Field(None, description="Intended audience of the token")
iss: str | None = Field(None, description="Issuer of the token")
model_config = ConfigDict(
json_schema_extra={