Documentation
¶
Index ¶
- Constants
- func DummyVerify(secret string)
- func FormatToken(id, secret string) string
- func GenerateRecoveryCodes(n int) ([]string, error)
- func GenerateTOTPSecret() (string, error)
- func HashBindingToken(token string) string
- func HashRecoveryCode(code string) string
- func HashSecret(secret string) (string, error)
- func NewRandomToken(bytes int) (string, error)
- func OTPAuthURI(issuer, account, secret string) string
- func SplitToken(presented string) (id, secret string, ok bool)
- func TOTPCodeAt(secret string, t time.Time) (string, error)
- func ValidateTOTP(secret, code string, t time.Time) bool
- func ValidateTOTPStep(secret, code string, t time.Time) (uint64, bool)
- func VerifySecret(encoded, secret string) bool
- type OIDCAuthState
- type Session
- type TOTPChallenge
- type WebAuthnChallenge
- type WebAuthnCredential
Constants ¶
const ( WebAuthnPurposeRegister = "register" WebAuthnPurposeLogin = "login" WebAuthnPurposeStepUp = "step_up" )
WebAuthn ceremony purposes. A challenge is minted for exactly one of these and may only be redeemed by the matching finish handler.
Variables ¶
This section is empty.
Functions ¶
func DummyVerify ¶ added in v0.2.0
func DummyVerify(secret string)
DummyVerify performs a verification against a throwaway hash so that code paths handling an unknown user or token spend roughly the same CPU time as a successful lookup. This blunts timing side channels that would otherwise leak account/token existence. The boolean result is meaningless and ignored.
func FormatToken ¶ added in v0.2.0
FormatToken renders the credential a caller presents for a personal access token: the public record id, a separator, and the secret. Embedding the id lets the server look the record up in O(1) and run a single verification, instead of trying every stored token.
func GenerateRecoveryCodes ¶ added in v0.2.0
GenerateRecoveryCodes returns n single-use recovery codes shown to the user exactly once; only their HashSecret hashes are persisted.
func GenerateTOTPSecret ¶ added in v0.2.0
GenerateTOTPSecret returns a fresh base32 (unpadded) shared secret.
func HashBindingToken ¶ added in v0.2.0
HashBindingToken returns the hex SHA-256 of a browser-binding token. The callback hashes the presented cookie and compares it to the stored hash.
func HashRecoveryCode ¶ added in v0.2.0
HashRecoveryCode hashes a recovery code for storage. Recovery codes carry ~80 bits of entropy, so a fast SHA-256 (not a slow password KDF) is sufficient and keeps the constant-time, lock-held comparison in the store cheap.
func HashSecret ¶
func NewRandomToken ¶
func OTPAuthURI ¶ added in v0.2.0
OTPAuthURI builds the otpauth:// URI an authenticator app consumes. The issuer and account are shown to the user; the secret is the base32 shared key.
func SplitToken ¶ added in v0.2.0
SplitToken parses a presented credential of the form "<id>.<secret>". Record ids never contain a dot and secrets are URL-safe base64 (also dot-free), so splitting on the first dot is unambiguous.
func TOTPCodeAt ¶ added in v0.2.0
TOTPCodeAt returns the 6-digit code for a specific instant. Exposed mainly so tests can assert against the RFC 6238 vectors and so enrollment can preview.
func ValidateTOTP ¶ added in v0.2.0
ValidateTOTP reports whether code matches secret at time t, tolerating one step of clock skew on either side. The comparison is constant-time.
func ValidateTOTPStep ¶ added in v0.2.0
ValidateTOTPStep reports whether code matches secret at time t (tolerating one step of clock skew on either side) and, on a match, returns the RFC-6238 counter (step) that matched. The returned step lets a caller enforce single-use of each code by persisting the highest accepted step and rejecting any submission whose step is not strictly greater. The comparison is constant-time; every candidate step is compared without an early return so the timing does not reveal which window matched.
func VerifySecret ¶
Types ¶
type OIDCAuthState ¶ added in v0.2.0
type OIDCAuthState struct {
State string `json:"state"`
Nonce string `json:"nonce"`
CodeVerifier string `json:"code_verifier"`
ProviderID string `json:"provider_id"`
ClientIP string `json:"client_ip"`
RedirectAfter string `json:"redirect_after"`
// BindingHash is the SHA-256 (hex) of a per-flow token set as an HttpOnly
// cookie at /start; the callback must present the matching cookie. This
// binds the round trip to the initiating browser (login-CSRF defense), the
// spec-intended role of `state`. Only the hash is persisted.
BindingHash string `json:"binding_hash"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
}
OIDCAuthState is the short-lived, single-use state for one in-flight OIDC auth-code login. It is created at /start, persisted, and consumed at /callback. It binds the round trip together:
- State: the OAuth2 `state` parameter (CSRF defense; also the store key).
- Nonce: checked against the ID token `nonce` claim (replay defense).
- CodeVerifier: the PKCE verifier (binds the code to this client).
- ClientIP: the requester's IP; the callback must come from the same IP.
- RedirectAfter: a sanitized same-origin path to land on after login.
func NewOIDCAuthState ¶ added in v0.2.0
func NewOIDCAuthState(providerID, clientIP, redirectAfter, codeVerifier, bindingToken string, ttl time.Duration) (OIDCAuthState, error)
NewOIDCAuthState mints state with fresh random `state` and `nonce` values, the caller-supplied PKCE verifier (generated by the oidc layer via oauth2.GenerateVerifier so this package stays dependency-free), and the SHA-256 of the caller-supplied browser-binding token.
func (OIDCAuthState) Expired ¶ added in v0.2.0
func (s OIDCAuthState) Expired() bool
Expired reports whether the state has passed its TTL.
type Session ¶
type Session struct {
ID string
ActorID string
CSRFToken string
// Epoch records the user's SecurityEpoch at the instant this session was
// minted. principalFromRequest rejects a session whose Epoch is older than
// the user's current SecurityEpoch, so a privilege-reducing event (2FA
// disable, password change, admin revoke) invalidates all prior sessions.
Epoch uint64
CreatedAt time.Time
ExpiresAt time.Time
Revoked bool
}
type TOTPChallenge ¶ added in v0.2.0
type TOTPChallenge struct {
ID string
UserID string
ClientIP string
CreatedAt time.Time
ExpiresAt time.Time
Used bool
// Attempts counts failed second-factor submissions; the store burns the
// challenge once it reaches the per-challenge cap.
Attempts int
}
TOTPChallenge is a short-lived, single-use gate issued after a first factor (password or OIDC) succeeds but before a session is granted to a 2FA user. It is store-backed and IP-bound, mirroring Session.
func NewTOTPChallenge ¶ added in v0.2.0
func NewTOTPChallenge(userID, clientIP string, ttl time.Duration) (TOTPChallenge, error)
NewTOTPChallenge mints a challenge bound to a user and the requesting client.
type WebAuthnChallenge ¶ added in v0.2.0
type WebAuthnChallenge struct {
ID string `json:"id"`
// UserID is the owning user for a registration ceremony. It is empty for a
// usernameless (discoverable) login, where the user is only resolved at finish
// from the authenticator's user handle.
UserID string `json:"user_id,omitempty"`
// ClientIP binds the challenge to the requesting client, exactly like
// TOTPChallenge, so a challenge captured on one network cannot be completed
// from another.
ClientIP string `json:"client_ip"`
// Purpose is one of WebAuthnPurpose*. The finish handler refuses a challenge
// minted for the other ceremony.
Purpose string `json:"purpose"`
// SessionData is the JSON-encoded webauthn.SessionData for this ceremony.
SessionData []byte `json:"session_data"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
Used bool `json:"used"`
}
WebAuthnChallenge is the short-lived, single-use, IP-bound gate that ties the begin and finish steps of a WebAuthn ceremony together, mirroring TOTPChallenge. It stores the library's opaque SessionData (the server-side half of the ceremony: the random challenge, the allowed-credential list, and the required user-verification level) so the client cannot tamper with it between the two calls.
func NewWebAuthnChallenge ¶ added in v0.2.0
func NewWebAuthnChallenge(userID, clientIP, purpose string, sessionData []byte, ttl time.Duration) (WebAuthnChallenge, error)
NewWebAuthnChallenge mints a challenge for a ceremony bound to the requesting client (and, for registration, to a user). The id is a fresh unguessable token returned to the client and echoed back at finish.
type WebAuthnCredential ¶ added in v0.2.0
type WebAuthnCredential struct {
// ID is the opaque store record id (id.New("wacred")). It is the handle the
// management UI uses to rename/delete; it is NOT the WebAuthn credential id.
ID string `json:"id"`
UserID string `json:"user_id"`
// Name is an operator-editable label, defaulted at creation from the
// authenticator/User-Agent so a user with several passkeys can tell them apart.
Name string `json:"name"`
// CredentialID is the raw WebAuthn credential id (the authenticator's handle
// for this key). Unique per credential; used to look a credential up on login.
CredentialID []byte `json:"credential_id"`
// PublicKey is the COSE-encoded public key used to verify assertions.
PublicKey []byte `json:"public_key"`
// AAGUID identifies the authenticator model (all-zero for many platform
// authenticators, including Apple's). Advisory only.
AAGUID []byte `json:"aaguid,omitempty"`
// SignCount is the last observed authenticator signature counter. Synced
// passkeys (e.g. Apple's, in iCloud Keychain) always report 0; a regression
// from a previously non-zero value is a clone signal, logged but not fatal.
SignCount uint32 `json:"sign_count"`
// Transports are the hints the authenticator advertised ("internal", "hybrid",
// "usb", …); passed back to the browser to speed up future prompts.
Transports []string `json:"transports,omitempty"`
// BackupEligible (BE) reports whether the credential can be backed up/synced.
// It is fixed for the life of the credential.
BackupEligible bool `json:"backup_eligible"`
// BackupState (BS) reports whether the credential is currently backed
// up/synced. It can change over time and is refreshed on every login.
BackupState bool `json:"backup_state"`
CreatedAt time.Time `json:"created_at"`
LastUsedAt time.Time `json:"last_used_at,omitempty"`
}
WebAuthnCredential is a single registered passkey belonging to a user. The public key and credential id are, by design, not secret (the whole point of asymmetric WebAuthn is that the private key never leaves the authenticator), so unlike TOTPSecret this record needs no at-rest envelope encryption.