auth

package
v0.1.28 Latest Latest
Warning

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

Go to latest
Published: Jun 10, 2026 License: GPL-2.0, GPL-3.0 Imports: 20 Imported by: 0

Documentation

Overview

Package auth implements authentication logic for Subflux: password hashing (Argon2id), TOTP, WebAuthn, OIDC, sessions, API keys, rate limiting, and RBAC middleware.

Index

Constants

View Source
const (
	CookieNameSecure = "__Host-sfx_session"
	CookieNameHTTP   = "sfx_session"
)

Session cookie names.

View Source
const CeremonyTimeout = 5 * time.Minute

CeremonyTimeout is the maximum duration a user has to complete an auth ceremony (TOTP, WebAuthn registration/login). Used for both server-side session expiry and client-side WebAuthn timeout configuration.

View Source
const PasswordMinLengthMultiFactor = authlib.PasswordMinLengthMultiFactor

PasswordMinLengthMultiFactor is the minimum password length when password login is not the sole sufficient factor.

View Source
const PasswordMinLengthSolo = authlib.PasswordMinLengthSolo

PasswordMinLengthSolo is the minimum password length when password login is enabled and thus a sole sufficient factor.

Variables

View Source
var (
	ErrOIDCDiscovery     = errors.New("oidc: provider discovery failed")
	ErrOIDCExchange      = errors.New("oidc: code exchange failed")
	ErrOIDCTokenInvalid  = errors.New("oidc: ID token verification failed")
	ErrOIDCNonceMismatch = errors.New("oidc: nonce mismatch")
	ErrOIDCConfigInvalid = errors.New("oidc: invalid configuration")
)

Sentinel errors for OIDC operations.

View Source
var (
	ErrSessionExpired  = errors.New("session expired")
	ErrSessionNotFound = errors.New("session not found")
)

Sentinel errors for session operations.

View Source
var ErrInvalidAPIKey = errors.New("invalid API key")

ErrInvalidAPIKey is returned when an API key cannot be verified.

View Source
var ErrUnauthenticated = errors.New("unauthenticated")

ErrUnauthenticated is returned when no valid credential is found.

View Source
var KnownAAGUIDs = []AAGUIDEntry{
	{"ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4", "Google Password Manager"},
	{"adce0002-35bc-c60a-648b-0b25f1f05503", "Chrome on Mac"},
	{"08987058-cadc-4b81-b6e1-30de50dcbe96", credNameWindowsHello},
	{"9ddd1817-af5a-4672-a2b9-3e3dd95000a9", credNameWindowsHello},
	{"6028b017-b1d4-4c02-b4b3-afcdafc96bb2", credNameWindowsHello},
	{"dd4ec289-e01d-41c9-bb89-70fa845d4bf2", "iCloud Keychain"},
	{"fbfc3007-154e-4ecc-8c0b-6e020557d7bd", "iCloud Keychain"},
	{"d548826e-79b4-db40-a3d8-11116f7e8349", "Bitwarden"},
	{"b5397723-31d4-4c13-b037-37be46e30e9e", "1Password"},
	{"bada5566-a7aa-401f-bd96-45619a55120d", "1Password"},
	{"2fc0579f-8113-47ea-b116-bb5a8db9202a", "YubiKey 5"},
	{"fa2b99dc-9e39-4257-8f92-4a30d23c4118", "YubiKey 5 NFC"},
}

KnownAAGUIDs is the registry of known authenticator AAGUIDs and their friendly names. Source: https://github.com/passkeydeveloper/passkey-authenticator-aaguids

Functions

func APICredentialToWebAuthn

func APICredentialToWebAuthn(c *api.PasskeyCredential) webauthn.Credential

APICredentialToWebAuthn converts an api.PasskeyCredential to a webauthn.Credential.

func APIKeyHash

func APIKeyHash(key string) string

APIKeyHash returns the hex-encoded SHA-256 hash of a key string.

func BeginConditionalLogin

func BeginConditionalLogin(wa *webauthn.WebAuthn) (*protocol.CredentialAssertion, *webauthn.SessionData, error)

