forked from cardosofelipe/fast-next-template
- Implemented OAuth 2.0 Authorization Server endpoints per RFCs, including token, introspection, revocation, and metadata discovery. - Added user consent submission, listing, and revocation APIs alongside frontend integration for improved UX. - Enforced stricter OAuth security measures (PKCE, state validation, scopes). - Refactored schemas and services for consistency and expanded coverage of OAuth workflows. - Updated documentation and type definitions for new API behaviors.
46 lines
1.5 KiB
Python
Executable File
46 lines
1.5 KiB
Python
Executable File
"""OAuth state model for CSRF protection during OAuth flows."""
|
|
|
|
from sqlalchemy import Column, DateTime, String
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
|
|
from .base import Base, TimestampMixin, UUIDMixin
|
|
|
|
|
|
class OAuthState(Base, UUIDMixin, TimestampMixin):
|
|
"""
|
|
Temporary storage for OAuth state parameters.
|
|
|
|
Prevents CSRF attacks during OAuth flows by storing a random state
|
|
value that must match on callback. Also stores PKCE code_verifier
|
|
for the Authorization Code flow with PKCE.
|
|
|
|
These records are short-lived (10 minutes by default) and should
|
|
be deleted after use or expiration.
|
|
"""
|
|
|
|
__tablename__ = "oauth_states"
|
|
|
|
# Random state parameter (CSRF protection)
|
|
state = Column(String(255), unique=True, nullable=False, index=True)
|
|
|
|
# PKCE code_verifier (used to generate code_challenge)
|
|
code_verifier = Column(String(128), nullable=True)
|
|
|
|
# OIDC nonce for ID token replay protection
|
|
nonce = Column(String(255), nullable=True)
|
|
|
|
# OAuth provider (google, github, etc.)
|
|
provider = Column(String(50), nullable=False)
|
|
|
|
# Original redirect URI (for callback validation)
|
|
redirect_uri = Column(String(500), nullable=True)
|
|
|
|
# User ID if this is an account linking flow (user is already logged in)
|
|
user_id = Column(UUID(as_uuid=True), nullable=True)
|
|
|
|
# Expiration time
|
|
expires_at = Column(DateTime(timezone=True), nullable=False)
|
|
|
|
def __repr__(self):
|
|
return f"<OAuthState {self.state[:8]}... ({self.provider})>"
|