passkey

package
v2.8.1 Latest Latest
Warning

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

Go to latest
Published: Aug 19, 2026 License: BSD-3-Clause Imports: 34 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrPasskeyServiceUnavailable = errors.Sentinel("passkey service is unavailable")
	ErrPasskeyCeremony           = errors.Sentinel("invalid or expired passkey ceremony")
	ErrPasskeyTransaction        = errors.Sentinel("invalid or expired authentication transaction")
	ErrPasskeyResponse           = errors.Sentinel("invalid passkey response")
	ErrPasskeyNotFound           = errors.Sentinel("passkey not found")
	ErrPasskeyExists             = errors.Sentinel("passkey already registered")
	ErrPasskeyStepUpRequired     = errors.Sentinel("fresh step-up authentication is required")
	ErrPasskeyMFAEnabled         = errors.Sentinel("passkey MFA must be disabled first")
	ErrPasskeyMFAAlreadyEnabled  = errors.Sentinel("passkey MFA is already enabled")
	ErrPasskeyMFANotEnabled      = errors.Sentinel("passkey MFA is not enabled")
	ErrPasskeyNoCredential       = errors.Sentinel("no passkeys are registered")
	ErrPasskeyLastCredential     = errors.Sentinel("cannot remove the last usable authentication method")
	ErrPasskeyRecoveryCode       = errors.Sentinel("invalid recovery code")
	ErrPasskeyName               = errors.Sentinel("invalid passkey name")
)

Functions

func RegisterPasskeys

func RegisterPasskeys(api huma.API, passkeyService *PasskeyService, authService *auth.AuthService, userService *user.UserService)

Types

type AuthTransaction added in v2.8.1

type AuthTransaction struct {
	database.BaseModel

	Kind        string     `json:"kind" gorm:"column:kind;not null;index"`
	UserID      string     `json:"userId" gorm:"column:user_id;not null;index"`
	SessionID   *string    `json:"sessionId,omitempty" gorm:"column:session_id;index"`
	Source      string     `json:"source" gorm:"column:source;not null"`
	UserAgent   *string    `json:"-" gorm:"column:user_agent"`
	IPAddress   *string    `json:"-" gorm:"column:ip_address"`
	SecretHash  *string    `json:"-" gorm:"column:secret_hash;index"`
	Status      string     `json:"status" gorm:"column:status;not null;index"`
	ExpiresAt   time.Time  `json:"expiresAt" gorm:"column:expires_at;not null;index"`
	CompletedAt *time.Time `json:"completedAt,omitempty" gorm:"column:completed_at"`
}

AuthTransaction links a primary authentication result to its pending passkey MFA or step-up ceremony. The client receives only ID/secret values; primary credentials and WebAuthn SessionData remain server-side.

func (AuthTransaction) TableName added in v2.8.1

func (AuthTransaction) TableName() string

type AuthenticationCompletion

type AuthenticationCompletion struct {
	User   *common.User
	Meta   auth.SessionMeta
	Source string
}

AuthenticationCompletion carries the original primary-auth metadata across an MFA transaction so the eventual session cannot be assigned client-chosen source or network metadata.

type BeginPasskeyRegistrationInput

type BeginPasskeyRegistrationInput struct {
	StepUpToken string `header:"X-Step-Up-Token"`
}

type BeginPasskeyRegistrationOutput

type BeginPasskeyRegistrationOutput struct {
	Body base.ApiResponse[passkeyBeginResponse]
}

type BeginStepUpInput

type BeginStepUpInput struct{}

type BeginStepUpOutput

type BeginStepUpOutput struct {
	Body base.ApiResponse[passkeyBeginResponse]
}

type DeletePasskeyInput

type DeletePasskeyInput struct {
	ID          string `path:"id"`
	StepUpToken string `header:"X-Step-Up-Token"`
}

type DeletePasskeyOutput

type DeletePasskeyOutput struct {
	Body base.ApiResponse[base.MessageResponse]
}

type FinishPasskeyRegistrationInput

type FinishPasskeyRegistrationInput struct {
	UserAgent string `header:"User-Agent"`
	Body      passkeyCredentialBody
}

type FinishPasskeyRegistrationOutput

type FinishPasskeyRegistrationOutput struct {
	Body base.ApiResponse[PasskeySummary]
}

type FinishStepUpInput

type FinishStepUpInput struct {
	Body stepUpFinishBody
}

type FinishStepUpOutput

type FinishStepUpOutput struct {
	Body base.ApiResponse[StepUpGrant]
}

type GetMFAStatusOutput

