auth

package
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2026 License: Apache-2.0 Imports: 43 Imported by: 0

Documentation

Overview

Package auth signs users in to the admin listener (PHASE1 step 10, security.md "Authentication"): Argon2id passwords, TOTP (RFC 6238), recovery codes, server-side sessions, CSRF protection, throttling and lockout. Permissions are decided by internal/authz.

Secrets at rest: passwords as Argon2id hashes; TOTP secrets encrypted with the auth key (auth.key_file), bound to user ID and purpose; recovery codes as HMAC with a key derived from it; sessions as SHA-256 of the cookie.

Index

Constants

View Source
const (
	CSRFHeader = "X-CSRF-Token"
	CSRFField  = "csrf_token"
)

CSRF token transport: htmx sends the header (set from a <meta> tag), plain forms the hidden field.

View Source
const (
	// DeviceCookieName is the cookie of a known device.
	DeviceCookieName = "__Host-myownpki_device"
	// DeviceCookieTTL: a device stays known this long after its last sign-in.
	DeviceCookieTTL = 365 * 24 * time.Hour
	// DeviceLockAfter failures with one device cookie lock that cookie for
	// DeviceLockFor; it then counts like an unknown browser.
	DeviceLockAfter = 10
	DeviceLockFor   = 15 * time.Minute
)
View Source
const (
	// MaxParallelHashes bounds the memory a login flood can take:
	// 4 x 64 MiB. The throttle refuses before a slot is taken.
	MaxParallelHashes = 4
	// HashWait is how long a request waits for a free slot before "busy".
	HashWait = 2 * time.Second
)
View Source
const (
	MinPasswordRunes = 12
	MaxPasswordRunes = 256
	MaxUsername      = 64
)

Password and user name rules (NIST SP 800-63B, PHASE1 step 10): length instead of composition rules, no expiry, a check against common passwords.

View Source
const (
	LockAfter = 10
	LockFor   = 15 * time.Minute
)

Lockout (the persistent stage after the in-memory throttle): after LockAfter failures in a row the account is locked for LockFor, then the count starts again. Never for good – otherwise anybody could lock the admin out by typing wrong passwords (myownpki user unlock ends it early).

View Source
const (
	// PendingTTL: after the password, the second factor must follow within this time.
	PendingTTL = 5 * time.Minute
	// IdleTimeout ends a session nobody used for this long.
	IdleTimeout = 30 * time.Minute
	// AbsoluteTimeout ends every session, used or not.
	AbsoluteTimeout = 12 * time.Hour
)

Session timing (PHASE1 step 10, owner decision e).

View Source
const (
	TokenPrefix  = "mop_"
	MaxTokens    = 20
	MaxTokenLife = 366 * 24 * time.Hour
)

Token limits.

View Source
const CookieName = "__Host-myownpki_session"

CookieName has the __Host- prefix: the browser only accepts it with Secure, Path=/ and no Domain, so a sibling host can't set or overwrite it.

View Source
const (
	MaxPasskeys = 10
)

Limits.

View Source
const (
	// RecoveryCodeCount codes per user, each usable once.
	RecoveryCodeCount = 10
)
View Source
const (
	TOTPSecretSize = 20 // 160 bits, as RFC 4226 recommends

)

TOTP parameters (RFC 6238): the defaults every authenticator app understands (Aegis, Google, Microsoft, 1Password).

Variables

View Source
var (
	ErrPasskeysOff     = errors.New("passkeys are not set up on this server (auth.passkey_origin)")
	ErrNoPasskey       = errors.New("no passkey is registered for this user")
	ErrNoCeremony      = errors.New("no passkey request is open for this session – start again")
	ErrTooManyPasskeys = fmt.Errorf("at most %d passkeys per user – remove one first", MaxPasskeys)
)

Errors of the passkey ceremonies.

View Source
var (
	ErrInvalidLogin = errors.New("wrong user name or password")
	ErrInvalidCode  = errors.New("wrong or already used code")
	ErrLocked       = errors.New("too many failed attempts")
)

Errors. ErrInvalidLogin is the same for an unknown user, a wrong password and a disabled account: the answer doesn't tell which names exist.

