superuser

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package superuser implements a system-operator identity plane that is structurally separate from the user package: its own table (via SuperuserStore), its own claims type, and — critically — its own JWT audience, so a perfectly valid user access token can never be accepted by superuser middleware, and vice versa, even though both planes may share the same underlying jwt.Signer/secret.

Index

Constants

View Source
const DefaultAudience = "authit-superuser"

DefaultAudience is the JWT audience claim that marks a token as belonging to the superuser plane.

Variables

View Source
var (
	ErrInvalidCredentials   = errors.New("authit/superuser: invalid credentials")
	ErrAccountLocked        = errors.New("authit/superuser: account locked")
	ErrInactive             = errors.New("authit/superuser: account is not active")
	ErrInvalidToken         = errors.New("authit/superuser: invalid or expired token")
	ErrCannotDeactivateSelf = errors.New("authit/superuser: cannot deactivate your own account")
	ErrAlreadyBootstrapped  = errors.New("authit/superuser: at least one superuser already exists")

	ErrTwoFactorUnavailable = errors.New("authit/superuser: this deployment has no second-factor store")
	ErrTwoFactorEnabled     = errors.New("authit/superuser: a second factor is already enabled")
	ErrTwoFactorNotEnabled  = errors.New("authit/superuser: no second factor is enabled")
	ErrInvalidTwoFactor     = errors.New("authit/superuser: invalid second-factor code")
	ErrSessionNotFound      = errors.New("authit/superuser: no such session")
)
View Source
var ErrRateLimited = ratelimit.ErrRateLimited

ErrRateLimited is returned when Config.RateLimiter refuses a login. Alias for ratelimit.ErrRateLimited. A limiter's own failure is propagated unchanged instead.

View Source
var ErrWeakPassword = authitcrypto.ErrWeakPassword

ErrWeakPassword is returned by Bootstrap and CreateSuperuser when Config.PasswordValidator rejects the password. Alias for crypto.ErrWeakPassword so errors.Is matches either.

Functions

This section is empty.

Types

type Claims

type Claims struct {
	jwt.RegisteredClaims
	Email string `json:"email,omitempty"`
}

Claims is the admin-plane access token claim set. Subject carries the superuser ID; ID (jti) carries the session — the refresh token's row id — so a request can tell which of an operator's sessions it is. Impersonation does NOT use this type — Impersonate mints an ordinary jwt.Claims (user-plane) token instead, so it flows through existing user routes unchanged; see jwt.Claims.ActorID.

func (Claims) HasAudience

func (c Claims) HasAudience(aud string) bool

HasAudience reports whether aud is present in the claims' audience list.

type Config

type Config struct {
	// Audience is the JWT audience claim that marks a token as belonging to
	// this plane. Defaults to DefaultAudience.
	Audience string
	// AccessTokenTTL defaults to 5 minutes.
	AccessTokenTTL time.Duration
	// RefreshTokenTTL defaults to 7 days.
	RefreshTokenTTL time.Duration
	// ImpersonationTTL defaults to 15 minutes.
	ImpersonationTTL time.Duration
	// MaxFailedLoginAttempts defaults to 5. Ignored if Stores.Lockouts is
	// nil.
	MaxFailedLoginAttempts int
	// FailedLoginWindow defaults to 15 minutes.
	FailedLoginWindow time.Duration
	// AuditLogger receives security-relevant events (login, lockout,
	// deactivation, impersonation). Nil means events are not recorded —
	// see package audit.
	AuditLogger audit.Logger
	// PasswordHasher hashes and verifies operator passwords. Nil means
	// crypto.DefaultHasher() — Argon2id. Existing hashes in any format
	// authit has written keep verifying, and are upgraded on next login.
	PasswordHasher authitcrypto.Hasher
	// PasswordValidator rejects unacceptable passwords when an operator
	// account is created. Nil means crypto.DefaultPasswordPolicy().
	// Consider a stricter policy here than on the user plane: these
	// accounts can impersonate.
	PasswordValidator authitcrypto.PasswordValidator
	// ImpersonationSigner signs the user-plane tokens Impersonate mints.
	// Nil means this plane's own signer, which is right when one key serves
	// both planes. Set it when the user plane has a key of its own: a token
	// the user plane cannot verify impersonates nobody.
	ImpersonationSigner authitjwt.Signer
	// RateLimiter throttles Authenticate before the password KDF runs.
	// Nil means ratelimit.Noop. Keys:
	//
	//	superuser-login:ip:<ip>        per source address
	//	superuser-login:email:<email>  per account, normalised
	RateLimiter ratelimit.Limiter
	// TOTPEncryptionKey encrypts TOTP secrets at rest; 32 bytes. Required
	// when Stores.TwoFactor is set.
	TOTPEncryptionKey []byte
	// TOTPIssuer names the account in an authenticator app. Defaults to
	// "authit-superuser".
	TOTPIssuer string
	// BackupCodeCount defaults to 10.
	BackupCodeCount int
	// PendingTwoFactorTTL is how long an operator has between a correct
	// password and a correct code. Defaults to 5 minutes.
	PendingTwoFactorTTL time.Duration
}