BeginConditionalLogin starts a WebAuthn assertion ceremony with conditional mediation. The browser shows passkeys in the autofill dropdown instead of a full-screen modal.

func BeginLogin

BeginLogin starts a WebAuthn assertion ceremony (discoverable login).

func BeginRegistration

BeginRegistration starts a WebAuthn registration ceremony. Requires resident key (discoverable credential) and excludes already-registered authenticators to prevent duplicate registrations. Requests the credProps extension so the browser reports whether the credential is discoverable.

func CanDisableAuthMethod

func CanDisableAuthMethod(method api.AuthMethod, hasPassword bool, passkeyCount int, oidcEnabled, oidcLinked bool) bool

CanDisableAuthMethod checks whether disabling the given auth method would leave the user with no viable authentication method.

func CheckBreachedPassword

func CheckBreachedPassword(ctx context.Context, client *http.Client, password string) (bool, error)

CheckBreachedPassword checks a password against the Have I Been Pwned Passwords API using k-anonymity. Returns true if the password has been found in a breach.

func ClearSessionCookie

func ClearSessionCookie(w http.ResponseWriter, r *http.Request)

ClearSessionCookie clears the session cookie.

func DummyHash

func DummyHash() string

DummyHash returns a pre-computed Argon2id hash used by the login handler to equalize timing when the username doesn't exist (H2 mitigation). The hash is computed lazily on first call via sync.Once.

func FinishLogin

func FinishLogin(wa *webauthn.WebAuthn, sessionData *webauthn.SessionData, response *http.Request, userFinder func(rawID, userHandle []byte) (webauthn.User, error)) (webauthn.User, *webauthn.Credential, error)

FinishLogin completes a WebAuthn assertion ceremony (discoverable login). The userFinder callback resolves the user from the credential's user handle. Returns both the resolved user and the validated credential.

func FinishRegistration

func FinishRegistration(wa *webauthn.WebAuthn, user *WebAuthnUser, sessionData *webauthn.SessionData, response *http.Request) (*webauthn.Credential, error)

FinishRegistration completes a WebAuthn registration ceremony.

func GenerateAPIKey

func GenerateAPIKey() (plaintext, hash, prefix, suffix string, err error)

GenerateAPIKey generates a new API key with 256 bits of entropy. It returns the plaintext key (prefixed with "sfx_"), its SHA-256 hash, a display prefix (first 8 chars), and a display suffix (last 4 chars).

func GenerateOIDCState

func GenerateOIDCState() (string, error)

GenerateOIDCState generates a random state string for OIDC flows.

func GeneratePKCE

func GeneratePKCE() (verifier, challenge string, err error)

GeneratePKCE generates a PKCE code verifier and its S256 challenge.

func GenerateSessionToken

func GenerateSessionToken() (plaintext, hash string, err error)

GenerateSessionToken generates a cryptographically random session token (256 bits / 32 bytes). It returns the hex-encoded plaintext token and its SHA-256 hash (also hex-encoded).

func HasRole

func HasRole(user *api.User, role api.Role) bool

HasRole reports whether the user is authorized for the given role.

func HashPassword

func HashPassword(password string) (string, error)

HashPassword hashes a password using Argon2id with OWASP parameters. Returns the hash in PHC string format: $argon2id$v=19$m=19456,t=2,p=1$<base64-salt>$<base64-hash>

func IsBrowserRequest

func IsBrowserRequest(r *http.Request) bool

IsBrowserRequest returns true if the request appears to be from a browser (Accept header contains text/html and no X-API-Key header).

func NewWebAuthn

func NewWebAuthn(rpID, rpDisplayName string, rpOrigins []string) (*webauthn.WebAuthn, error)

NewWebAuthn creates a configured webauthn.WebAuthn instance.

func PasskeyFriendlyName

func PasskeyFriendlyName(aaguid []byte, existingNames []string) string

PasskeyFriendlyName returns a human-friendly name for a passkey based on its AAGUID. Known AAGUIDs are mapped to provider names. If the name already exists in existingNames, a numeric suffix is appended (e.g. "Google Password Manager 2"). Unknown AAGUIDs fall back to "Passkey N".

