auth

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 20, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package auth is Gombit's runtime Bearer JWT surface (C3 / M5-2).

Behavior lives here, not in generated apps. framework.New mounts the Huma routes when config.Auth.JWTSecret is set and a database is attached.

Access tokens are short-lived JWTs. Refresh tokens are opaque, stored hashed, and rotated on each successful refresh. The generated SPA keeps both in memory — never localStorage or sessionStorage. Cookie/session + CSRF (--auth cookie) is a first-class alternative (M5-3).

Users can receive stable permission keys directly or through groups. IsSuperuser bypasses every permission check.

Index

Constants

View Source
const (
	// AccessCookieName holds the short-lived access JWT in cookie mode.
	AccessCookieName = "gombit_access"
	// RefreshCookieName holds the rotating refresh token in cookie mode.
	RefreshCookieName = "gombit_refresh"
	// CSRFCookieName holds the signed double-submit CSRF token.
	CSRFCookieName = "gombit_csrf"
	// CSRFHeaderName is the header clients must echo the CSRF cookie value
	// into for state-changing (POST/PUT/PATCH/DELETE) requests.
	CSRFHeaderName = "X-CSRF-Token"
)

Cookie and CSRF header names for cookie-mode session auth (M5-3). See docs/auth-cookie.md for the threat model these pair with.

Variables

View Source
var ErrEmailTaken = errEmailTaken

ErrEmailTaken is the exported form of errEmailTaken. Register and CreateSuperuser return it (wrapped) so callers such as gombit createsuperuser can detect duplicates with errors.Is without a second uniqueness check.

Functions

func AddUserToGroup

func AddUserToGroup(ctx context.Context, db *gorm.DB, user *User, group *Group) error

AddUserToGroup adds a persisted user to a persisted group.

func CSRFMiddleware

func CSRFMiddleware(cfg config.Config) gin.HandlerFunc

CSRFMiddleware enforces the double-submit CSRF defense documented in docs/auth-cookie.md for cookie-mode session auth (M5-3). It is a Gin middleware (not scoped to Huma auth routes) because CSRF must cover every state-changing request in the app, including future feature routes, not just the auth surface.

Safe methods (GET/HEAD/OPTIONS) bootstrap a signed CSRF cookie when one is missing. Other methods require a X-CSRF-Token header that matches the gombit_csrf cookie and verifies against cfg.Auth.JWTSecret; otherwise the request is rejected with a D10 403 authorization error.

framework.New wires this into the runtime middleware stack automatically when cfg.Auth.Enabled() and cfg.Auth.EffectiveMode() == AuthModeCookie.

func GrantPermissionToGroup

func GrantPermissionToGroup(ctx context.Context, db *gorm.DB, group *Group, permission *Permission) error

GrantPermissionToGroup grants a persisted permission to a persisted group.

func GrantPermissionToUser

func GrantPermissionToUser(ctx context.Context, db *gorm.DB, user *User, permission *Permission) error

GrantPermissionToUser grants a persisted permission directly to a user.

func HasPermission

func HasPermission(ctx context.Context, db *gorm.DB, user User, key string) (bool, error)

HasPermission reports whether user has key directly, through a group, or through the Django-style superuser bypass.

func HasPermissions

func HasPermissions(ctx context.Context, db *gorm.DB, user User, keys ...string) (map[string]bool, error)

HasPermissions checks several keys while loading the user's associations once. Missing users and unassigned keys are reported as false.

func Migrate

func Migrate(db *gorm.DB) error

Migrate creates the auth identity, token, group, and permission tables.

func Models

func Models() []any

Models returns GORM models for Atlas / AutoMigrate.

func Mount

func Mount(api huma.API, db *gorm.DB, cfg config.Config) error

Mount registers framework-owned auth Huma routes on api. The route shapes differ by cfg.Auth.EffectiveMode(): AuthModeJWT (default) mounts the Bearer token surface (register/login/refresh/logout/me exchanging JSON tokens); AuthModeCookie mounts the same operation IDs and paths but exchanges HttpOnly session cookies instead, plus GET /auth/csrf. The two modes are mutually exclusive per app — see docs/auth-cookie.md for the cookie/CSRF threat model.

Types

type Clock

type Clock interface {
	Now() time.Time
}

Clock is the time source used for token expiry. Tests inject a fake.

type Group

