Install LOC guardrails (check-loc.sh, architecture.md, pre-commit hook) and split all 44 files exceeding 500 LOC into domain-focused modules: - consent-service (Go): models, handlers, services, database splits - backend-core (Python): security_api, rbac_api, pdf_service, auth splits - admin-core (TypeScript): 5 page.tsx + sidebar extractions - pitch-deck (TypeScript): 6 slides, 3 UI components, engine.ts splits - voice-service (Python): enhanced_task_orchestrator split Result: 0 violations, 36 exempted (pipeline, tests, pure-data files). Go build verified clean. No behavior changes — pure structural splits. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
104 lines
5.0 KiB
Go
104 lines
5.0 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// OAuthClient represents a registered OAuth 2.0 client application
|
|
type OAuthClient struct {
|
|
ID uuid.UUID `json:"id" db:"id"`
|
|
ClientID string `json:"client_id" db:"client_id"`
|
|
ClientSecret string `json:"-" db:"client_secret"` // Never expose in JSON
|
|
Name string `json:"name" db:"name"`
|
|
Description *string `json:"description,omitempty" db:"description"`
|
|
RedirectURIs []string `json:"redirect_uris" db:"redirect_uris"` // JSON array
|
|
Scopes []string `json:"scopes" db:"scopes"` // Allowed scopes
|
|
GrantTypes []string `json:"grant_types" db:"grant_types"` // authorization_code, refresh_token
|
|
IsPublic bool `json:"is_public" db:"is_public"` // Public clients (SPAs) don't have secret
|
|
IsActive bool `json:"is_active" db:"is_active"`
|
|
CreatedBy *uuid.UUID `json:"created_by,omitempty" db:"created_by"`
|
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
|
|
}
|
|
|
|
// OAuthAuthorizationCode represents an authorization code for the OAuth flow
|
|
type OAuthAuthorizationCode struct {
|
|
ID uuid.UUID `json:"id" db:"id"`
|
|
Code string `json:"-" db:"code"` // Hashed
|
|
ClientID string `json:"client_id" db:"client_id"`
|
|
UserID uuid.UUID `json:"user_id" db:"user_id"`
|
|
RedirectURI string `json:"redirect_uri" db:"redirect_uri"`
|
|
Scopes []string `json:"scopes" db:"scopes"`
|
|
CodeChallenge *string `json:"-" db:"code_challenge"` // For PKCE
|
|
CodeChallengeMethod *string `json:"-" db:"code_challenge_method"` // S256 or plain
|
|
ExpiresAt time.Time `json:"expires_at" db:"expires_at"`
|
|
UsedAt *time.Time `json:"used_at,omitempty" db:"used_at"`
|
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
}
|
|
|
|
// OAuthAccessToken represents an OAuth access token
|
|
type OAuthAccessToken struct {
|
|
ID uuid.UUID `json:"id" db:"id"`
|
|
TokenHash string `json:"-" db:"token_hash"`
|
|
ClientID string `json:"client_id" db:"client_id"`
|
|
UserID uuid.UUID `json:"user_id" db:"user_id"`
|
|
Scopes []string `json:"scopes" db:"scopes"`
|
|
ExpiresAt time.Time `json:"expires_at" db:"expires_at"`
|
|
RevokedAt *time.Time `json:"revoked_at,omitempty" db:"revoked_at"`
|
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
}
|
|
|
|
// OAuthRefreshToken represents an OAuth refresh token
|
|
type OAuthRefreshToken struct {
|
|
ID uuid.UUID `json:"id" db:"id"`
|
|
TokenHash string `json:"-" db:"token_hash"`
|
|
AccessTokenID uuid.UUID `json:"access_token_id" db:"access_token_id"`
|
|
ClientID string `json:"client_id" db:"client_id"`
|
|
UserID uuid.UUID `json:"user_id" db:"user_id"`
|
|
Scopes []string `json:"scopes" db:"scopes"`
|
|
ExpiresAt time.Time `json:"expires_at" db:"expires_at"`
|
|
RevokedAt *time.Time `json:"revoked_at,omitempty" db:"revoked_at"`
|
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
}
|
|
|
|
// OAuthAuthorizeRequest for the authorization endpoint
|
|
type OAuthAuthorizeRequest struct {
|
|
ResponseType string `form:"response_type" binding:"required"` // Must be "code"
|
|
ClientID string `form:"client_id" binding:"required"`
|
|
RedirectURI string `form:"redirect_uri" binding:"required"`
|
|
Scope string `form:"scope"` // Space-separated scopes
|
|
State string `form:"state" binding:"required"` // CSRF protection
|
|
CodeChallenge string `form:"code_challenge"` // PKCE
|
|
CodeChallengeMethod string `form:"code_challenge_method"` // S256 (recommended) or plain
|
|
}
|
|
|
|
// OAuthTokenRequest for the token endpoint
|
|
type OAuthTokenRequest struct {
|
|
GrantType string `form:"grant_type" binding:"required"` // authorization_code or refresh_token
|
|
Code string `form:"code"` // For authorization_code grant
|
|
RedirectURI string `form:"redirect_uri"` // For authorization_code grant
|
|
ClientID string `form:"client_id" binding:"required"`
|
|
ClientSecret string `form:"client_secret"` // For confidential clients
|
|
CodeVerifier string `form:"code_verifier"` // For PKCE
|
|
RefreshToken string `form:"refresh_token"` // For refresh_token grant
|
|
Scope string `form:"scope"` // For refresh_token grant (optional)
|
|
}
|
|
|
|
// OAuthTokenResponse for successful token requests
|
|
type OAuthTokenResponse struct {
|
|
AccessToken string `json:"access_token"`
|
|
TokenType string `json:"token_type"` // Always "Bearer"
|
|
ExpiresIn int `json:"expires_in"` // Seconds until expiration
|
|
RefreshToken string `json:"refresh_token,omitempty"`
|
|
Scope string `json:"scope,omitempty"`
|
|
}
|
|
|
|
// OAuthErrorResponse for OAuth errors (RFC 6749)
|
|
type OAuthErrorResponse struct {
|
|
Error string `json:"error"`
|
|
ErrorDescription string `json:"error_description,omitempty"`
|
|
ErrorURI string `json:"error_uri,omitempty"`
|
|
}
|