View Source
var DefaultParams = Params{Time: 3, MemoryKiB: 64 * 1024, Threads: 1}

DefaultParams: 64 MiB, 3 passes, 1 lane – above the OWASP minimum (19 MiB, t=2). The unseal passphrase uses 256 MiB, but it is checked rarely; logins are frequent.

View Source
var ErrBadUsername = errors.New("user name: 1 to 64 characters, lower case a-z, digits and . _ @ -, starting with a letter or digit")

ErrBadUsername: user names are refused, never converted.

View Source
var ErrBusy = errors.New("the server is busy – try again in a moment")

ErrBusy: all hash slots are taken – the client should retry shortly.

View Source
var ErrCSRF = errors.New("missing or wrong CSRF token – reload the page")

ErrCSRF: a state-changing request without the session's token.

View Source
var ErrEnrollmentNeedsFile = errors.New("standard output is not an interactive terminal – " +
	"the TOTP secret and recovery codes would end up in a log; use --otpauth-file <path>")

ErrEnrollmentNeedsFile: without an interactive terminal (Docker, systemd, a pipe) the secret would end up in a log, so it may only go to a file.

View Source
var ErrKeyExists = errors.New("the auth key file exists already – it is never overwritten")

ErrKeyExists: an auth key is never overwritten – every TOTP secret would become unreadable.

View Source
var ErrNoSession = errors.New("not signed in or the session has ended – sign in again")

ErrNoSession: no valid session – sign in (again).

View Source
var ErrNoTOTPChange = errors.New("no authenticator change was started")

ErrNoTOTPChange: confirm without a started change.

View Source
var ErrNoToken = errors.New("no such API token")

ErrNoToken: no such token of this user.

View Source
var ErrNoUser = errors.New("no such user")

ErrNoUser: no user with this ID.

View Source
var ErrRefused = errors.New("request refused")

ErrRefused is wrapped by *RefusedError: a request the caller can fix.

View Source
var ErrUnreadableSecret = errors.New("the TOTP secret can't be read with this auth key – wrong auth.key_file? (lost key: myownpki user reset-2fa)")

ErrUnreadableSecret: the TOTP secret doesn't open with this auth key – another key file, or a damaged row.

View Source
var ErrWeakPassword = errors.New("password refused")

ErrWeakPassword is wrapped by every *PolicyError.

Functions

func CSRF

func CSRF(next http.Handler, sessionToken func(*http.Request) (string, bool)) http.Handler

CSRF protects state-changing requests in two layers (security.md):

  1. http.CrossOriginProtection refuses cross-origin requests by the browser's Sec-Fetch-Site / Origin headers – this also covers the login form, which has no session yet;
  2. a request with a session must carry the session's token (header or form field), compared in constant time.

sessionToken returns the expected token of the request's session, or false when the request has none. GET, HEAD and OPTIONS pass unchecked: they must not change state.

func CheckPassword

func CheckPassword(username, password string) error

CheckPassword applies the password rules for username.

func CheckUsername

func CheckUsername(name string) error

CheckUsername applies the user name rule. Upper case is refused, not lowered: the name a user types is the name that is stored.

func ClearSessionCookie

func ClearSessionCookie() *http.Cookie

ClearSessionCookie removes the cookie (logout).

func ClientIP

func ClientIP(r *http.Request, trusted []netip.Prefix) netip.Addr

ClientIP returns the address of the client behind r. Without trusted proxies it is the direct peer. When the peer is a trusted proxy, the X-Forwarded-For list is read from the right: the first address that is not a trusted proxy is the client. Everything left of it was written by the client itself and is never believed.

func CreateKeyFile

func CreateKeyFile(path string, rnd io.Reader) error

CreateKeyFile writes a new random auth key to path (mode 0600). It never overwrites an existing file. rnd nil = crypto/rand.

func DeviceCookie

func DeviceCookie(value string) *http.Cookie

DeviceCookie is the cookie for value (from LoginResult.DeviceCookie).

func SafeMethod

func SafeMethod(m string) bool

SafeMethod: GET, HEAD and OPTIONS must not change state and need no token.

func SameCSRFToken