Config tunes the superuser package's flows. Defaults are intentionally stricter than the user package's: short-lived access tokens, since operator sessions warrant more frequent re-authentication.

type LoginResult added in v0.4.0

type LoginResult struct {
	Tokens         *TokenPair
	TwoFactorToken string
}

LoginResult is what Authenticate returns: tokens, or — for an operator with a second factor — the pending token VerifyTwoFactorLogin exchanges for them. Exactly one is set.

type Service

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

Service implements the superuser/operator auth plane.

func NewService

func NewService(stores Stores, signer authitjwt.Signer, cfg Config) (*Service, error)

NewService constructs a Service. signer may be the same jwt.Signer used by the user package — audience separation, not a separate secret, is what keeps the two planes from accepting each other's tokens. Config.AuditLogger may be nil, in which case audit.NoopLogger is used.

func (*Service) Authenticate

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

Authenticate verifies email/password against the superuser table and issues a token pair scoped to this plane's audience.

An operator with a second factor gets a LoginResult carrying only a TwoFactorToken; VerifyTwoFactorLogin completes the sign-in.

func (*Service) BeginTwoFactorSetup added in v0.4.0

func (s *Service) BeginTwoFactorSetup(ctx context.Context, superuserID, email string) (TwoFactorSetup, error)

BeginTwoFactorSetup stores a fresh secret, not yet enabled; nothing changes at sign-in until ConfirmTwoFactorSetup proves the app holds it.

func (*Service) Bootstrap

func (s *Service) Bootstrap(ctx context.Context, email, password, displayName string) (store.Superuser, error)

Bootstrap creates the first superuser, and only the first: it fails with ErrAlreadyBootstrapped if any superuser already exists. Intended to be called once at application startup from a trusted source (e.g. an environment variable), never from an HTTP handler.

func (*Service) ChangePassword added in v0.4.0

func (s *Service) ChangePassword(ctx context.Context, superuserID, currentSessionID, current, next string) (int, error)

ChangePassword replaces the operator's password and ends every other session, reporting how many: whoever else knew the old one is signed out with it.

func (*Service) ConfirmTwoFactorSetup added in v0.4.0

func (s *Service) ConfirmTwoFactorSetup(ctx context.Context, superuserID, code string) ([]string, error)

ConfirmTwoFactorSetup enables the factor from BeginTwoFactorSetup and returns the backup codes, in plaintext, once.

func (*Service) CreateFromShell added in v0.4.0

func (s *Service) CreateFromShell(ctx context.Context, email, password, displayName string) (store.Superuser, error)

CreateFromShell creates a superuser attributed to nobody, for a command run by whoever holds a shell on the deployment. That access already outranks this plane, so there is no signed-in superuser to name as creator.

func (*Service) CreateSuperuser

func (s *Service) CreateSuperuser(ctx context.Context, email, password, displayName, createdByID string) (store.Superuser, error)

