Add complete auth subsystem with OAuth 2.1 authorization server using ory/fosite, local user accounts with bcrypt password hashing, session management, and HTTP handlers for the Web UI. Components: - User store with bcrypt hashing (configurable cost, default 12), CRUD, validation (username 3-64 chars alphanumeric+underscore, password 8-72 bytes) - Session store with secure random IDs, configurable lifetime (default 24h), expiration cleanup, and per-user invalidation - OAuth client store with client_id/secret generation and bcrypt verification - Fosite storage adapter implementing CoreStorage, TokenRevocationStorage, and PKCERequestStorage backed by SQLite - OAuth provider configured with authorization code (PKCE S256 mandatory), client credentials, refresh token rotation, and token introspection - HTTP handlers: POST /auth/register, POST /auth/login, POST /auth/logout, GET /auth/me, PUT /auth/password, GET /oauth/authorize, POST /oauth/token, POST /oauth/introspect - Middleware: RequireSession (cookie), RequireBearer (access token), RequireAuth (either), RequireAdmin (role check) - Structured auth event logging (login, token issuance, session lifecycle) - Schema migration 002_auth.sql extending users, oauth_clients, oauth_tokens tables and adding sessions, oauth_authorization_codes tables - Initial admin user auto-created on first run with random password printed to stdout - All tests pass with CGO_ENABLED=0, zero external runtime dependencies Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
39 lines
1.6 KiB
Go
39 lines
1.6 KiB
Go
package auth
|
|
|
|
import "errors"
|
|
|
|
// Sentinel errors for the auth package.
|
|
var (
|
|
// User errors
|
|
ErrUserNotFound = errors.New("user not found")
|
|
ErrDuplicateUsername = errors.New("username already exists")
|
|
ErrInvalidUsername = errors.New("username must be 3-64 characters, alphanumeric and underscore only")
|
|
ErrPasswordTooShort = errors.New("password must be at least 8 characters")
|
|
ErrPasswordTooLong = errors.New("password must be at most 72 bytes (bcrypt limit)")
|
|
ErrInvalidPassword = errors.New("invalid password")
|
|
|
|
// Session errors
|
|
ErrSessionNotFound = errors.New("session not found")
|
|
ErrSessionExpired = errors.New("session expired")
|
|
|
|
// OAuth client errors
|
|
ErrClientNotFound = errors.New("client not found")
|
|
|
|
// Token errors
|
|
ErrTokenNotFound = errors.New("token not found")
|
|
ErrTokenExpired = errors.New("token expired")
|
|
ErrTokenConsumed = errors.New("token already consumed")
|
|
ErrTokenInvalid = errors.New("invalid token")
|
|
ErrCodeNotFound = errors.New("authorization code not found")
|
|
ErrCodeExpired = errors.New("authorization code expired")
|
|
ErrCodeUsed = errors.New("authorization code already used")
|
|
ErrPKCERequired = errors.New("PKCE code_challenge is required")
|
|
ErrPKCEPlainNotAllowed = errors.New("code_challenge_method 'plain' is not allowed, use S256")
|
|
ErrPKCEVerifierMismatch = errors.New("code_verifier does not match code_challenge")
|
|
|
|
// Config errors
|
|
ErrBcryptCostTooLow = errors.New("bcrypt cost must be at least 10")
|
|
ErrBcryptCostTooHigh = errors.New("bcrypt cost must be at most 31")
|
|
ErrSecretTooShort = errors.New("system secret must be at least 32 bytes")
|
|
)
|