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
- Variables
- func APICredentialToWebAuthn(c *api.PasskeyCredential) webauthn.Credential
- func APIKeyHash(key string) string
- func BeginConditionalLogin(wa *webauthn.WebAuthn) (*protocol.CredentialAssertion, *webauthn.SessionData, error)
- func BeginLogin(wa *webauthn.WebAuthn) (*protocol.CredentialAssertion, *webauthn.SessionData, error)
- func BeginRegistration(wa *webauthn.WebAuthn, user *WebAuthnUser) (*protocol.CredentialCreation, *webauthn.SessionData, error)
- func CanDisableAuthMethod(method api.AuthMethod, hasPassword bool, passkeyCount int, ...) bool
- func CheckBreachedPassword(ctx context.Context, client *http.Client, password string) (bool, error)
- func ClearSessionCookie(w http.ResponseWriter, r *http.Request)
- func DummyHash() string
- func FinishLogin(wa *webauthn.WebAuthn, sessionData *webauthn.SessionData, ...) (webauthn.User, *webauthn.Credential, error)
- func FinishRegistration(wa *webauthn.WebAuthn, user *WebAuthnUser, sessionData *webauthn.SessionData, ...) (*webauthn.Credential, error)
- func GenerateAPIKey() (plaintext, hash, prefix, suffix string, err error)
- func GenerateOIDCState() (string, error)
- func GeneratePKCE() (verifier, challenge string, err error)
- func GenerateSessionToken() (plaintext, hash string, err error)
- func HasRole(user *api.User, role api.Role) bool
- func HashPassword(password string) (string, error)
- func IsBrowserRequest(r *http.Request) bool
- func NewWebAuthn(rpID, rpDisplayName string, rpOrigins []string) (*webauthn.WebAuthn, error)
- func PasskeyFriendlyName(aaguid []byte, existingNames []string) string
- func ReadSessionCookie(r *http.Request) string
- func ResolveOIDCUser(claims *OIDCClaims, existingBySub *api.User) (user *api.User, isNew bool)
- func SessionCookieName(r *http.Request) string
- func SessionHash(token string) string
- func SetSessionCookie(w http.ResponseWriter, r *http.Request, token string, maxAge int)
- func ValidateOIDCConfig(cfg api.OIDCConfig) error
- func ValidatePasswordContext(password, username string) error
- func ValidatePasswordLength(password string, passwordOnly bool) error
- func ValidateRedirectURI(uri string) string
- func ValidateSession(sess *api.Session, idleTimeout, absTimeout time.Duration, now time.Time) error
- func VerifyAPIKey(ctx context.Context, store SessionStore, key string) (*api.Key, error)
- func VerifyPassword(password, encodedHash string) (bool, error)
- func WebAuthnCredentialToAPI(c *webauthn.Credential, userID int64, name string) *api.PasskeyCredential
- type AAGUIDEntry
- type APIKeyVerifier
- type Authenticator
- type CredentialVerifier
- type OIDCClaims
- type OIDCProvider
- type SessionStore
- type SessionVerifier
- type WebAuthnStore
- type WebAuthnUser
Constants ¶
const ( CookieNameSecure = "__Host-sfx_session" CookieNameHTTP = "sfx_session" )
Session cookie names.
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.
const PasswordMinLengthMultiFactor = authlib.PasswordMinLengthMultiFactor
PasswordMinLengthMultiFactor is the minimum password length when password login is not the sole sufficient factor.
const PasswordMinLengthSolo = authlib.PasswordMinLengthSolo
PasswordMinLengthSolo is the minimum password length when password login is enabled and thus a sole sufficient factor.
Variables ¶
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.
var ( ErrSessionExpired = errors.New("session expired") ErrSessionNotFound = errors.New("session not found") )
Sentinel errors for session operations.
var ErrInvalidAPIKey = errors.New("invalid API key")
ErrInvalidAPIKey is returned when an API key cannot be verified.
var ErrUnauthenticated = errors.New("unauthenticated")
ErrUnauthenticated is returned when no valid credential is found.
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 ¶
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 ¶
func BeginLogin(wa *webauthn.WebAuthn) (*protocol.CredentialAssertion, *webauthn.SessionData, error)
BeginLogin starts a WebAuthn assertion ceremony (discoverable login).
func BeginRegistration ¶
func BeginRegistration(wa *webauthn.WebAuthn, user *WebAuthnUser) (*protocol.CredentialCreation, *webauthn.SessionData, error)
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 ¶
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 ¶
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 ¶
GenerateOIDCState generates a random state string for OIDC flows.
func GeneratePKCE ¶
GeneratePKCE generates a PKCE code verifier and its S256 challenge.
func GenerateSessionToken ¶
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 HashPassword ¶
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 ¶
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 ¶
NewWebAuthn creates a configured webauthn.WebAuthn instance.
func PasskeyFriendlyName ¶
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 ¶
ReadSessionCookie reads the session token from the cookie.
func ResolveOIDCUser ¶
ResolveOIDCUser maps an OIDC identity to a user by (issuer, sub) only.
func SessionCookieName ¶
SessionCookieName returns the appropriate cookie name based on whether the request arrived over HTTPS.
func SessionHash ¶
SessionHash returns the hex-encoded SHA-256 hash of a plaintext token.
func SetSessionCookie ¶
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 ¶
ValidatePasswordContext rejects passwords that trivially embed the username or the application name.
func ValidatePasswordLength ¶
ValidatePasswordLength enforces minimum and maximum password length.
func ValidateRedirectURI ¶
ValidateRedirectURI ensures the URI is a safe relative path.
func ValidateSession ¶
ValidateSession checks whether a session is still valid given the idle and absolute timeout durations.
func VerifyAPIKey ¶
VerifyAPIKey hashes the provided key, looks it up in the store, and returns the matching APIKey record.
func VerifyPassword ¶
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 ¶
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.
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 ¶
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.
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.