CreateSuperuser creates an additional superuser, attributed to createdByID (another superuser's ID). There is no public registration endpoint for this plane by design — only an authenticated superuser (or Bootstrap) can create one.

func (*Service) Deactivate

func (s *Service) Deactivate(ctx context.Context, callerID, targetID string) error

Deactivate soft-deletes a superuser account and revokes all of its sessions. There is deliberately no reactivate: a deactivated superuser is not recoverable through this API.

func (*Service) DisableTwoFactor added in v0.4.0

func (s *Service) DisableTwoFactor(ctx context.Context, superuserID, code string) error

DisableTwoFactor removes the factor, given a current code or a backup code, so an operator who lost the app can still turn it off.

func (*Service) Impersonate

func (s *Service) Impersonate(ctx context.Context, superuserID, targetUserID, targetUserEmail string) (string, error)

Impersonate mints a short-lived, ordinary user-plane access token (no superuser audience) for targetUserID, stamped with ActorID=superuserID so it is distinguishable from a normal login (jwt.Claims.IsImpersonation). Because it carries the user-plane's normal shape, it flows through existing user routes/middleware unchanged.

The token is not tracked server-side: it is valid until it naturally expires (Config.ImpersonationTTL, default 15 minutes) and cannot be revoked early. This bounds blast radius via a short TTL rather than a live revocation check on every request. The call is recorded through Config.AuditLogger if one is configured (see package audit) — early revocation is still on the host, but the trail is not.

func (*Service) ListSessions added in v0.4.0

func (s *Service) ListSessions(ctx context.Context, superuserID, currentSessionID string) ([]Session, error)

ListSessions returns the operator's live sessions, oldest first.

func (*Service) ListSuperusers

func (s *Service) ListSuperusers(ctx context.Context) ([]store.Superuser, error)

ListSuperusers lists every superuser account.

func (*Service) Logout

func (s *Service) Logout(ctx context.Context, refreshToken string) error

Logout revokes a single refresh token. Idempotent.

func (*Service) Refresh

func (s *Service) Refresh(ctx context.Context, refreshToken, userAgent, ipAddress string) (TokenPair, error)

Refresh exchanges a valid, unrevoked superuser refresh token for a new token pair, rotating it.

func (*Service) RegenerateBackupCodes added in v0.4.0

func (s *Service) RegenerateBackupCodes(ctx context.Context, superuserID, code string) ([]string, error)

RegenerateBackupCodes replaces the backup codes. It takes an app code only, so a stolen backup code cannot mint more.

func (*Service) RevokeOtherSessions added in v0.4.0

func (s *Service) RevokeOtherSessions(ctx context.Context, superuserID, currentSessionID string) (int, error)

RevokeOtherSessions ends every session but currentSessionID and reports how many it ended.

func (*Service) RevokeSession added in v0.4.0

func (s *Service) RevokeSession(ctx context.Context, superuserID, sessionID string) error

RevokeSession ends one of the operator's own sessions. Another operator's session id is ErrSessionNotFound, like one that does not exist.

func (*Service) TwoFactorStatus added in v0.4.0

func (s *Service) TwoFactorStatus(ctx context.Context, superuserID string) (TwoFactorStatus, error)

TwoFactorStatus reports whether superuserID has a second factor.

func (*Service) Verify

func (s *Service) Verify(token string) (Claims, error)

Verify validates a superuser access token, additionally requiring the configured audience so a valid user-plane token can never be accepted here.

func (*Service) VerifyTwoFactorLogin added in v0.4.0

func (s *Service) VerifyTwoFactorLogin(ctx context.Context, pendingToken, code, userAgent, ipAddress string) (TokenPair, error)

VerifyTwoFactorLogin completes a sign-in Authenticate left pending, exchanging the pending token and an app or backup code for tokens.

type Session added in v0.4.0

type Session struct {
	ID        string
	IsCurrent bool
	UserAgent string
	IPAddress string
	CreatedAt time.Time
	ExpiresAt time.Time
}

Session is one of an operator's signed-in devices.

type Stores

type Stores struct {
	Superusers    store.SuperuserStore
	RefreshTokens store.SuperuserRefreshTokenStore
	// Lockouts is optional; if nil, failed-login lockout is disabled.
	Lockouts store.LockoutStore
	// Tx is optional. Supplying it makes Refresh's rotation atomic; nil
	// leaves it as two independent writes. See store.TxRunner.
	Tx store.TxRunner
	// TwoFactor is optional; if nil, operators cannot enrol a second factor
	// and Authenticate never asks for one. Requires Config.TOTPEncryptionKey.
	TwoFactor store.SuperuserTwoFactorStore
}

Stores groups the persistence ports the superuser package needs.

type TokenPair

type TokenPair struct {
	AccessToken  string
	RefreshToken string
	ExpiresAt    time.Time
}

TokenPair is what a completed login/refresh returns.

type TwoFactorSetup added in v0.4.0

type TwoFactorSetup struct {
	Secret     string
	OTPAuthURL string
}

TwoFactorSetup is what BeginTwoFactorSetup returns for the operator's authenticator app.

type TwoFactorStatus added in v0.4.0

type TwoFactorStatus struct {
	Available            bool
	Enabled              bool
	VerifiedAt           *time.Time
	RemainingBackupCodes int
}

TwoFactorStatus is an operator's second-factor state.

Jump to

Keyboard shortcuts

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