func SameCSRFToken(got, want string) bool

SameCSRFToken compares a sent token with the session's in constant time; an empty expected token never matches.

func SessionCookie

func SessionCookie(token string) *http.Cookie

SessionCookie is the cookie for token. No Expires: the browser drops it at exit, the server ends it after IdleTimeout / AbsoluteTimeout anyway.

func WriteEnrollmentFile

func WriteEnrollmentFile(path string, e *Enrollment) (err error)

WriteEnrollmentFile writes e to a new file with mode 0600 – for setups without a terminal (Docker demo). An existing file is never overwritten.

Types

type Client

type Client struct {
	IP string
	// Device is the value of the device cookie; "" = none.
	Device string
}

Client is where a sign-in attempt comes from.

type Draft

type Draft struct {

	// Enrollment to show or write; UserID is filled in by CreateUser.
	Enrollment *Enrollment
	// contains filtered or unexported fields
}

Draft is the second factor of a user who doesn't exist yet: TOTP secret and recovery codes made before anything is written. init shows it and takes the first code before it writes (owner decision f), and a setup without a terminal writes its files first – nothing in the database refers to a secret that was never handed out.

func (*Draft) Clear

func (d *Draft) Clear()

Clear removes the secret from memory once it is stored and shown.

func (*Draft) Confirm

func (d *Draft) Confirm(code string, now time.Time) bool

Confirm checks the first code from the authenticator app. On success the user is created with a confirmed authenticator, and this code's time step counts as used.

func (*Draft) Confirmed

func (d *Draft) Confirmed() bool

Confirmed reports whether Confirm succeeded.

type Enrollment

type Enrollment struct {
	UserID   string
	Username string
	// URI is the otpauth:// link (QR code or a file for the Docker demo).
	URI string
	// Secret is the same secret for typing in, in groups of four.
	Secret string
	// RecoveryCodes, when new ones were made (user creation, reset-2fa).
	RecoveryCodes []string
}

Enrollment is what a user needs to set up an authenticator app. Show it once; it is never shown again.

func (*Enrollment) Text

func (e *Enrollment) Text() string

Text is the enrollment for a person: what to do, the link, the secret and the recovery codes.

type Hasher

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

Hasher hashes and checks passwords with a bounded number of parallel runs.

func NewHasher

func NewHasher(p Params, parallel int, wait time.Duration) *Hasher

NewHasher returns a hasher with at most parallel concurrent runs.

func (*Hasher) Hash

func (h *Hasher) Hash(ctx context.Context, password string) (string, error)

Hash returns the PHC string of password with the hasher's parameters.

func (*Hasher) Verify

func (h *Hasher) Verify(ctx context.Context, encoded, password string) (ok, rehash bool, err error)

Verify checks password against encoded. rehash reports that the hash uses other parameters than the current defaults.

func (*Hasher) VerifyDummy

func (h *Hasher) VerifyDummy(ctx context.Context, password string) error

VerifyDummy costs as much as a real check. It runs for unknown user names, so the response time doesn't tell whether a name exists.

type Key

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

Key is the auth key. It lives outside the databases, so a copy of system.db alone holds no usable TOTP secret.

func LoadKey

func LoadKey(path string) (*Key, []string, error)

LoadKey reads the auth key. A file others can read is allowed with a warning (Docker secrets are 0444 by default); a file others can write is refused – whoever can replace the key can lock everybody out.

type LockedError

type LockedError struct{ RetryAfter time.Duration }

LockedError carries how long the caller must wait (HTTP 429 Retry-After).

func (*LockedError) Error

func (e *LockedError) Error() string

func (*LockedError) Unwrap

func (e *LockedError) Unwrap() error

type LoginResult

type LoginResult struct {
	// Token is the new cookie value (SessionCookie). After the password it is
	// a pending session; after the second factor a full one.
	Token   string
	Session *Session
	// NeedsSecondFactor: the password was right, a TOTP code (or a recovery
	// code) must follow.
	NeedsSecondFactor bool
	// FirstTOTP: the authenticator was never confirmed – the setup from init
	// or reset-2fa is completed by this login.
	FirstTOTP bool
	// Factor is the second factor used: "totp" or "recovery_code".
	Factor string
	// RecoveryCodesLeft after a sign-in with a recovery code.
	RecoveryCodesLeft int
	// DeviceCookie is a new device cookie value after a complete sign-in
	// (DeviceCookie); renewed at every one.
	DeviceCookie string
	Warnings     []string
}

