Documentation
¶
Index ¶
- Constants
- Variables
- func GetAuthenticatorName(aaguid *string) string
- type AfterAuthenticationHook
- type AfterRegistrationHook
- type AuthenticationFailedPayload
- type AuthenticationOptionsCreatedPayload
- type AuthenticationOptionsResult
- type AuthenticationVerifiedPayload
- type CeremonyType
- type Config
- type DeletePasskeyParams
- type GenerateAuthenticationOptionsParams
- type GenerateRegistrationOptionsParams
- type ListPasskeysParams
- type Option
- func WithAfterAuthentication(hook AfterAuthenticationHook) Option
- func WithAfterRegistration(hook AfterRegistrationHook) Option
- func WithAttestation(att protocol.ConveyancePreference) Option
- func WithAuthenticatorAttachment(attachment protocol.AuthenticatorAttachment) Option
- func WithChallengeTimeout(d time.Duration) Option
- func WithRPDisplayName(name string) Option
- func WithRPID(rpID string) Option
- func WithRPOrigins(origins ...string) Option
- func WithRequireSessionOnRegistration(require bool) Option
- func WithResidentKey(rk protocol.ResidentKeyRequirement) Option
- func WithResolveUser(fn ResolveUserFunc) Option
- func WithSessionDuration(d time.Duration) Option
- func WithUserVerification(uv protocol.UserVerificationRequirement) Option
- type PasskeyChallenge
- type PasskeyDeletedPayload
- type PasskeyRegistrationUser
- type PasskeyUpdatedPayload
- type Plugin
- func (p *Plugin) Config() Config
- func (p *Plugin) DeletePasskey(ctx context.Context, params *DeletePasskeyParams) error
- func (p *Plugin) GenerateAuthenticationOptions(ctx context.Context, params *GenerateAuthenticationOptionsParams) (*AuthenticationOptionsResult, error)
- func (p *Plugin) GenerateRegistrationOptions(ctx context.Context, params *GenerateRegistrationOptionsParams) (*RegistrationOptionsResult, error)
- func (p *Plugin) GetPasskey(ctx context.Context, id string) (*entity.Passkey, error)
- func (p *Plugin) ID() string
- func (p *Plugin) Init(ctx *plugin.Context) error
- func (p *Plugin) ListPasskeys(ctx context.Context, params *ListPasskeysParams) ([]*entity.Passkey, error)
- func (p *Plugin) ServeAuthenticationOptions(w http.ResponseWriter, r *http.Request)
- func (p *Plugin) ServeRegistrationOptions(w http.ResponseWriter, r *http.Request)
- func (p *Plugin) ServeVerifyAuthentication(w http.ResponseWriter, r *http.Request)
- func (p *Plugin) ServeVerifyRegistration(w http.ResponseWriter, r *http.Request)
- func (p *Plugin) UpdatePasskey(ctx context.Context, params *UpdatePasskeyParams) (*entity.Passkey, error)
- func (p *Plugin) VerifyAuthentication(ctx context.Context, params *VerifyAuthenticationParams) (*VerifyAuthenticationResult, error)
- func (p *Plugin) VerifyRegistration(ctx context.Context, params *VerifyRegistrationParams) (*entity.Passkey, error)
- type RegistrationFailedPayload
- type RegistrationOptionsCreatedPayload
- type RegistrationOptionsResult
- type RegistrationVerifiedPayload
- type Repository
- type ResolveUserFunc
- type UpdatePasskeyParams
- type VerifyAuthenticationParams
- type VerifyAuthenticationResult
- type VerifyRegistrationParams
Constants ¶
const ( EventRegistrationOptionsCreated = "passkey:registration_options:created" EventRegistrationVerified = "passkey:registration:verified" EventRegistrationFailed = "passkey:registration:failed" EventAuthenticationOptionsCreated = "passkey:authentication_options:created" EventAuthenticationVerified = "passkey:authentication:verified" EventAuthenticationFailed = "passkey:authentication:failed" EventPasskeyUpdated = "passkey:updated" EventPasskeyDeleted = "passkey:deleted" )
Event topic names for Passkey lifecycle events published to the EventBus.
const ( ExtraKeyOrigin = "origin" ExtraKeyAAGUID = "aaguid" ExtraKeyDeviceType = "deviceType" ExtraKeyTransports = "transports" ExtraKeyChallengeToken = "challengeToken" ExtraKeyUserAgent = "userAgent" ExtraKeyIPAddress = "ipAddress" )
Metadata keys for extra context passed in parameters or event payloads.
const ( StoreKeyRPID = "passkey:rp_id" StoreKeyRPOrigins = "passkey:rp_origins" StoreKeyRPName = "passkey:rp_name" StoreKeyActiveCount = "passkey:active_count" )
Shared context storage keys for plugin.Context.
const AnonymousAAGUID = "00000000-0000-0000-0000-000000000000"
AnonymousAAGUID represents the zero/anonymous AAGUID indicating an unauthenticated or privacy-preserving model.
Variables ¶
var ( // ErrPasskeyNotFound is returned when no registered passkey credential matches the query. ErrPasskeyNotFound = errors.New("passkey: passkey not found") // ErrPasskeyAlreadyExists is returned when attempting to register a credential that is already bound to an account. ErrPasskeyAlreadyExists = errors.New("passkey: passkey credential already registered") // ErrChallengeNotFound is returned when a WebAuthn ceremony challenge token is missing or invalid. ErrChallengeNotFound = errors.New("passkey: challenge not found or expired") // ErrChallengeExpired is returned when a submitted WebAuthn challenge has passed its expiration time. ErrChallengeExpired = errors.New("passkey: challenge has expired") // ErrInvalidCeremonyType is returned when a challenge issued for registration is used for authentication or vice-versa. ErrInvalidCeremonyType = errors.New("passkey: invalid ceremony type for this challenge") ErrUnauthorized = errors.New("passkey: unauthorized to perform this operation") // ErrVerificationFailed is returned when WebAuthn cryptographic assertion/attestation verification fails. ErrVerificationFailed = errors.New("passkey: failed to verify webauthn ceremony response") // ErrCounterNotIncremented is returned when an authenticator signature counter does not increment, indicating potential cloning. ErrCounterNotIncremented = errors.New("passkey: signature counter did not increment (possible authenticator clone)") // ErrUserNotFound is returned when the target user record cannot be found. ErrUserNotFound = errors.New("passkey: user not found") // ErrSessionRequired is returned when passkey registration is attempted without an active session while requireSession is true. ErrSessionRequired = errors.New("passkey: passkey registration requires an authenticated session") // ErrResolveUserRequired is returned when requireSession is false but no resolveUser callback is configured. ErrResolveUserRequired = errors.New("passkey: resolveUser callback is required when requireSession is false and no session exists") // ErrInvalidResolvedUser is returned when a custom resolveUser callback returns a nil user entity. ErrInvalidResolvedUser = errors.New("passkey: resolved user is invalid") // ErrOriginMissing is returned when RP origin cannot be resolved from the HTTP request context. ErrOriginMissing = errors.New("passkey: origin is missing in request context") // ErrInvalidParameter is returned when a required input parameter is missing or malformed. ErrInvalidParameter = errors.New("passkey: invalid parameter provided") // ErrUnableToCreateSession is returned when creating a user session fails after successful WebAuthn authentication. ErrUnableToCreateSession = errors.New("passkey: unable to create user session") // ErrFailedToUpdatePasskey is returned when updating passkey metadata or counter fails. ErrFailedToUpdatePasskey = errors.New("passkey: failed to update passkey") )
Sentinel errors for the Passkey plugin.
var CommonAuthenticatorNames = map[string]string{
"ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4": "Google Password Manager",
"fbfc3007-154e-4ecc-8c0b-6e020557d7bd": "Apple Passwords",
"dd4ec289-e01d-41c9-bb89-70fa845d4bf2": "iCloud Keychain (Managed)",
"08987058-cadc-4b81-b6e1-30de50dcbe96": "Windows Hello",
"9ddd1817-af5a-4672-a2b9-3e3dd95000a9": "Windows Hello",
"6028b017-b1d4-4c02-b4b3-afcdafc96bb2": "Windows Hello",
"bada5566-a7aa-401f-bd96-45619a55120d": "1Password",
"d548826e-79b4-db40-a3d8-11116f7e8349": "Bitwarden",
"531126d6-e717-415c-9320-3d9aa6981239": "Dashlane",
"b78a0a55-6ef8-d246-a042-ba0f6d55050c": "LastPass",
"b84e4048-15dc-4dd0-8640-f4f60813c8af": "NordPass",
"50726f74-6f6e-5061-7373-50726f746f6e": "Proton Pass",
"0ea242b4-43c4-4a1b-8b17-dd6d0b6baec6": "Keeper",
"53414d53-554e-4700-0000-000000000000": "Samsung Pass",
"cb69481e-8ff7-4039-93ec-0a2729a1ef67": "YubiKey 5 Series",
"ee882879-721c-4916-956b-f40570e30973": "YubiKey 5 NFC",
"fa2b99dc-9e39-4257-8f92-4a30d23c4118": "YubiKey 5C NFC",
}
CommonAuthenticatorNames maps known Authenticator Attestation GUIDs (AAGUID) to human-friendly provider names.
Functions ¶
func GetAuthenticatorName ¶
GetAuthenticatorName returns the descriptive name of an authenticator given its AAGUID, or "Passkey" as fallback.
Types ¶
type AfterAuthenticationHook ¶
type AfterAuthenticationHook func(ctx context.Context, passkey *entity.Passkey, user *entity.User, session *entity.Session) error
AfterAuthenticationHook is executed after a passkey assertion is verified and a new session created.
type AfterRegistrationHook ¶
type AfterRegistrationHook func(ctx context.Context, passkey *entity.Passkey, user *entity.User) error
AfterRegistrationHook is executed after a new passkey credential has been successfully verified and saved.
type AuthenticationFailedPayload ¶
type AuthenticationFailedPayload struct {
UserID *string `json:"userId,omitempty"`
ChallengeToken string `json:"challengeToken"`
Reason string `json:"reason"`
Extra map[string]any `json:"extra,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
AuthenticationFailedPayload is dispatched when an authentication assertion fails.
type AuthenticationOptionsCreatedPayload ¶
type AuthenticationOptionsCreatedPayload struct {
UserID *string `json:"userId,omitempty"`
ChallengeToken string `json:"challengeToken"`
ExpiresAt time.Time `json:"expiresAt"`
Extra map[string]any `json:"extra,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
AuthenticationOptionsCreatedPayload is dispatched when an authentication assertion challenge is created.
type AuthenticationOptionsResult ¶
type AuthenticationOptionsResult struct {
Options *protocol.CredentialAssertion `json:"options"`
ChallengeToken string `json:"challengeToken"`
ExpiresAt time.Time `json:"expiresAt"`
}
AuthenticationOptionsResult contains assertion request options and the tracking challenge token.
type AuthenticationVerifiedPayload ¶
type AuthenticationVerifiedPayload struct {
Passkey *entity.Passkey `json:"passkey"`
User *entity.User `json:"user"`
Session *entity.Session `json:"session"`
Extra map[string]any `json:"extra,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
AuthenticationVerifiedPayload is dispatched when a passkey assertion is verified and session created.
type CeremonyType ¶
type CeremonyType string
CeremonyType defines the type of WebAuthn ceremony.
const ( CeremonyRegistration CeremonyType = "registration" CeremonyAuthentication CeremonyType = "authentication" )
type Config ¶
type Config struct {
RPDisplayName string // Relying Party human-readable name (default: "GoModularAuth")
RPID string // Relying Party domain identifier (default: "localhost")
RPOrigins []string // Allowed origin URLs (e.g. "http://localhost:3000")
ChallengeTimeout time.Duration // Ephemeral challenge lifespan (default: 5 minutes)
RequireSessionOnRegistration bool // Enforce caller session during registration (default: true)
UserVerification protocol.UserVerificationRequirement // User verification requirement (default: "preferred")
ResidentKey protocol.ResidentKeyRequirement // Resident key / Discoverable credential preference (default: "preferred")
Attestation protocol.ConveyancePreference // Attestation conveyance preference (default: "none")
AuthenticatorAttachment *protocol.AuthenticatorAttachment // Optional attachment ("platform" or "cross-platform")
SessionDuration time.Duration // Lifespan of created user sessions (default: 7 days)
ResolveUser ResolveUserFunc // User resolution callback
AfterRegistration AfterRegistrationHook // Post-registration hook
AfterAuthentication AfterAuthenticationHook // Post-authentication hook
}
Config holds runtime configuration options for the Passkey plugin.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a Config populated with production-ready defaults.
type DeletePasskeyParams ¶
type DeletePasskeyParams struct {
ID string `json:"id"`
CallerUserID string `json:"callerUserId"`
plugin.ExtraContainer
}
DeletePasskeyParams contains deletion parameters for an existing passkey.
type GenerateAuthenticationOptionsParams ¶
type GenerateAuthenticationOptionsParams struct {
UserID *string `json:"userId,omitempty"` // Optional. Omit for discoverable/resident key/autofill login.
plugin.ExtraContainer
}
GenerateAuthenticationOptionsParams holds inputs for creating a WebAuthn assertion ceremony challenge.
type GenerateRegistrationOptionsParams ¶
type GenerateRegistrationOptionsParams struct {
UserID string `json:"userId,omitempty"`
UserName string `json:"userName,omitempty"`
UserDisplayName string `json:"userDisplayName,omitempty"`
AuthenticatorAttachment *protocol.AuthenticatorAttachment `json:"authenticatorAttachment,omitempty"`
Context *string `json:"context,omitempty"`
CustomName *string `json:"customName,omitempty"`
plugin.ExtraContainer
}
GenerateRegistrationOptionsParams holds inputs for creating a WebAuthn credential registration ceremony challenge.
type ListPasskeysParams ¶
type ListPasskeysParams struct {
UserID string `json:"userId"`
plugin.ExtraContainer
}
ListPasskeysParams filters passkeys for a specific user.
type Option ¶
type Option func(*Config)
Option configures the Passkey plugin.
func WithAfterAuthentication ¶
func WithAfterAuthentication(hook AfterAuthenticationHook) Option
WithAfterAuthentication sets the post-authentication lifecycle hook.
func WithAfterRegistration ¶
func WithAfterRegistration(hook AfterRegistrationHook) Option
WithAfterRegistration sets the post-registration lifecycle hook.
func WithAttestation ¶
func WithAttestation(att protocol.ConveyancePreference) Option
WithAttestation sets the attestation conveyance preference.
func WithAuthenticatorAttachment ¶
func WithAuthenticatorAttachment(attachment protocol.AuthenticatorAttachment) Option
WithAuthenticatorAttachment restricts authenticators to platform or cross-platform devices.
func WithChallengeTimeout ¶
WithChallengeTimeout sets the validity duration for ephemeral challenges.
func WithRPDisplayName ¶
WithRPDisplayName sets the Relying Party human-readable name.
func WithRPID ¶
WithRPID sets the Relying Party domain identifier (e.g. "auth.example.com" or "localhost").
func WithRPOrigins ¶
WithRPOrigins sets the list of allowed origin URLs.
func WithRequireSessionOnRegistration ¶
WithRequireSessionOnRegistration toggles whether registration requires an active authenticated caller.
func WithResidentKey ¶
func WithResidentKey(rk protocol.ResidentKeyRequirement) Option
WithResidentKey sets the resident key (discoverable credential) preference.
func WithResolveUser ¶
func WithResolveUser(fn ResolveUserFunc) Option
WithResolveUser sets the callback to resolve user identity during unauthenticated registration flows.
func WithSessionDuration ¶
WithSessionDuration sets the validity duration for sessions issued upon successful authentication.
func WithUserVerification ¶
func WithUserVerification(uv protocol.UserVerificationRequirement) Option
WithUserVerification sets the WebAuthn user verification preference.
type PasskeyChallenge ¶
type PasskeyChallenge struct {
Token string `json:"token"`
Type CeremonyType `json:"type"`
Challenge string `json:"challenge"`
UserID *string `json:"userId,omitempty"`
UserName *string `json:"userName,omitempty"`
DisplayName *string `json:"displayName,omitempty"`
Context *string `json:"context,omitempty"`
SessionData string `json:"sessionData"` // Serialized JSON of webauthn.SessionData
ExpiresAt time.Time `json:"expiresAt"`
CreatedAt time.Time `json:"createdAt"`
}
PasskeyChallenge represents the ephemeral state of an in-flight WebAuthn challenge.
type PasskeyDeletedPayload ¶
type PasskeyDeletedPayload struct {
PasskeyID string `json:"passkeyId"`
UserID string `json:"userId"`
Timestamp time.Time `json:"timestamp"`
}
PasskeyDeletedPayload is dispatched when a passkey is deleted.
type PasskeyRegistrationUser ¶
type PasskeyRegistrationUser struct {
ID string `json:"id"`
Name string `json:"name"`
DisplayName string `json:"displayName"`
}
PasskeyRegistrationUser represents a resolved user identity when registering without an active session.
type PasskeyUpdatedPayload ¶
type PasskeyUpdatedPayload struct {
Passkey *entity.Passkey `json:"passkey"`
OldName *string `json:"oldName,omitempty"`
NewName string `json:"newName"`
Timestamp time.Time `json:"timestamp"`
}
PasskeyUpdatedPayload is dispatched when a passkey's metadata/name is updated.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin implements the plugin.Plugin interface for FIDO2/WebAuthn Passkey authentication.
func New ¶
func New(repo Repository, opts ...Option) *Plugin
New creates and returns a new Passkey plugin configured with the given repository and options.
func (*Plugin) DeletePasskey ¶
func (p *Plugin) DeletePasskey(ctx context.Context, params *DeletePasskeyParams) error
DeletePasskey removes a passkey after verifying caller ownership.
func (*Plugin) GenerateAuthenticationOptions ¶
func (p *Plugin) GenerateAuthenticationOptions(ctx context.Context, params *GenerateAuthenticationOptionsParams) (*AuthenticationOptionsResult, error)
GenerateAuthenticationOptions begins a WebAuthn assertion ceremony for signing in.
func (*Plugin) GenerateRegistrationOptions ¶
func (p *Plugin) GenerateRegistrationOptions(ctx context.Context, params *GenerateRegistrationOptionsParams) (*RegistrationOptionsResult, error)
GenerateRegistrationOptions begins a WebAuthn credential registration ceremony.
func (*Plugin) GetPasskey ¶
GetPasskey retrieves a single passkey by its unique identifier.
func (*Plugin) Init ¶
Init initializes the plugin within the core engine and configures the WebAuthn cryptographic engine.
func (*Plugin) ListPasskeys ¶
func (p *Plugin) ListPasskeys(ctx context.Context, params *ListPasskeysParams) ([]*entity.Passkey, error)
ListPasskeys retrieves all passkeys associated with a user.
func (*Plugin) ServeAuthenticationOptions ¶ added in v0.20.0
func (p *Plugin) ServeAuthenticationOptions(w http.ResponseWriter, r *http.Request)
ServeAuthenticationOptions is a net/http handler for generating WebAuthn passkey authentication options.
func (*Plugin) ServeRegistrationOptions ¶ added in v0.20.0
func (p *Plugin) ServeRegistrationOptions(w http.ResponseWriter, r *http.Request)
ServeRegistrationOptions is a net/http handler for generating WebAuthn passkey registration options.
func (*Plugin) ServeVerifyAuthentication ¶ added in v0.20.0
func (p *Plugin) ServeVerifyAuthentication(w http.ResponseWriter, r *http.Request)
ServeVerifyAuthentication is a net/http handler for verifying WebAuthn passkey authentication.
func (*Plugin) ServeVerifyRegistration ¶ added in v0.20.0
func (p *Plugin) ServeVerifyRegistration(w http.ResponseWriter, r *http.Request)
ServeVerifyRegistration is a net/http handler for verifying WebAuthn passkey registration.
func (*Plugin) UpdatePasskey ¶
func (p *Plugin) UpdatePasskey(ctx context.Context, params *UpdatePasskeyParams) (*entity.Passkey, error)
UpdatePasskey modifies a passkey's friendly name after verifying ownership.
func (*Plugin) VerifyAuthentication ¶
func (p *Plugin) VerifyAuthentication(ctx context.Context, params *VerifyAuthenticationParams) (*VerifyAuthenticationResult, error)
VerifyAuthentication verifies a WebAuthn assertion response and issues an authenticated session.
func (*Plugin) VerifyRegistration ¶
func (p *Plugin) VerifyRegistration(ctx context.Context, params *VerifyRegistrationParams) (*entity.Passkey, error)
VerifyRegistration verifies the response from navigator.credentials.create() and registers the passkey.
type RegistrationFailedPayload ¶
type RegistrationFailedPayload struct {
UserID *string `json:"userId,omitempty"`
ChallengeToken string `json:"challengeToken"`
Reason string `json:"reason"`
Extra map[string]any `json:"extra,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
RegistrationFailedPayload is dispatched when registration ceremony verification fails.
type RegistrationOptionsCreatedPayload ¶
type RegistrationOptionsCreatedPayload struct {
UserID string `json:"userId"`
UserName string `json:"userName"`
ChallengeToken string `json:"challengeToken"`
ExpiresAt time.Time `json:"expiresAt"`
Extra map[string]any `json:"extra,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
RegistrationOptionsCreatedPayload is dispatched when a registration challenge is generated.
type RegistrationOptionsResult ¶
type RegistrationOptionsResult struct {
Options *protocol.CredentialCreation `json:"options"`
ChallengeToken string `json:"challengeToken"`
ExpiresAt time.Time `json:"expiresAt"`
}
RegistrationOptionsResult contains creation options sent to the browser and the associated challenge token.
type RegistrationVerifiedPayload ¶
type RegistrationVerifiedPayload struct {
Passkey *entity.Passkey `json:"passkey"`
User *entity.User `json:"user"`
Extra map[string]any `json:"extra,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
RegistrationVerifiedPayload is dispatched when a new passkey credential is authenticated and persisted.
type Repository ¶
type Repository interface {
// CreatePasskey persists a new WebAuthn passkey credential record.
//
// Function:
// Called upon completing a WebAuthn registration ceremony to store public key credentials.
//
// Storage:
// Database (GORM / SQL) - Persistent storage for WebAuthn public keys.
//
// Arguments:
// - ctx: Request cancellation context.
// - passkey: The Passkey entity containing credential ID, public key, counter, and transports.
//
// Returns:
// - error: Nil on success, or database error.
//
// Example SQL:
// INSERT INTO passkeys (id, user_id, credential_id, public_key, counter, aaguid, name, created_at, updated_at)
// VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);
CreatePasskey(ctx context.Context, passkey *entity.Passkey) error
// GetPasskeyByID retrieves a passkey credential by its primary key ID.
//
// Function:
// Used during passkey management (viewing or renaming passkeys).
//
// Storage:
// Database (GORM / SQL) - Passkey record lookup by ID.
//
// Arguments:
// - ctx: Request cancellation context.
// - id: Unique passkey record identifier.
//
// Returns:
// - *entity.Passkey: Matching passkey entity if found.
// - error: ErrPasskeyNotFound if missing, or database error.
//
// Example SQL:
// SELECT id, user_id, credential_id, public_key, counter, aaguid, name, created_at, updated_at FROM passkeys WHERE id = $1 LIMIT 1;
GetPasskeyByID(ctx context.Context, id string) (*entity.Passkey, error)
// GetPasskeyByCredentialID retrieves a passkey credential by its raw WebAuthn credential ID.
//
// Function:
// Used during authentication ceremony verification to locate the matching public key.
//
// Storage:
// Database (GORM / SQL) - Query by unique credential ID string.
//
// Arguments:
// - ctx: Request cancellation context.
// - credentialID: Base64URL encoded credential ID string.
//
// Returns:
// - *entity.Passkey: Matching passkey entity if found.
// - error: ErrPasskeyNotFound if missing, or database error.
//
// Example SQL:
// SELECT id, user_id, credential_id, public_key, counter, aaguid, name, created_at, updated_at FROM passkeys WHERE credential_id = $1 LIMIT 1;
GetPasskeyByCredentialID(ctx context.Context, credentialID string) (*entity.Passkey, error)
// ListPasskeysByUserID retrieves all registered passkey credentials belonging to a user.
//
// Function:
// Used during user security settings listing or passwordless sign-in user credential discovery.
//
// Storage:
// Database (GORM / SQL) - Query registered credentials by user ID.
//
// Arguments:
// - ctx: Request cancellation context.
// - userID: Target user identifier.
//
// Returns:
// - []*entity.Passkey: Slice of registered passkey credentials.
// - error: Nil on success, or database error.
//
// Example SQL:
// SELECT id, user_id, credential_id, public_key, counter, aaguid, name, created_at, updated_at FROM passkeys WHERE user_id = $1;
ListPasskeysByUserID(ctx context.Context, userID string) ([]*entity.Passkey, error)
// UpdatePasskey updates mutable attributes of an existing passkey (e.g. friendly name).
//
// Function:
// Used when a user renames a registered passkey.
//
// Storage:
// Database (GORM / SQL) - Record update.
//
// Arguments:
// - ctx: Request cancellation context.
// - passkey: Modified passkey entity.
//
// Returns:
// - error: Nil on success, or database error.
//
// Example SQL:
// UPDATE passkeys SET name = $1, updated_at = $2 WHERE id = $3;
UpdatePasskey(ctx context.Context, passkey *entity.Passkey) error
// UpdatePasskeyCounter updates the signature counter of a passkey after successful assertion.
//
// Function:
// Called after validating an authentication ceremony response to prevent clone attacks.
//
// Storage:
// Database (GORM / SQL) - Signature counter update.
//
// Arguments:
// - ctx: Request cancellation context.
// - id: Unique passkey record ID.
// - newCounter: Incrementally higher signature counter value.
//
// Returns:
// - error: Nil on success, or database error.
//
// Example SQL:
// UPDATE passkeys SET counter = $1, updated_at = $2 WHERE id = $3;
UpdatePasskeyCounter(ctx context.Context, id string, newCounter uint32) error
// DeletePasskey permanently removes a single passkey credential record.
//
// Function:
// Called when a user revokes or deletes a passkey in security settings.
//
// Storage:
// Database (GORM / SQL) - Passkey credential deletion.
//
// Arguments:
// - ctx: Request cancellation context.
// - id: Passkey record identifier.
//
// Returns:
// - error: Nil on success, or database error.
//
// Example SQL:
// DELETE FROM passkeys WHERE id = $1;
DeletePasskey(ctx context.Context, id string) error
// DeletePasskeysByUserID purges all passkeys belonging to a user.
//
// Function:
// Called during user account deletion.
//
// Storage:
// Database (GORM / SQL) - Bulk passkey deletion.
//
// Arguments:
// - ctx: Request cancellation context.
// - userID: Target user identifier.
//
// Returns:
// - error: Nil on success, or database error.
//
// Example SQL:
// DELETE FROM passkeys WHERE user_id = $1;
DeletePasskeysByUserID(ctx context.Context, userID string) error
// SavePasskeyChallenge persists an ephemeral WebAuthn ceremony challenge record.
//
// Function:
// Called during generate-register-options and generate-authenticate-options endpoints.
//
// Storage:
// Cache (Redis / In-Memory TTL) - Short-lived in-flight challenge token with TTL.
//
// Arguments:
// - ctx: Request cancellation context.
// - challenge: Ephemeral PasskeyChallenge state.
//
// Returns:
// - error: Nil on success, or database error.
//
// Example SQL:
// INSERT INTO passkey_challenges (token, type, challenge, user_id, session_data, expires_at, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7);
//
// Example Cache (Redis):
// err := rdb.Set(ctx, "webauthn:challenge:" + challenge.Token, bytes, ttl).Err()
SavePasskeyChallenge(ctx context.Context, challenge *PasskeyChallenge) error
// GetPasskeyChallenge retrieves an active ceremony challenge record by challenge token string.
//
// Function:
// Used to inspect challenge state without consuming it.
//
// Storage:
// Cache (Redis / In-Memory TTL) - Ephemeral challenge lookup.
//
// Arguments:
// - ctx: Request cancellation context.
// - token: Ephemeral challenge token identifier.
//
// Returns:
// - *PasskeyChallenge: Matching challenge state.
// - error: ErrChallengeNotFound if missing or expired.
//
// Example SQL:
// SELECT token, type, challenge, user_id, session_data, expires_at, created_at FROM passkey_challenges WHERE token = $1 LIMIT 1;
//
// Example Cache (Redis):
// val, err := rdb.Get(ctx, "webauthn:challenge:" + token).Bytes()
GetPasskeyChallenge(ctx context.Context, token string) (*PasskeyChallenge, error)
// ConsumePasskeyChallenge atomically retrieves and removes an in-flight ceremony challenge record.
//
// Function:
// Called during ceremony verification endpoints to ensure single-use replay protection.
//
// Storage:
// Cache (Redis GETDEL / Memory) - Atomic read-and-delete single-use challenge consumption.
//
// Arguments:
// - ctx: Request cancellation context.
// - token: Ephemeral challenge token identifier.
//
// Returns:
// - *PasskeyChallenge: The consumed challenge state if valid.
// - error: ErrChallengeNotFound if missing, or ErrChallengeExpired if passed validity time.
//
// Example SQL:
// DELETE FROM passkey_challenges WHERE token = $1 AND expires_at > $2 RETURNING token, type, challenge, user_id, session_data, expires_at, created_at;
//
// Example Cache (Redis):
// val, err := rdb.GetDel(ctx, "webauthn:challenge:" + token).Bytes()
ConsumePasskeyChallenge(ctx context.Context, token string) (*PasskeyChallenge, error)
// DeletePasskeyChallenge removes a challenge record from storage by token.
//
// Function:
// Called during explicit cancellation or cleanup.
//
// Storage:
// Cache (Redis / In-Memory TTL) - Challenge token eviction.
//
// Arguments:
// - ctx: Request cancellation context.
// - token: Challenge token string.
//
// Returns:
// - error: Nil on success, or database error.
//
// Example SQL:
// DELETE FROM passkey_challenges WHERE token = $1;
//
// Example Cache (Redis):
// err := rdb.Del(ctx, "webauthn:challenge:" + token).Err()
DeletePasskeyChallenge(ctx context.Context, token string) error
// GetUserByID retrieves user profile details by ID.
//
// Function:
// Used during passkey registration to populate WebAuthn User Entity details (name, display name).
//
// Storage:
// Database (GORM / SQL) - User primary key lookup.
//
// Arguments:
// - ctx: Request cancellation context.
// - userID: Target user identifier.
//
// Returns:
// - *entity.User: Matching user entity if found.
// - error: ErrUserNotFound if missing, or database error.
//
// Example SQL:
// SELECT id, email, name, email_verified, created_at, updated_at FROM users WHERE id = $1 LIMIT 1;
GetUserByID(ctx context.Context, userID string) (*entity.User, error)
// GetUserByEmail retrieves user profile details by email address.
//
// Function:
// Used during passwordless authentication initiation when identifying user by email.
//
// Storage:
// Database (GORM / SQL) - Query user by email address.
//
// Arguments:
// - ctx: Request cancellation context.
// - email: User primary email address.
//
// Returns:
// - *entity.User: Matching user entity if found.
// - error: ErrUserNotFound if missing, or database error.
//
// Example SQL:
// SELECT id, email, name, email_verified, created_at, updated_at FROM users WHERE email = $1 LIMIT 1;
GetUserByEmail(ctx context.Context, email string) (*entity.User, error)
// CreateSession initializes and persists a new authenticated user session.
//
// Function:
// Called after successful passkey authentication ceremony completion.
//
// Storage:
// Database (GORM / SQL) - Active session creation.
//
// Arguments:
// - ctx: Request cancellation context.
// - session: DTO containing user ID, token, expiration, IP address, and user agent.
//
// Returns:
// - *entity.Session: Populated active session entity.
// - error: ErrUnableToCreateSession on failure.
//
// Example SQL:
// INSERT INTO sessions (id, user_id, token, expires_at, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6);
CreateSession(ctx context.Context, session *dto.CreateSessionParams) (*entity.Session, error)
}
Repository defines the storage contract required by the Passkey authentication plugin. Implement this interface on your custom database adapter (e.g. PostgreSQL, MySQL, SQLite, MongoDB, GORM).
Implementation Example (GORM / database/sql): ¶
type GormPasskeyRepository struct {
db *gorm.DB
}
func (r *GormPasskeyRepository) GetPasskeyByCredentialID(ctx context.Context, credentialID string) (*entity.Passkey, error) {
var pk entity.Passkey
if err := r.db.WithContext(ctx).Where("credential_id = ?", credentialID).First(&pk).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, passkey.ErrPasskeyNotFound
}
return nil, err
}
return &pk, nil
}
Storage and Caching Recommendation (Ephemeral Challenge Caching): ¶
In-flight WebAuthn challenges (`PasskeyChallenge`) are short-lived (e.g. 2-5 minutes) and single-use. Storing challenges in Redis or an in-memory key-value cache prevents unnecessary DB table writes:
type RedisPasskeyChallengeStore struct {
redis *redis.Client
}
func (r *RedisPasskeyChallengeStore) SavePasskeyChallenge(ctx context.Context, ch *passkey.PasskeyChallenge) error {
bytes, _ := json.Marshal(ch)
ttl := time.Until(ch.ExpiresAt)
return r.redis.Set(ctx, "webauthn:challenge:"+ch.Token, bytes, ttl).Err()
}
func (r *RedisPasskeyChallengeStore) ConsumePasskeyChallenge(ctx context.Context, token string) (*passkey.PasskeyChallenge, error) {
key := "webauthn:challenge:" + token
val, err := r.redis.GetDel(ctx, key).Bytes()
if err != nil {
return nil, passkey.ErrChallengeNotFound
}
var ch passkey.PasskeyChallenge
_ = json.Unmarshal(val, &ch)
return &ch, nil
}
type ResolveUserFunc ¶
type ResolveUserFunc func(ctx context.Context, queryContext *string, extra map[string]any) (*PasskeyRegistrationUser, error)
ResolveUserFunc is invoked to resolve or provision a user identity during registration when requireSession is false.
type UpdatePasskeyParams ¶
type UpdatePasskeyParams struct {
ID string `json:"id"`
CallerUserID string `json:"callerUserId"`
Name string `json:"name"`
plugin.ExtraContainer
}
UpdatePasskeyParams contains update parameters for an existing passkey.
type VerifyAuthenticationParams ¶
type VerifyAuthenticationParams struct {
ChallengeToken string `json:"challengeToken"`
Origin string `json:"origin,omitempty"`
Response *protocol.CredentialAssertionResponse `json:"response"`
plugin.ExtraContainer
}
VerifyAuthenticationParams holds data returned from navigator.credentials.get() for verification.
type VerifyAuthenticationResult ¶
type VerifyAuthenticationResult struct {
User *entity.User `json:"user"`
Session *entity.Session `json:"session"`
Passkey *entity.Passkey `json:"passkey"`
}
VerifyAuthenticationResult contains authenticated identity, issued session, and the verified passkey.
type VerifyRegistrationParams ¶
type VerifyRegistrationParams struct {
ChallengeToken string `json:"challengeToken"`
Origin string `json:"origin,omitempty"`
Response *protocol.CredentialCreationResponse `json:"response"`
Name *string `json:"name,omitempty"`
CallerUserID *string `json:"callerUserId,omitempty"`
plugin.ExtraContainer
}
VerifyRegistrationParams holds data returned from navigator.credentials.create() for verification.