func ReadSessionCookie

func ReadSessionCookie(r *http.Request) string

ReadSessionCookie reads the session token from the cookie.

func ResolveOIDCUser

func ResolveOIDCUser(claims *OIDCClaims, existingBySub *api.User) (user *api.User, isNew bool)

ResolveOIDCUser maps an OIDC identity to a user by (issuer, sub) only.

func SessionCookieName

func SessionCookieName(r *http.Request) string

SessionCookieName returns the appropriate cookie name based on whether the request arrived over HTTPS.

func SessionHash

func SessionHash(token string) string

SessionHash returns the hex-encoded SHA-256 hash of a plaintext token.

func SetSessionCookie

func SetSessionCookie(w http.ResponseWriter, r *http.Request, token string, maxAge int)

SetSessionCookie sets the session cookie on the response.

func ValidateOIDCConfig

func ValidateOIDCConfig(cfg api.OIDCConfig) error

ValidateOIDCConfig checks that the required fields of an OIDCConfig are set.

func ValidatePasswordContext

func ValidatePasswordContext(password, username string) error

ValidatePasswordContext rejects passwords that trivially embed the username or the application name.

func ValidatePasswordLength

func ValidatePasswordLength(password string, passwordOnly bool) error

ValidatePasswordLength enforces minimum and maximum password length.

func ValidateRedirectURI

func ValidateRedirectURI(uri string) string

ValidateRedirectURI ensures the URI is a safe relative path.

func ValidateSession

func ValidateSession(sess *api.Session, idleTimeout, absTimeout time.Duration, now time.Time) error

ValidateSession checks whether a session is still valid given the idle and absolute timeout durations.

func VerifyAPIKey

func VerifyAPIKey(ctx context.Context, store SessionStore, key string) (*api.Key, error)

VerifyAPIKey hashes the provided key, looks it up in the store, and returns the matching APIKey record.

func VerifyPassword

func VerifyPassword(password, encodedHash string) (bool, error)

VerifyPassword verifies a password against an encoded Argon2id hash in PHC string format. Uses constant-time comparison.

func WebAuthnCredentialToAPI

func WebAuthnCredentialToAPI(c *webauthn.Credential, userID int64, name string) *api.PasskeyCredential

WebAuthnCredentialToAPI converts a webauthn.Credential to an api.PasskeyCredential.

Types

type AAGUIDEntry

type AAGUIDEntry struct {
	UUID string
	Name string
}

AAGUIDEntry maps an authenticator AAGUID (as UUID string) to a friendly name.

type APIKeyVerifier

type APIKeyVerifier struct {
	Store SessionStore
}

APIKeyVerifier authenticates requests via X-API-Key header or api_key query param.

func (*APIKeyVerifier) Verify

func (v *APIKeyVerifier) Verify(ctx context.Context, r *http.Request) (*api.User, string, error)

Verify checks the API key header and query param, returns the user if valid.

type Authenticator

type Authenticator struct {
	Store SessionStore
	// Bypass reports whether all authentication is disabled (nil means never).
	Bypass      func() bool
	IdleTimeout time.Duration
	AbsTimeout  time.Duration
}

Authenticator resolves an HTTP request to an authenticated user.

func (*Authenticator) Authenticate

func (a *Authenticator) Authenticate(r *http.Request) (*api.User, string, error)

Authenticate checks session cookie first, then API key header, then API key query param. Returns the user and session hash, or ErrUnauthenticated.

func (*Authenticator) RequireAuth

func (a *Authenticator) RequireAuth(w http.ResponseWriter, r *http.Request) (*api.User, string, bool)

RequireAuth checks authentication and returns the user. If not authenticated, it writes the appropriate response (401 or redirect) and returns ok=false.

type CredentialVerifier

type CredentialVerifier interface {
	// Verify attempts to authenticate the request. Returns the user and
	// session hash on success, or a nil user if this verifier cannot
	// authenticate the request (credential not present). Returns an error
	// only on internal failures (DB errors, etc.).
	Verify(ctx context.Context, r *http.Request) (*api.User, string, error)
}