LoginResult is the outcome of a sign-in step.

type NewUserRequest

type NewUserRequest struct {
	Username      string
	Password      string
	InstanceAdmin bool
	Roles         []authz.Binding
	// Draft, if set, is the second factor made beforehand (init); nil = a
	// new secret and new recovery codes.
	Draft *Draft
	Who   keys.Who
}

NewUserRequest creates a user with password, TOTP secret and recovery codes. Who needs user.manage (init runs as the console).

type Params

type Params struct {
	Time      uint32
	MemoryKiB uint32
	Threads   uint8
}

Params are the Argon2id parameters of a password hash. They are stored in the hash (PHC string format), so stronger defaults apply to old hashes at the next sign-in.

func TestParams added in v0.2.0

func TestParams() Params

TestParams are the weak Argon2id parameters of tests – the same values as the key ring's test KDF, kept in one place (keys.TestArgon2). It panics outside a test binary.

type Passkey

type Passkey struct {
	ID, Name   string
	CreatedAt  time.Time
	LastUsedAt time.Time // zero = never
}

Passkey is a registered passkey as the owner sees it.

type Passkeys

type Passkeys struct {

	// RPID is the host name passkeys are bound to.
	RPID string
	// contains filtered or unexported fields
}

Passkeys holds the WebAuthn relying party and the open ceremonies.

func NewPasskeys

func NewPasskeys(rpID, origin string) (*Passkeys, error)

NewPasskeys sets up the relying party for rpID and origin (both from config.PasskeyRPID and auth.passkey_origin).

type PolicyError

type PolicyError struct{ Reasons []string }

PolicyError lists every rule a password breaks.

func (*PolicyError) Error

func (e *PolicyError) Error() string

func (*PolicyError) Unwrap

func (e *PolicyError) Unwrap() error

type RefusedError

type RefusedError struct{ Reasons []string }

RefusedError lists every problem of a user management request at once.

func (*RefusedError) Error

func (e *RefusedError) Error() string

func (*RefusedError) Unwrap

func (e *RefusedError) Unwrap() error

type Service

type Service struct {
	System   *sql.DB // writer of system.db
	Audit    *sql.DB // writer of the instance audit log (<data>/audit.db)
	Key      *Key
	Hasher   *Hasher
	Throttle *Throttle
	Clock    clock.Clock
	Tenant   string    // tenant whose step-up setting new proofs snapshot
	Rand     io.Reader // nil = crypto/rand
	// Issuer is the name authenticator apps show; "" = "MyOwnPKI".
	Issuer string
	// Wake, if set, is called after an audit event was written to the outbox.
	Wake func()
	// Passkeys is the WebAuthn relying party; nil = no passkeys (TOTP only).
	Passkeys *Passkeys
}

Service signs users in and manages their sessions and second factors.

func NewService

func NewService(system, auditLog *sql.DB, key *Key, clk clock.Clock) *Service

NewService returns a service with the production hasher and throttle.

func (*Service) Authenticate

func (s *Service) Authenticate(ctx context.Context, token string) (*Session, error)

Authenticate returns the signed-in session of token with its principal. It ends sessions of disabled users and records activity (at most once a minute), which moves the idle timeout.

func (*Service) AuthenticatePassive

func (s *Service) AuthenticatePassive(ctx context.Context, token string) (*Session, error)

AuthenticatePassive is Authenticate without counting as activity: for requests a page makes on its own (status polling), so an open browser tab still times out (owner 2026-10-17).

func (*Service) AuthenticateToken

func (s *Service) AuthenticateToken(ctx context.Context, secret string) (*Session, error)

AuthenticateToken turns a bearer secret into a session-like principal: the token's roles that the owner still has, no instance admin, no step-up. Expired tokens and tokens of disabled users don't work.