type GetMFAStatusOutput struct {
	Body base.ApiResponse[MFAStatus]
}

type GetPasskeyCapabilitiesOutput

type GetPasskeyCapabilitiesOutput struct {
	Body base.ApiResponse[PasskeyCapabilities]
}

type ListMyPasskeysOutput

type ListMyPasskeysOutput struct {
	Body base.ApiResponse[[]PasskeySummary]
}

type MFARecoveryCodesOutput

type MFARecoveryCodesOutput struct {
	Body base.ApiResponse[RecoveryCodesResponse]
}

type MFASettingsInput

type MFASettingsInput struct {
	StepUpToken string `header:"X-Step-Up-Token"`
}

type MFAStatus

type MFAStatus struct {
	Enabled                bool `json:"enabled"`
	PasskeyCount           int  `json:"passkeyCount"`
	RecoveryCodesRemaining int  `json:"recoveryCodesRemaining"`
}

MFAStatus is the account's passkey MFA state. Recovery codes are counted, but their plaintext values are never returned after generation.

type MobilePasskeyExchangeInput

type MobilePasskeyExchangeInput struct {
	UserAgent string `header:"User-Agent"`
	Body      mobilePasskeyExchangeBody
}

type MobilePasskeyExchangeOutput

type MobilePasskeyExchangeOutput struct {
	SetCookie []string `header:"Set-Cookie" doc:"Session cookie"`
	Body      base.ApiResponse[authtypes.AuthenticationResponse]
}

type MobilePasskeyFinishInput

type MobilePasskeyFinishInput struct {
	Body mobilePasskeyFinishBody
}

type MobilePasskeyFinishOutput

type MobilePasskeyFinishOutput struct {
	Body base.ApiResponse[authtypes.MobilePasskeyCompletion]
}

type Passkey added in v2.8.1

type Passkey struct {
	database.BaseModel

	UserID string       `json:"userId" gorm:"column:user_id;not null;index"`
	User   *common.User `json:"-" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`

	RPID                    string               `json:"rpId" gorm:"column:rp_id;not null"`
	CredentialID            []byte               `json:"-" gorm:"column:credential_id;not null"`
	PublicKey               []byte               `json:"-" gorm:"column:public_key;not null"`
	AttestationType         string               `json:"attestationType,omitempty" gorm:"column:attestation_type"`
	AttestationFormat       string               `json:"attestationFormat,omitempty" gorm:"column:attestation_format"`
	Transports              database.StringSlice `json:"transports,omitempty" gorm:"column:transports;type:text"`
	AAGUID                  []byte               `json:"-" gorm:"column:aaguid"`
	SignCount               uint32               `json:"signCount" gorm:"column:sign_count;not null;default:0"`
	BackupEligible          bool                 `json:"backupEligible" gorm:"column:backup_eligible;not null;default:false"`
	BackupState             bool                 `json:"backupState" gorm:"column:backup_state;not null;default:false"`
	CloneWarning            bool                 `json:"cloneWarning" gorm:"column:clone_warning;not null;default:false"`
	AuthenticatorAttachment string               `json:"authenticatorAttachment,omitempty" gorm:"column:authenticator_attachment"`

	AttestationClientDataJSON     []byte `json:"-" gorm:"column:attestation_client_data_json"`
	AttestationClientDataHash     []byte `json:"-" gorm:"column:attestation_client_data_hash"`
	AttestationAuthenticatorData  []byte `json:"-" gorm:"column:attestation_authenticator_data"`
	AttestationPublicKeyAlgorithm int64  `json:"-" gorm:"column:attestation_public_key_algorithm"`
	AttestationObject             []byte `json:"-" gorm:"column:attestation_object"`

	Name       string     `json:"name" gorm:"column:name;not null"`
	LastUsedAt *time.Time `json:"lastUsedAt,omitempty" gorm:"column:last_used_at"`
}

Passkey stores the WebAuthn credential record required to validate future assertions. The raw attestation fields are retained so the credential can be audited or revalidated if the WebAuthn library's verification policy evolves.

func (Passkey) TableName added in v2.8.1

func (Passkey) TableName() string

type PasskeyCapabilities

type PasskeyCapabilities struct {
	PasskeyMFAEnabled          bool `json:"passkeyMfaEnabled"`
	PasskeyCount               int  `json:"passkeyCount"`
	HasLocalPassword           bool `json:"hasLocalPassword"`
	HasOIDCFallback            bool `json:"hasOidcFallback"`
	CanEnrollWithActiveSession bool `json:"canEnrollWithActiveSession"`
	CanDeleteLastPasskey       bool `json:"canDeleteLastPasskey"`
	RequiresStepUp             bool `json:"requiresStepUp"`
}