CredentialVerifier resolves an HTTP request to an authenticated user using a specific credential type (session, API key, passkey).

type OIDCClaims

type OIDCClaims struct {
	Subject           string `json:"sub"`
	Issuer            string `json:"iss"`
	Email             string `json:"email"`
	PreferredUsername string `json:"preferred_username"`
	Name              string `json:"name"`
	EmailVerified     bool   `json:"email_verified"`
}

OIDCClaims holds the verified claims extracted from an OIDC ID token.

type OIDCProvider

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

OIDCProvider wraps the coreos/go-oidc provider with PKCE support.

func NewOIDCProvider

func NewOIDCProvider(ctx context.Context, cfg api.OIDCConfig) (*OIDCProvider, error)

NewOIDCProvider creates an OIDC provider from config.

func (*OIDCProvider) AuthorizationURL

func (p *OIDCProvider) AuthorizationURL(state, nonce, codeChallenge string) string

AuthorizationURL generates the OIDC authorization URL with PKCE and state.

func (*OIDCProvider) Exchange

func (p *OIDCProvider) Exchange(ctx context.Context, code, codeVerifier, nonce string) (*OIDCClaims, *time.Time, error)

Exchange exchanges an authorization code for tokens and validates the ID token.

type SessionStore

type SessionStore interface {
	GetSessionByHash(ctx context.Context, tokenHash string) (*api.Session, error)
	GetUserByID(ctx context.Context, id int64) (*api.User, error)
	GetAPIKeyByHash(ctx context.Context, hash string) (*api.Key, error)
}

SessionStore is the narrow interface consumed by Authenticator. It declares only the store methods needed for session and API-key authentication, enabling focused testing with minimal fakes. The concrete store.DB satisfies this interface via structural typing.

type SessionVerifier

type SessionVerifier struct {
	Store       SessionStore
	IdleTimeout time.Duration
	AbsTimeout  time.Duration
}

SessionVerifier authenticates requests via session cookie.

func (*SessionVerifier) Verify

func (v *SessionVerifier) Verify(ctx context.Context, r *http.Request) (*api.User, string, error)

Verify checks the session cookie and returns the user if valid.

type WebAuthnStore

type WebAuthnStore interface {
	GetPasskeysByUserID(ctx context.Context, userID int64) ([]api.PasskeyCredential, error)
	UpdatePasskeyAfterLogin(ctx context.Context, credID []byte, signCount uint32, flags api.PasskeyFlags) error
	GetUserByID(ctx context.Context, id int64) (*api.User, error)
}

WebAuthnStore is the narrow interface consumed by WebAuthn/passkey handlers. It declares only the 3 store methods needed for passkey authentication, enabling focused testing with minimal fakes.

type WebAuthnUser

type WebAuthnUser struct {
	User        *api.User
	Credentials []api.PasskeyCredential
}

WebAuthnUser adapts api.User + credentials to the webauthn.User interface. User must never be nil; use NewWebAuthnUser to construct.

func NewWebAuthnUser

func NewWebAuthnUser(user *api.User, creds []api.PasskeyCredential) (*WebAuthnUser, error)

NewWebAuthnUser returns a WebAuthnUser with the given user and credentials. Returns an error if user is nil.

func (*WebAuthnUser) WebAuthnCredentials

func (u *WebAuthnUser) WebAuthnCredentials() []webauthn.Credential

WebAuthnCredentials converts the stored credentials to webauthn.Credential.

func (*WebAuthnUser) WebAuthnDisplayName

func (u *WebAuthnUser) WebAuthnDisplayName() string

WebAuthnDisplayName returns the display name, falling back to username.

func (*WebAuthnUser) WebAuthnID

func (u *WebAuthnUser) WebAuthnID() []byte

WebAuthnID encodes the user ID as a binary varint.

func (*WebAuthnUser) WebAuthnName

func (u *WebAuthnUser) WebAuthnName() string

WebAuthnName returns the username.

Jump to

Keyboard shortcuts

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