func (*Service) BeginPasskeyLogin

func (s *Service) BeginPasskeyLogin(ctx context.Context, token string, c Client) (json.RawMessage, error)

BeginPasskeyLogin starts an assertion: after the password (pending session) it is the second factor, while signed in it is a step-up.

func (*Service) BeginPasskeyRegistration

func (s *Service) BeginPasskeyRegistration(ctx context.Context, token, currentPassword, ip string) (json.RawMessage, error)

BeginPasskeyRegistration starts adding a passkey: it needs a fresh step-up (checked by the caller with authz) and the current password (decision 45). The options go unchanged to navigator.credentials.create().

func (*Service) ChangePassword

func (s *Service) ChangePassword(ctx context.Context, token, current, next, ip string) error

ChangePassword sets a new password; it needs the current one. Every other session of the user ends.

func (*Service) ConfirmTOTP

func (s *Service) ConfirmTOTP(ctx context.Context, userID, code string, who keys.Who) error

ConfirmTOTP confirms the authenticator of a new user right away (init on the console asks for the first code).

func (*Service) ConfirmTOTPChange

func (s *Service) ConfirmTOTPChange(ctx context.Context, token, code, ip string) error

ConfirmTOTPChange switches to the new secret once a code from it works. Every other session of the user ends.

func (*Service) CreateToken

func (s *Service) CreateToken(ctx context.Context, token, name string, roles []authz.Binding, expires time.Time, ip string) (string, *Token, error)

CreateToken makes a token for the signed-in user with roles the user has (never more) and an end within a year. The secret is returned once.

func (*Service) CreateUser

func (s *Service) CreateUser(ctx context.Context, req NewUserRequest) (*Enrollment, error)

CreateUser stores the user and returns the enrollment to show once: the TOTP secret is fixed now, so the first code – at init on the console, or at the first login – only confirms it (no window in which somebody else could set up their own authenticator).

func (*Service) DeletePasskey

func (s *Service) DeletePasskey(ctx context.Context, token, passkeyID, currentPassword, ip string) error

DeletePasskey removes one of the signed-in user's passkeys; it needs the current password (and a fresh step-up, checked by the caller).

func (*Service) DisableUser

func (s *Service) DisableUser(ctx context.Context, userID string, who keys.Who) error

DisableUser disables a user for good (users are never deleted: their ID is in the audit log) and ends their sessions. Nobody disables themselves.

func (*Service) EnableUser

func (s *Service) EnableUser(ctx context.Context, userID string, who keys.Who) error

EnableUser lets a disabled user sign in again.

func (*Service) FinishPasskeyLogin

func (s *Service) FinishPasskeyLogin(ctx context.Context, token string, body []byte, c Client) (*LoginResult, error)

FinishPasskeyLogin checks the assertion. For a pending session it completes the sign-in (a new session ID, like a TOTP code); while signed in it refreshes the step-up time. A wrong assertion counts like a wrong code; a sign counter that goes back is refused as a cloned authenticator.

func (*Service) FinishPasskeyRegistration

func (s *Service) FinishPasskeyRegistration(ctx context.Context, token, name string, body []byte, ip string) (*Passkey, error)

FinishPasskeyRegistration stores the passkey the browser created. The credentials epoch rises (device cookies void) and the user's other sessions end, as for every new authenticator.

func (*Service) HasPasskeys

func (s *Service) HasPasskeys(ctx context.Context, userID string) (bool, error)

HasPasskeys tells whether a sign-in may offer the passkey method.

func (*Service) ListPasskeys

func (s *Service) ListPasskeys(ctx context.Context, token string) ([]Passkey, error)

ListPasskeys lists the passkeys of the signed-in user.

func (*Service) ListTokens

func (s *Service) ListTokens(ctx context.Context, token string) ([]Token, error)

ListTokens lists the signed-in user's tokens.

func (*Service) ListUsers

func (s *Service) ListUsers(ctx context.Context, who keys.Who) ([]UserInfo, error)

ListUsers lists every user.

func (*Service) Login

func (s *Service) Login(ctx context.Context, username, password, ip string) (*LoginResult, error)

Login checks user name and password and opens a pending session.