PasskeyCapabilities describes the server-derived authentication choices for the current account. It deliberately does not trust client-supplied flags.

type PasskeyCeremony added in v2.8.1

type PasskeyCeremony struct {
	database.BaseModel

	Purpose           string     `json:"purpose" gorm:"column:purpose;not null;index"`
	UserID            *string    `json:"userId,omitempty" gorm:"column:user_id;index"`
	SessionID         *string    `json:"sessionId,omitempty" gorm:"column:session_id;index"`
	AuthTransactionID *string    `json:"authTransactionId,omitempty" gorm:"column:auth_transaction_id;index"`
	RPID              string     `json:"rpId" gorm:"column:rp_id;not null"`
	SessionData       string     `json:"-" gorm:"column:session_data;type:text;not null"`
	ExpiresAt         time.Time  `json:"expiresAt" gorm:"column:expires_at;not null;index"`
	ConsumedAt        *time.Time `json:"-" gorm:"column:consumed_at;index"`
}

PasskeyCeremony holds the opaque server-side state between WebAuthn begin and finish calls. It is intentionally never serialized to the client.

func (PasskeyCeremony) TableName added in v2.8.1

func (PasskeyCeremony) TableName() string

type PasskeyChallenge

type PasskeyChallenge struct {
	CeremonyID    string
	TransactionID string
	Options       any
	ExpiresAt     time.Time
}

PasskeyChallenge is the public result of a WebAuthn ceremony begin. The matching SessionData remains in passkey_ceremonies.

type PasskeyHandler

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

func (*PasskeyHandler) BeginMFA

func (*PasskeyHandler) BeginPasskeyLogin

func (h *PasskeyHandler) BeginPasskeyLogin(ctx context.Context, _ *struct{}) (*PasskeyLoginBeginOutput, error)

func (*PasskeyHandler) BeginRegistration

func (*PasskeyHandler) BeginStepUp

func (*PasskeyHandler) DeletePasskey

func (h *PasskeyHandler) DeletePasskey(ctx context.Context, input *DeletePasskeyInput) (*DeletePasskeyOutput, error)

func (*PasskeyHandler) DisableMFA

func (*PasskeyHandler) EnableMFA

func (*PasskeyHandler) ExchangeMobilePasskeyLogin

func (h *PasskeyHandler) ExchangeMobilePasskeyLogin(ctx context.Context, input *MobilePasskeyExchangeInput) (*MobilePasskeyExchangeOutput, error)

func (*PasskeyHandler) FinishMFA

func (*PasskeyHandler) FinishMobilePasskeyLogin

func (h *PasskeyHandler) FinishMobilePasskeyLogin(ctx context.Context, input *MobilePasskeyFinishInput) (*MobilePasskeyFinishOutput, error)

func (*PasskeyHandler) FinishPasskeyLogin

func (*PasskeyHandler) FinishRegistration

func (*PasskeyHandler) FinishStepUp

func (h *PasskeyHandler) FinishStepUp(ctx context.Context, input *FinishStepUpInput) (*FinishStepUpOutput, error)

func (*PasskeyHandler) GetCapabilities

func (h *PasskeyHandler) GetCapabilities(ctx context.Context, _ *struct{}) (*GetPasskeyCapabilitiesOutput, error)

func (*PasskeyHandler) GetMFAStatus

func (h *PasskeyHandler) GetMFAStatus(ctx context.Context, _ *struct{}) (*GetMFAStatusOutput, error)

func (*PasskeyHandler) ListMyPasskeys

func (h *PasskeyHandler) ListMyPasskeys(ctx context.Context, _ *struct{}) (*ListMyPasskeysOutput, error)

func (*PasskeyHandler) PasswordStepUp

func (h *PasskeyHandler) PasswordStepUp(ctx context.Context, input *PasswordStepUpInput) (*PasswordStepUpOutput, error)

func (*PasskeyHandler) RegenerateRecoveryCodes

func (h *PasskeyHandler) RegenerateRecoveryCodes(ctx context.Context, input *MFASettingsInput) (*MFARecoveryCodesOutput, error)

func (*PasskeyHandler) RenamePasskey

func (h *PasskeyHandler) RenamePasskey(ctx context.Context, input *RenamePasskeyInput) (*RenamePasskeyOutput, error)

func (*PasskeyHandler) UseRecoveryCode