type Group struct {
	ID          uint         `gorm:"primaryKey"`
	Name        string       `gorm:"uniqueIndex;not null;size:100"`
	Permissions []Permission `gorm:"many2many:auth_group_permissions;"`
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

Group collects permissions that can be assigned to users.

func EnsureGroup

func EnsureGroup(ctx context.Context, db *gorm.DB, name string) (Group, error)

EnsureGroup returns the group with name, creating it when needed.

type Hasher

type Hasher interface {
	Hash(password string) (string, error)
	Compare(hash, password string) error
}

Hasher hashes and compares passwords.

type Permission

type Permission struct {
	ID          uint   `gorm:"primaryKey"`
	Key         string `gorm:"column:permission_key;uniqueIndex;not null;size:120"` // not "key": reserved in MySQL
	Description string `gorm:"size:255"`
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

Permission is a stable authorization key, such as admin.widgets.view.

func EnsurePermission

func EnsurePermission(ctx context.Context, db *gorm.DB, key, description string) (Permission, error)

EnsurePermission returns the permission for key, creating it when needed.

type PublicUser

type PublicUser struct {
	ID    uint   `json:"id" example:"1" doc:"User identifier"`
	Email string `json:"email" example:"ada@example.com" format:"email" doc:"Normalized email"`
}

PublicUser is the safe user representation returned by register and /me.

type RefreshToken

type RefreshToken struct {
	ID         uint       `gorm:"primaryKey"`
	UserID     uint       `gorm:"index;not null"`
	TokenHash  string     `gorm:"uniqueIndex;size:64;not null"`
	ExpiresAt  time.Time  `gorm:"index;not null"`
	RevokedAt  *time.Time `gorm:"index"`
	ReplacedBy *uint
	CreatedAt  time.Time
}

RefreshToken is a hashed, rotating refresh credential.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the runtime auth implementation: users, password hashes, access JWTs, and rotating refresh tokens.

func NewService

func NewService(db *gorm.DB, cfg config.Config) (*Service, error)

NewService constructs a Service. cfg.Auth.JWTSecret must be set.

func (*Service) Authenticate

func (s *Service) Authenticate(ctx context.Context, email, password string) (User, error)

Authenticate verifies email and password.

func (*Service) CreateSuperuser

func (s *Service) CreateSuperuser(ctx context.Context, email, password string) (User, error)

CreateSuperuser creates a user with IsSuperuser set, for gombit createsuperuser (M4-6). It shares Register's hasher and uniqueness path; duplicate emails return errEmailTaken.

func (*Service) IssueTokens

func (s *Service) IssueTokens(ctx context.Context, user User) (TokenPair, error)

IssueTokens returns a new access JWT and a rotating refresh token.

func (*Service) ParseAccess

func (s *Service) ParseAccess(ctx context.Context, token string) (User, error)

ParseAccess validates a Bearer access JWT and loads the user.

func (*Service) Register

func (s *Service) Register(ctx context.Context, email, password string) (User, error)

Register creates a user. Duplicate emails return errEmailTaken.

func (*Service) RequireCookieSession

func (s *Service) RequireCookieSession() func(ctx huma.Context, next func(huma.Context))

RequireCookieSession is the exported cookie-session gate used by runtime packages such as admin. It authenticates the gombit_access cookie and stores the User on the request context for UserFromContext. It does not itself enforce CSRF; CSRFMiddleware (csrf.go) is wired as global Gin middleware ahead of it for state-changing requests.

func (*Service) RevokeRefresh

func (s *Service) RevokeRefresh(ctx context.Context, raw string) error

RevokeRefresh invalidates one refresh token. Missing/already-revoked tokens succeed so logout is idempotent.

func (*Service) RotateRefresh

func (s *Service) RotateRefresh(ctx context.Context, raw string) (TokenPair, error)

RotateRefresh validates the current refresh token, revokes it, and issues a new pair. Reuse of a revoked token revokes the user's remaining tokens. The lookup, revoke, and replacement happen in one transaction so concurrent refresh of the same token cannot mint two valid pairs.

type TokenPair

type TokenPair struct {
	AccessToken  string `json:"access_token" example:"eyJhbGciOiJIUzI1NiJ9..." doc:"Short-lived Bearer access JWT. Hold in memory only."`
	RefreshToken string `json:"refresh_token" example:"aa11..." doc:"Opaque rotating refresh token. Hold in memory only; never web storage."`
	TokenType    string `json:"token_type" example:"Bearer" doc:"Token type; always Bearer"`
	ExpiresIn    int    `json:"expires_in" example:"900" doc:"Access token lifetime in seconds"`
}

TokenPair is the login/refresh success payload (inside D10 data).

type User

type User struct {
	ID           uint         `gorm:"primaryKey"`
	Email        string       `gorm:"uniqueIndex;size:255;not null"`
	PasswordHash string       `gorm:"not null"`
	IsSuperuser  bool         `gorm:"not null;default:false"`
	Groups       []Group      `gorm:"many2many:auth_user_groups;"`
	Permissions  []Permission `gorm:"many2many:auth_user_permissions;"`
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

User is the auth identity. IsSuperuser bypasses every permission check; regular users receive permissions directly or through groups.

func UserFromContext

func UserFromContext(ctx context.Context) (User, bool)

UserFromContext returns the authenticated user stored by requireBearer or RequireCookieSession.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL