Help us shape the future of Go! Take the 2026 Go Developer Survey

auth

package
v0.18.3 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2026 License: AGPL-3.0 Imports: 39 Imported by: 0

Documentation

Overview

Package auth provides users, roles and permissions, sessions, CSRF, API keys, magic links, login rate limiting and the admin shell (login, setup, locale switch). The dashboard at /admin belongs to the admin module.

Index

Constants

View Source
const (
	KeyKindAPI   = "api"
	KeyKindAgent = "agent"
)

API key kinds. API keys authenticate on /api (and /mcp); agent keys only on /mcp, so an AI agent can never reach the REST API (for example to approve its own sensitive calls).

View Source
const (
	StatusActive   = "active"
	StatusDisabled = "disabled"
	RoleAdmin      = "admin"
	RoleCustomer   = "customer"
)

User statuses and the customer role.

View Source
const DefaultMagicLinkTTL = 15 * time.Minute

DefaultMagicLinkTTL applies when NewMagicLink gets ttl == 0.

View Source
const MaxRotateGrace = 7 * 24 * time.Hour

MaxRotateGrace bounds how long a rotated key keeps working.

View Source
const ReauthWindow = 10 * time.Minute

ReauthWindow is how long a sign-in or password confirmation counts as recent for RequireRecentAuth.

Variables

View Source
var (
	ErrKeyRevoked       = errors.New("auth: api key revoked")
	ErrKeyExpired       = errors.New("auth: api key expired")
	ErrKeyOwnerInactive = errors.New("auth: api key owner is not active")
)

Errors from WithAPIKeyActor (besides ErrNotFound for an unknown key).

View Source
var (
	ErrRoleNotFound = errors.New("auth: role not found")
	ErrRoleInUse    = errors.New("auth: role is assigned to users")
	ErrSystemRole   = errors.New("auth: system role cannot be deleted or have its slug changed")
)

Role errors returned by RoleService.

View Source
var (
	ErrNotFound           = errors.New("auth: not found")
	ErrEmailTaken         = errors.New("auth: email already registered")
	ErrInvalidCredentials = errors.New("auth: invalid email or password")
	ErrLocked             = errors.New("auth: too many failed attempts, try again later")
	ErrInvalidToken       = errors.New("auth: invalid or expired token")
	ErrSetupDone          = errors.New("auth: setup already completed")
	ErrOnboardingRequired = errors.New("auth: first-login setup required")
)

Errors returned by Service.

View Source
var AnonymousAPIPaths = []string{"/api/v1/openapi.json", "/api/docs", "/api/v1/site/info"}

AnonymousAPIPaths are API paths served without a key or session (the api module may append during Register, before serving starts).

Functions

func APIKeyMiddleware

func APIKeyMiddleware(deps *kernel.Deps) func(http.Handler) http.Handler

APIKeyMiddleware authenticates API requests: "Authorization: Bearer gp_…" (actor kind api_key, scopes, per-key rate limit → 429), or else a valid session cookie (unsafe methods then need X-CSRF-Token). Anything else gets the 401 envelope, except AnonymousAPIPaths. Agent keys (KeyKindAgent) get 403 agent_key_not_allowed: they work only on the MCP endpoint.

func ActorFromContext

func ActorFromContext(ctx context.Context) audit.Actor

ActorFromContext returns the audit actor for ctx: the user, the API key, or audit.SystemActor.

func CSRFMiddleware

func CSRFMiddleware() func(http.Handler) http.Handler

CSRFMiddleware rejects non-GET/HEAD/OPTIONS requests (403) whose _csrf form value or X-CSRF-Token header does not match the session token. Without a session it uses a double-submit cookie (gp_csrf, issued on first visit), which protects anonymous forms such as login and setup. Run it after SessionMiddleware.

func CSRFToken

func CSRFToken(ctx context.Context) string

CSRFToken returns the token forms must send as _csrf (or HTMX as X-CSRF-Token): the session's token, or for anonymous visitors the double-submit token from the gp_csrf cookie. Requires CSRFMiddleware.

func Can

func Can(ctx context.Context, perm string) bool

Can reports whether the principal in ctx holds perm: an active user whose role grants it, and for API keys additionally a matching scope (a key never exceeds its owner's role). Pending first-login setup grants no permissions.

func CanGrant

func CanGrant(ctx context.Context, scopes []string) bool

CanGrant reports whether the principal in ctx may hand out every scope in scopes (when creating or rotating an API key): each permission must pass Can, and "*" needs a role that holds "*" (and, for a key, "*" in the key's own scopes). Nobody can therefore mint a key stronger than themselves.

func HashPassword

func HashPassword(password string) (string, error)

HashPassword returns a PHC-encoded Argon2id hash: $argon2id$v=19$m=19456,t=2,p=1$<salt>$<hash> (raw std base64).

func MCPKeyMiddleware

func MCPKeyMiddleware(deps *kernel.Deps) func(http.Handler) http.Handler

MCPKeyMiddleware is APIKeyMiddleware for the MCP endpoint: it accepts both API and agent keys.

func NewMagicLink(ctx context.Context, deps *kernel.Deps, userID string, ttl time.Duration) (string, error)

NewMagicLink creates a single-use login token for userID (sent by email as BaseURL + "/auth/magic/" + token). Only its SHA-256 is stored.

func RecentlyAuthenticated

func RecentlyAuthenticated(ctx context.Context, window time.Duration) bool

RecentlyAuthenticated reports whether ctx is a browser session whose user signed in or confirmed their password within the last window. API keys, CLI and job contexts are never recent: they have no session.

func ReloadRoles

func ReloadRoles(ctx context.Context, d *sql.DB) error

ReloadRoles reloads the role → permission mapping from role_permissions.

func RequirePerm

func RequirePerm(perm string) func(http.Handler) http.Handler

RequirePerm answers 403 unless Can(ctx, perm): a JSON envelope for API requests, plain text otherwise.

func RequireRecentAuth

func RequireRecentAuth(next http.Handler) http.Handler

RequireRecentAuth guards sensitive admin routes (backup downloads): a session older than ReauthWindow is sent to /admin/reauth, which asks for the password again and then returns to the original URL.

func SessionMiddleware

func SessionMiddleware(deps *kernel.Deps) func(http.Handler) http.Handler

SessionMiddleware loads the user from the gp_session cookie (sliding 14 day expiry) into the context. It never rejects a request.

func StartUserSession

func StartUserSession(d *kernel.Deps, w http.ResponseWriter, r *http.Request, u *User) error

StartUserSession starts the native session after a caller has authenticated the user. It is not a password verifier; callers must verify credentials first.

func VerifyPassword

func VerifyPassword(encoded, password string) bool

VerifyPassword accepts native Argon2id and migrated WordPress password hashes. Malformed hashes and parameters outside bounded verification costs never match.

func WithAPIKeyActor

func WithAPIKeyActor(ctx context.Context, d *kernel.Deps, keyID string) (context.Context, error)

WithAPIKeyActor returns ctx acting as the API key keyID, exactly as if the key had authenticated the request: Can checks the key's scopes and its owner's role, and audit records the key as the actor. It is for work done later on a key's behalf (e.g. an MCP call executed once a human approves it). The key must exist (ErrNotFound), not be revoked (ErrKeyRevoked) or expired (ErrKeyExpired), and its owner must be active (ErrKeyOwnerInactive) and must have completed first-login setup (ErrOnboardingRequired).

func WithUser

func WithUser(ctx context.Context, u *User) context.Context

WithUser returns ctx acting as u (as a session user). Used by tests, the CLI and jobs that act on behalf of a user.

Types

type APIKey

type APIKey struct {
	ID, Name, Prefix, UserID                    string
	Kind                                        string // KeyKindAPI or KeyKindAgent
	Scopes                                      []string
	RatePerMin                                  int
	ExpiresAt, LastUsedAt, CreatedAt, RevokedAt string
	UseCount                                    int64  // approximate: flushed at most once a minute
	RotatedFrom                                 string // id of the key this one replaced
}

APIKey is a bearer key: plaintext "gp_" + Prefix (8) + secret (32), stored as SHA-256. Scopes are permission strings; "*" grants all of the owner's permissions.

type KeyService

type KeyService interface {
	// Create creates an API key of kind KeyKindAPI.
	Create(ctx context.Context, name, userID string, scopes []string, rate int, expires string) (plaintext string, k *APIKey, err error)
	// CreateKind is Create with an explicit kind (KeyKindAPI or KeyKindAgent).
	CreateKind(ctx context.Context, kind, name, userID string, scopes []string, rate int, expires string) (plaintext string, k *APIKey, err error)
	List(ctx context.Context) ([]APIKey, error)
	Get(ctx context.Context, id string) (*APIKey, error)
	Revoke(ctx context.Context, id string) error
	// Rotate issues a new key with the same name, kind, owner, scopes, rate
	// and expiry, and retires the old one: revoked now when grace is 0,
	// otherwise expiring after grace (so clients can switch without downtime).
	Rotate(ctx context.Context, id string, grace time.Duration) (plaintext string, k *APIKey, err error)
}

KeyService manages API keys.

func NewKeyService

func NewKeyService(d *sql.DB, a *audit.Writer, perms *kernel.PermissionRegistry) KeyService

NewKeyService returns the API key service on d. Create validates scopes against perms (plus "*"; nil skips the check); creation and revocation are recorded through a (nil: not audited). rate <= 0 means 60/min; expires is RFC3339 (normalised to UTC) or "" for never.

type ListQuery

type ListQuery struct {
	Search        string
	Page, PerPage int
	Sort, Order   string
}

ListQuery filters Service.List. Sort: email, name, role, created_at, last_login_at (default created_at); Order: asc or desc (default desc).

type Module

type Module struct{}

Module is the auth kernel.Module.

func New

func New() *Module

New returns the auth module.

func (*Module) Catalogs

func (*Module) Catalogs() fs.FS

Catalogs implements kernel.CatalogProvider.

func (*Module) Migrations

func (*Module) Migrations() fs.FS

Migrations implements kernel.Module.

func (*Module) Name

func (*Module) Name() string

Name implements kernel.Module.

func (*Module) Register

func (*Module) Register(d *kernel.Deps) error

Register declares permissions, publishes the services (kernel.ServiceAuth, ServiceAPIKeys, ServiceRoles, ServiceAdminTemplates), installs the auth middleware on Admin, API and Public, mounts the auth routes and finally calls Router.SetAuthenticator.

type Role added in v0.2.0

type Role struct {
	Slug        string
	Name        map[string]string // locale → label (en required)
	System      bool
	Permissions []string
	UserCount   int
}

Role is a named permission pack. System roles are seeded; custom roles have System=false and may be deleted when no users reference them.

type RolePatch added in v0.2.0

type RolePatch struct {
	Name        map[string]string
	Permissions *[]string
}

RolePatch updates only its non-nil fields. Permissions replaces the full grant set when non-nil (empty slice clears all grants).

type RoleService added in v0.2.0

type RoleService interface {
	List(ctx context.Context) ([]Role, error)
	Get(ctx context.Context, slug string) (*Role, error)
	Create(ctx context.Context, slug string, name map[string]string, permissions []string) (*Role, error)
	Update(ctx context.Context, slug string, patch RolePatch) (*Role, error)
	Delete(ctx context.Context, slug string) error
}

RoleService manages roles and role_permissions.

func NewRoleService added in v0.2.0

func NewRoleService(d *sql.DB, a *audit.Writer, perms *kernel.PermissionRegistry) RoleService

NewRoleService returns the role service. Create/Update validate permissions against perms (nil skips the known-permission check).

type Service

type Service interface {
	Create(ctx context.Context, email, name, password, role string) (*User, error)
	GetByEmail(ctx context.Context, email string) (*User, error)
	Get(ctx context.Context, id string) (*User, error)
	List(ctx context.Context, q ListQuery) ([]User, int, error)
	Update(ctx context.Context, id string, patch UserPatch) (*User, error)
	Delete(ctx context.Context, id string) error
	CheckPassword(ctx context.Context, email, password string) (*User, error) // rate-limited per IP (audit.IPFromContext) + email
	Count(ctx context.Context) (int, error)
}

Service manages users.

func NewService

func NewService(d *sql.DB, a *audit.Writer) Service

NewService returns the user service on d (auth migrations applied). Every change and every failed login is recorded through a (nil: not audited).

type User

type User struct {
	ID, Email, Name, Role, Locale, Status string
	LastLoginAt, CreatedAt, UpdatedAt     string
	// contains filtered or unexported fields
}

User is an account. Customers have role "customer".

func BootstrapAdmin added in v0.4.0

func BootstrapAdmin(ctx context.Context, d *sql.DB, a *audit.Writer, password string) (*User, error)

BootstrapAdmin creates the first admin atomically, assigning the durable admin login alias and mandatory first-login setup. ErrSetupDone means an account already exists; it never resets it.

func ConsumeMagicLink(ctx context.Context, deps *kernel.Deps, token string) (*User, error)

ConsumeMagicLink marks token used and returns its active user, or ErrInvalidToken when it is unknown, used or expired.

func UserFromContext

func UserFromContext(ctx context.Context) *User

UserFromContext returns the acting user (an API key's owner for key requests), or nil.

type UserPatch

type UserPatch struct {
	Name, Email, Role, Locale, Status, Password *string
}

UserPatch updates only its non-nil fields.

type ValidationError

type ValidationError struct{ Fields map[string]string }

ValidationError lists invalid fields; values are i18n keys.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Jump to

Keyboard shortcuts

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