func (h *PasskeyHandler) UseRecoveryCode(ctx context.Context, input *PasskeyRecoveryInput) (*PasskeyRecoveryOutput, error)

type PasskeyLoginBeginOutput

type PasskeyLoginBeginOutput struct {
	Body base.ApiResponse[passkeyBeginResponse]
}

type PasskeyLoginFinishInput

type PasskeyLoginFinishInput struct {
	UserAgent string `header:"User-Agent"`
	Body      passkeyCredentialBody
}

type PasskeyLoginFinishOutput

type PasskeyLoginFinishOutput struct {
	SetCookie []string `header:"Set-Cookie" doc:"Session cookie"`
	Body      base.ApiResponse[authtypes.AuthenticationResponse]
}

type PasskeyMFAFinishInput

type PasskeyMFAFinishInput struct {
	Body mfaFinishBody
}

type PasskeyMFAFinishOutput

type PasskeyMFAFinishOutput struct {
	SetCookie []string `header:"Set-Cookie" doc:"Session cookie"`
	Body      base.ApiResponse[authtypes.AuthenticationResponse]
}

type PasskeyMFAStartInput

type PasskeyMFAStartInput struct {
	Body mfaBeginBody
}

type PasskeyMFAStartOutput

type PasskeyMFAStartOutput struct {
	Body base.ApiResponse[authtypes.MFAChallenge]
}

type PasskeyRecoveryCode added in v2.8.1

type PasskeyRecoveryCode struct {
	database.BaseModel

	UserID   string     `json:"userId" gorm:"column:user_id;not null;index"`
	CodeHash string     `json:"-" gorm:"column:code_hash;not null;uniqueIndex"`
	UsedAt   *time.Time `json:"usedAt,omitempty" gorm:"column:used_at;index"`
}

PasskeyRecoveryCode stores only a one-way digest of a recovery code. The plaintext is returned once during generation and is never recoverable.

func (PasskeyRecoveryCode) TableName added in v2.8.1

func (PasskeyRecoveryCode) TableName() string

type PasskeyRecoveryInput

type PasskeyRecoveryInput struct {
	Body recoveryBody
}

type PasskeyRecoveryOutput

type PasskeyRecoveryOutput struct {
	SetCookie []string `header:"Set-Cookie" doc:"Session cookie"`
	Body      base.ApiResponse[authtypes.AuthenticationResponse]
}

type PasskeyService

type PasskeyService = passkeyService

PasskeyService owns WebAuthn ceremonies, passkey persistence, MFA transactions, recovery codes, and step-up grants. It intentionally does not issue JWTs; auth.AuthService remains the single token/session issuer.

func NewPasskeyService

func NewPasskeyService(db *database.DB, cfg *config.Config) *PasskeyService

type PasskeySummary

type PasskeySummary struct {
	ID                      string     `json:"id"`
	Name                    string     `json:"name"`
	RPID                    string     `json:"rpId"`
	AAGUID                  string     `json:"aaguid,omitempty"`
	Transports              []string   `json:"transports,omitempty"`
	BackupEligible          bool       `json:"backupEligible"`
	BackupState             bool       `json:"backupState"`
	CloneWarning            bool       `json:"cloneWarning"`
	AuthenticatorAttachment string     `json:"authenticatorAttachment,omitempty"`
	CreatedAt               time.Time  `json:"createdAt"`
	UpdatedAt               *time.Time `json:"updatedAt,omitempty"`
	LastUsedAt              *time.Time `json:"lastUsedAt,omitempty"`
}

PasskeySummary is the safe representation of a stored credential.

type PasswordStepUpInput

type PasswordStepUpInput struct {
	Body passwordReauthBody
}

type PasswordStepUpOutput

type PasswordStepUpOutput struct {
	Body base.ApiResponse[StepUpGrant]
}

type RecoveryCodesResponse

type RecoveryCodesResponse struct {
	Codes []string `json:"codes" doc:"Recovery codes; shown only once"`
}

type RenamePasskeyInput

type RenamePasskeyInput struct {
	ID          string `path:"id"`
	StepUpToken string `header:"X-Step-Up-Token"`
	Body        renamePasskeyBody
}

type RenamePasskeyOutput

type RenamePasskeyOutput struct {
	Body base.ApiResponse[PasskeySummary]
}

type StepUpGrant

type StepUpGrant struct {
	Token     string    `json:"token"`
	ExpiresAt time.Time `json:"expiresAt"`
}

StepUpGrant is returned after a fresh password or passkey assertion. The token is hashed in the database and stays usable, for the issuing session only, until ExpiresAt.

Jump to

Keyboard shortcuts

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