func (*Service) LoginFrom

func (s *Service) LoginFrom(ctx context.Context, username, password string, c Client) (*LoginResult, error)

LoginFrom is Login with the client's device cookie: a known device is judged by its own counter instead of the user lockout.

func (*Service) LoginRecoveryCode

func (s *Service) LoginRecoveryCode(ctx context.Context, pendingToken, code, ip string) (*LoginResult, error)

LoginRecoveryCode completes a sign-in with a recovery code (lost phone). Each code works once.

func (*Service) LoginTOTP

func (s *Service) LoginTOTP(ctx context.Context, pendingToken, code, ip string) (*LoginResult, error)

LoginTOTP completes a sign-in with a TOTP code. The pending session is replaced by a new full one: a new session ID after every privilege change.

func (*Service) Logout

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

Logout ends the session of token.

func (*Service) NewDraft

func (s *Service) NewDraft(username string) (*Draft, error)

NewDraft makes a TOTP secret and recovery codes for username.

func (*Service) NewRecoveryCodes

func (s *Service) NewRecoveryCodes(ctx context.Context, token, currentPassword, ip string) ([]string, error)

NewRecoveryCodes replaces all recovery codes of the signed-in user; it needs the current password. Show the codes once.

func (*Service) PruneSessions

func (s *Service) PruneSessions(ctx context.Context) (int64, error)

PruneSessions deletes ended sessions; the server runs it periodically.

func (*Service) RemoveRole

func (s *Service) RemoveRole(ctx context.Context, userID, tenant, ca string, who keys.Who) error

RemoveRole takes a role away; the user's sessions end.

func (*Service) ResetTOTP

func (s *Service) ResetTOTP(ctx context.Context, username string, who keys.Who) (*Enrollment, error)

ResetTOTP gives a user a new TOTP secret and new recovery codes and ends their sessions. The user confirms the secret at the next login – only this secret works, so there is no window for anybody else. Used by admins and by myownpki user reset-2fa (lost phone, lost auth key).

func (*Service) RevokeToken

func (s *Service) RevokeToken(ctx context.Context, token, id, ip string) error

RevokeToken deletes one of the signed-in user's tokens; it stops working at once.

func (*Service) SecondFactor

func (s *Service) SecondFactor(ctx context.Context, pendingToken, code string, c Client) (*LoginResult, error)

SecondFactor completes a sign-in with what the user typed: six digits are a TOTP code, anything else is tried as a recovery code. Failures of both count in the same throttle and lockout as wrong passwords.

func (*Service) SetRole

func (s *Service) SetRole(ctx context.Context, userID string, b authz.Binding, who keys.Who) error

SetRole gives a user a role in a tenant (optionally one CA). The user's sessions end: new rights only with a new session (security.md).

func (*Service) SetRoles

func (s *Service) SetRoles(ctx context.Context, userID, tenant string, want []authz.Binding, who keys.Who) error

SetRoles replaces the roles of a user in one tenant: missing ones are added, others removed. The user's sessions end (by SetRole/RemoveRole).

func (*Service) StartTOTPChange

func (s *Service) StartTOTPChange(ctx context.Context, token, currentPassword, ip string) (*Enrollment, error)

StartTOTPChange creates a new authenticator secret for the signed-in user; it needs the current password. The old secret stays valid until ConfirmTOTPChange.

func (*Service) StepUp

func (s *Service) StepUp(ctx context.Context, token, code, ip string) (*Session, error)

StepUp confirms a sensitive action with a fresh TOTP code (security.md): the session snapshots the proof time and the tenant's current lifetime.

func (*Service) StepUpFrom

func (s *Service) StepUpFrom(ctx context.Context, token, code string, c Client) (*Session, error)

StepUpFrom is StepUp with the client's device cookie.

func (*Service) Unlock

func (s *Service) Unlock(ctx context.Context, username string, who keys.Who) error

Unlock ends a lockout early (myownpki user unlock on the console).

func (*Service) UnlockByID

func (s *Service) UnlockByID(ctx context.Context, userID string, who keys.Who) error

UnlockByID ends a lockout early, like `myownpki user unlock`.

