passkeys

package
v0.6.2 Latest Latest
Warning

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

Go to latest
Published: Sep 2, 2026 License: AGPL-3.0 Imports: 17 Imported by: 0

Documentation

Overview

Package passkeys owns the per-user WebAuthn surface: registering, listing, renaming, and deleting the caller's own passkeys, plus setting and removing the caller's password. It also drives the public login ceremony (begin / finish), which has no principal yet.

Ceremony challenge state lives in the webauthn_ceremonies table (short-lived, single-use) so a multi-replica deployment can finish a ceremony on a different instance than began it. Every credential a user owns is one row in webauthn_credentials; the raw public key never leaves the backend.

Index

Constants

This section is empty.

Variables

View Source
var ErrNeedsReauth = errors.New("passkey assertion failed")

ErrNeedsReauth is returned by the login finish path when the assertion is invalid — the handler maps it to 401.

Functions

This section is empty.

Types

type LoginResult

type LoginResult struct {
	UserID             uuid.UUID
	Email              string
	DisplayName        string
	TenantRole         string
	MustChangePassword bool
}

LoginResult carries the identity a successful assertion resolved to, enough for the handler to mint tokens without re-reading the row.

type Passkey

type Passkey struct {
	ID             uuid.UUID
	FriendlyName   string
	CreatedAt      pgtype.Timestamptz
	LastUsedAt     pgtype.Timestamptz
	BackupEligible bool
}

Passkey is the wire shape the handler sees — display metadata only, never the public key or raw credential id.

type Service

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

Service is the per-user passkey + password layer. Authenticated methods gate on TenantSelfPasskeyManage (any user manages their own); the login ceremony is public.

func New

func New(d *db.DB, w *webauthn.WebAuthn, logger *zap.Logger) *Service

func (*Service) Delete

func (s *Service) Delete(ctx context.Context, p authz.Principal, id uuid.UUID) error

Delete removes one of the caller's passkeys, refusing to remove the last sign-in credential (a user with no passkey and no password could never log in again).

func (*Service) GC

func (s *Service) GC(ctx context.Context) error

GC deletes expired ceremony rows. Safe to call concurrently from any replica.

func (*Service) List

func (s *Service) List(ctx context.Context, p authz.Principal) ([]Passkey, error)

List returns the caller's passkeys, oldest first.

func (*Service) LoginBegin

func (s *Service) LoginBegin(ctx context.Context, _ string) (ceremonyID string, options *protocol.CredentialAssertion, err error)

LoginBegin starts a usernameless ceremony. Login options are always discoverable so neither the response nor the authenticator credential list reveals whether a submitted email exists or has passkeys.

func (*Service) LoginFinish

func (s *Service) LoginFinish(ctx context.Context, ceremonyID string, r *http.Request) (LoginResult, error)

LoginFinish completes a login ceremony and returns the resolved identity. The assertion is read from r.Body by go-webauthn. On success the matched credential's sign counter is advanced.

func (*Service) RegisterBegin

func (s *Service) RegisterBegin(ctx context.Context, p authz.Principal) (ceremonyID string, options *protocol.CredentialCreation, err error)

RegisterBegin starts a registration ceremony for the caller. The returned options are handed to the browser; ceremonyID must be echoed to RegisterFinish.

func (*Service) RegisterFinish

func (s *Service) RegisterFinish(ctx context.Context, p authz.Principal, ceremonyID, friendlyName string, r *http.Request) (Passkey, error)

RegisterFinish completes a registration ceremony. The attestation is read straight from r.Body by go-webauthn; ceremonyID and friendlyName come from the query string. Registering a passkey also clears must_change_password — it satisfies the "secure your account" requirement.

func (*Service) RemovePassword

func (s *Service) RemovePassword(ctx context.Context, p authz.Principal) error

RemovePassword clears the caller's password, refusing if it would leave no sign-in credential (no passkey registered).

func (*Service) Rename

func (s *Service) Rename(ctx context.Context, p authz.Principal, id uuid.UUID, name string) error

Rename relabels one of the caller's passkeys. Owner-scoped at the query level.

func (*Service) SetPassword

func (s *Service) SetPassword(ctx context.Context, p authz.Principal, password string) error

SetPassword sets or replaces the caller's password (strength-checked) and clears must_change_password — securing the account.

Jump to

Keyboard shortcuts

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