func (*Service) UserByID

func (s *Service) UserByID(ctx context.Context, who keys.Who, id string) (*UserInfo, error)

UserByID returns one user.

type Session

type Session struct {
	UserID         string
	Username       string
	CSRFToken      string
	CreatedAt      time.Time
	ExpiresAt      time.Time // absolute end (a pending session: PendingTTL)
	LastSeen       time.Time // last activity; the idle timeout counts from here
	StepUpAt       time.Time
	StepUpDuration time.Duration
	// Principal is what the session may do; nil for a pending session.
	Principal *authz.Principal
	// TokenID and TokenName are set when an API token signed the request in
	// (no cookie, no CSRF token, never a step-up).
	TokenID, TokenName string
	// contains filtered or unexported fields
}

Session is a signed-in user (or one between password and second factor).

func (*Session) Binding added in v0.3.1

func (s *Session) Binding() SessionBinding

Binding returns the immutable binding of this authenticated session.

func (*Session) IdleExpiresAt

func (s *Session) IdleExpiresAt() time.Time

IdleExpiresAt is when the session ends without further activity.

type SessionBinding added in v0.3.1

type SessionBinding struct {
	SessionIDHash    string
	CredentialsEpoch int64
}

SessionBinding identifies the server-side session and the user's credentials generation without exposing either as mutable Session fields.

type Throttle

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

Throttle slows down guessing of passwords, TOTP and recovery codes. It lives in memory and is checked before any hash is computed. The lockout in the database (users.locked_until) is the second, persistent stage.

func NewThrottle

func NewThrottle() *Throttle

NewThrottle returns an empty throttle.

func (*Throttle) DeviceLocked

func (t *Throttle) DeviceLocked(nonce string, now time.Time) bool

DeviceLocked reports whether the device cookie with this random value had too many failures: it then counts like an unknown browser.

func (*Throttle) Failure

func (t *Throttle) Failure(ip, user string, known bool, now time.Time)

Failure counts a failed attempt.

func (*Throttle) FailureDevice

func (t *Throttle) FailureDevice(ip, user, nonce string, now time.Time)

FailureDevice counts a failed attempt with a device cookie: against (IP, user) and against the cookie, not against the user.

func (*Throttle) Forget

func (t *Throttle) Forget(user string)

Forget clears every counter of user (myownpki user unlock).

func (*Throttle) Success

func (t *Throttle) Success(ip, user string)

Success clears the strict counter of (ip, user). The per-user counter stays: a successful login of the real user must not reset what an attacker elsewhere has built up.

func (*Throttle) Wait

func (t *Throttle) Wait(ip, user string, known bool, now time.Time) time.Duration

Wait returns how long the attempt must wait; 0 = go ahead. known: the user name exists.

func (*Throttle) WaitDevice

func (t *Throttle) WaitDevice(ip, user string, now time.Time) time.Duration

WaitDevice is Wait for an attempt with a valid device cookie: only the strict (IP, user) counter applies, not the per-user one strangers build up.

type Token

type Token struct {
	ID, Name             string
	Roles                []authz.Binding
	CreatedAt, ExpiresAt time.Time
	LastUsedAt           time.Time // zero = never
}

Token is an API token as its owner sees it (never the secret).

type UserInfo

type UserInfo struct {
	ID, Username  string
	InstanceAdmin bool
	Disabled      bool
	LockedUntil   time.Time // zero = not locked
	TOTPConfirmed bool
	Passkeys      int
	Roles         []authz.Binding
	CreatedAt     time.Time
}

UserInfo is a user as an instance admin sees it (step 18): no secrets.

Directories

Path Synopsis
Package passkeytest is a virtual WebAuthn authenticator for tests (PHASE1 step 17): it answers the options of navigator.credentials.create() and .get() the way a browser with a platform authenticator does – ECDSA P-256, attestation "none", user verification done.
Package passkeytest is a virtual WebAuthn authenticator for tests (PHASE1 step 17): it answers the options of navigator.credentials.create() and .get() the way a browser with a platform authenticator does – ECDSA P-256, attestation "none", user verification done.

Jump to

Keyboard shortcuts

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