Help us shape the future of Go! Take the 2026 Go Developer Survey

passkey

package
v1.9.0 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 39 Imported by: 0

README

passkey

WebAuthn and FIDO2 sign-in. Face ID, Touch ID, Windows Hello, a YubiKey, or a passkey synced through iCloud or a password manager.

This is the one auth method on the list that phishing can't beat. The credential is bound to your origin by the browser, so a lookalike domain gets nothing, and the private key never leaves the authenticator. Registration and login are both two-step ceremonies: begin, then finish.

The plugin owns its own credential store, so it brings a migration with it.

Use it when

  • Phishing resistance is the requirement, not a nice-to-have
  • You want sign-in that's genuinely faster than typing a password, on devices that already have a biometric
  • You're moving off passwords and need something users will not fight you on

Skip it when

  • Your users are on shared or locked-down machines with no authenticator to enrol against
  • You can't guarantee HTTPS and a stable domain. RPID is the domain, and changing it invalidates every credential your users registered
  • You need it to be the only method on day one. Keep a fallback until enrolment is high

Wiring

authsome.WithPlugin(passkey.New(passkey.Config{
    RPDisplayName: "My App",
    RPID:          "example.com",
    RPOrigins:     []string{"https://example.com"},
}))

RPID must match the domain the browser sees. Get it wrong and the ceremony fails with an error that does not say why.

Config

Field Type Default What it does
RPDisplayName string AuthSome Name shown in the browser prompt
RPID string localhost Relying party ID, normally your bare domain
RPOrigins []string empty Origins allowed to run ceremonies
SessionTimeout time.Duration 5m How long a half-finished ceremony stays open

Settings

Key Default
passkey.rp_display_name AuthSome
passkey.rp_id localhost
passkey.rp_origins []
passkey.session_timeout_seconds 300

Endpoints

Registered under /v1/passkeys.

Method Path Purpose
POST /register/begin Start enrolment, returns the creation challenge
POST /register/finish Complete enrolment and store the credential
POST /login/begin Start authentication, returns the assertion challenge
POST /login/finish Verify the assertion and mint a session
GET / List the calling user's passkeys
DELETE /:credentialId Remove a passkey

Lifecycle hooks

None on the sign-in path. It registers as an AuthMethodContributor so the dashboard and the sign-in UI know the user has a passkey enrolled, and a MigrationProvider for its credential table.

mfa covers TOTP and SMS if you want a second factor rather than a replacement for the first. magiclink is the other passwordless route.

Documentation

Overview

contract.go: Wires the passkey plugin into the forge-dashboard contract surface via plugin.ContractContributor.

Package passkey provides WebAuthn/passkey authentication for AuthSome.

The passkey plugin supports FIDO2 WebAuthn registration and authentication ceremonies. It owns its own credential store (like the MFA plugin) and provides HTTP routes via the RouteProvider interface.

Usage:

eng, _ := authsome.NewEngine(
    authsome.WithStore(store),
    authsome.WithPlugin(passkey.New(passkey.Config{
        RPDisplayName: "My App",
        RPID:          "example.com",
        RPOrigins:     []string{"https://example.com"},
    })),
)

Index

Constants

This section is empty.

Variables

View Source
var (
	// SettingRPDisplayName controls the relying party display name.
	SettingRPDisplayName = settings.Define("passkey.rp_display_name", "AuthSome",
		settings.WithDisplayName("RP Display Name"),
		settings.WithDescription("Relying party display name shown to users during WebAuthn ceremonies"),
		settings.WithCategory("Passkey / WebAuthn"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithInputType(formconfig.FieldText),
		settings.WithPlaceholder("AuthSome"),
		settings.WithUIValidation(formconfig.Validation{Required: true, MaxLen: 100}),
		settings.WithHelpText("The name users see when registering or authenticating with passkeys"),
		settings.WithOrder(10),
	)

	// SettingRPID controls the relying party identifier.
	SettingRPID = settings.Define("passkey.rp_id", "localhost",
		settings.WithDisplayName("RP ID"),
		settings.WithDescription("Relying party identifier (typically the domain name)"),
		settings.WithCategory("Passkey / WebAuthn"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithInputType(formconfig.FieldText),
		settings.WithPlaceholder("example.com"),
		settings.WithUIValidation(formconfig.Validation{
			Required: true,
			MaxLen:   253,

			Pattern: `^(localhost|127\.0\.0\.1|::1|([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*)$`,
		}),
		settings.WithHelpText("Must match the effective domain users browse to. Use 'localhost' for dev."),
		settings.WithOrder(20),
	)

	// SettingRPOrigins controls the allowed origins for WebAuthn ceremonies.
	// When empty, origins are derived from RPID (and the request origin for
	// localhost dev mode — see Plugin.waForRequest).
	SettingRPOrigins = settings.Define("passkey.rp_origins", []string{},
		settings.WithDisplayName("RP Origins"),
		settings.WithDescription("Allowed origins for WebAuthn ceremonies (e.g. https://app.example.com)"),
		settings.WithCategory("Passkey / WebAuthn"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithInputType(formconfig.FieldTextarea),
		settings.WithPlaceholder("https://app.example.com\nhttps://www.example.com"),
		settings.WithHelpText("One origin per line. Each must include scheme and port. Leave empty to use Config defaults; localhost dev auto-trusts the request origin regardless of port."),
		settings.WithOrder(25),
	)

	// SettingSessionTimeoutSeconds controls the WebAuthn ceremony timeout.
	SettingSessionTimeoutSeconds = settings.Define("passkey.session_timeout_seconds", 300,
		settings.WithDisplayName("Ceremony Timeout (seconds)"),
		settings.WithDescription("How long a WebAuthn ceremony session lives in seconds"),
		settings.WithCategory("Passkey / WebAuthn"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Required: true, Min: intPtr(30), Max: intPtr(600)}),
		settings.WithHelpText("Time allowed to complete a passkey ceremony. Default: 300 (5 minutes)"),
		settings.WithOrder(30),
	)
)
View Source
var ErrCredentialNotFound = errors.New("passkey: credential not found")

ErrCredentialNotFound is returned when a credential is not found.

View Source
var PostgresMigrations = migrate.NewGroup("authsome-passkey", migrate.DependsOn("authsome"))

PostgresMigrations is the postgres migration group for the passkey plugin.

View Source
var SqliteMigrations = migrate.NewGroup("authsome-passkey", migrate.DependsOn("authsome"))

SqliteMigrations is the SQLite migration group for the passkey plugin.

Functions

This section is empty.

Types

type Assertion added in v1.9.0

type Assertion struct {
	ID       string            `json:"id"`
	RawID    string            `json:"rawId"`
	Type     string            `json:"type"`
	Response AssertionResponse `json:"response"`
}

Assertion is the credential navigator.credentials.get() returns, posted to /v1/passkeys/login/finish.

type AssertionResponse added in v1.9.0

type AssertionResponse struct {
	ClientDataJSON    string `json:"clientDataJSON"`
	AuthenticatorData string `json:"authenticatorData"`
	Signature         string `json:"signature"`
	UserHandle        string `json:"userHandle,omitempty"`
}

AssertionResponse is the assertion half of a login credential. UserHandle is present only for discoverable (passwordless) login.

type Attestation added in v1.9.0

type Attestation struct {
	ID       string              `json:"id"`
	RawID    string              `json:"rawId"`
	Type     string              `json:"type"`
	Response AttestationResponse `json:"response"`
}

Attestation is the credential navigator.credentials.create() returns, posted to /v1/passkeys/register/finish.

type AttestationResponse added in v1.9.0

type AttestationResponse struct {
	ClientDataJSON    string `json:"clientDataJSON"`
	AttestationObject string `json:"attestationObject"`
}

AttestationResponse is the attestation half of a registration credential.

type Config

type Config struct {
	// RPDisplayName is the relying party display name shown to users.
	RPDisplayName string

	// RPID is the relying party identifier (typically the domain name).
	RPID string

	// RPOrigins are the allowed origins for WebAuthn ceremonies.
	RPOrigins []string

	// SessionTimeout is how long a WebAuthn ceremony session lives (default: 5 minutes).
	SessionTimeout time.Duration

	// StepUpWindow is how recently the caller must have signed in to
	// register a passkey (default: 5 minutes). A passkey is a sign-in
	// credential, so adding one on a possibly stolen session is refused.
	StepUpWindow time.Duration
}

Config configures the passkey/WebAuthn plugin.

type Credential added in v1.9.0

type Credential struct {
	ID              id.PasskeyID `json:"id"`
	UserID          id.UserID    `json:"user_id"`
	AppID           id.AppID     `json:"app_id"`
	CredentialID    []byte       `json:"credential_id"`    // Raw credential ID from WebAuthn
	PublicKey       []byte       `json:"public_key"`       // COSE-encoded public key
	AttestationType string       `json:"attestation_type"` // "none", "packed", etc.
	Transport       []string     `json:"transport"`        // "usb", "ble", "nfc", "internal"
	SignCount       uint32       `json:"sign_count"`
	AAGUID          []byte       `json:"aaguid"`
	DisplayName     string       `json:"display_name"`
	CreatedAt       time.Time    `json:"created_at"`
	UpdatedAt       time.Time    `json:"updated_at"`
}

Credential represents a WebAuthn/passkey credential stored for a user.

type CredentialInfo added in v1.9.0

type CredentialInfo struct {
	ID          string   `json:"id"`
	DisplayName string   `json:"display_name"`
	Transport   []string `json:"transport"`
	CreatedAt   string   `json:"created_at"`
}

CredentialInfo is a serializable credential summary.

type DeleteRequest added in v1.9.0

type DeleteRequest struct {
	CredentialID string `path:"credentialId"`
}

DeleteRequest is the request for deleting a passkey.

type DeleteResponse added in v1.9.0

type DeleteResponse struct {
	Status string `json:"status"`
}

DeleteResponse confirms passkey deletion.

type ListRequest added in v1.9.0

type ListRequest struct{}

ListRequest is an empty request for listing passkeys.

type ListResponse added in v1.9.0

type ListResponse struct {
	Credentials []*CredentialInfo `json:"credentials"`
}

ListResponse contains the user's registered passkeys.

type LoginBeginRequest added in v1.9.0

type LoginBeginRequest struct{}

LoginBeginRequest initiates a WebAuthn authentication ceremony. LoginBeginRequest has no fields: which ceremony to start is decided by whether the caller is already authenticated, not by anything they send.

It previously carried an Email, but nothing ever read it — resolveUser resolves from the auth context — so supplying one selected the step-up branch while the value itself was discarded. Email-identified passkey login is not implemented; it would also disclose whether an address has passkeys registered, which the discoverable flow avoids by design.

type LoginBeginResponse added in v1.9.0

type LoginBeginResponse struct {
	Options any `json:"options"`
}

LoginBeginResponse contains the WebAuthn assertion options.

type LoginFinishRequest added in v1.9.0

type LoginFinishRequest struct{}

LoginFinishRequest is the raw assertion response from the browser.

type LoginFinishResponse added in v1.9.0

type LoginFinishResponse struct {
	UserID       string `json:"user_id"`
	Status       string `json:"status"`
	SessionToken string `json:"session_token,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`
}

LoginFinishResponse confirms successful authentication. For passwordless (discoverable) login it also carries the freshly-issued session tokens.

type MemoryStore added in v1.9.0

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

MemoryStore is an in-memory Store for testing.

func NewMemoryStore added in v1.9.0

func NewMemoryStore() *MemoryStore

NewMemoryStore creates a new in-memory passkey credential store.

func (*MemoryStore) CreateCredential added in v1.9.0

func (s *MemoryStore) CreateCredential(_ context.Context, c *Credential) error

CreateCredential stores a new passkey credential.

func (*MemoryStore) DeleteCredential added in v1.9.0

func (s *MemoryStore) DeleteCredential(_ context.Context, credentialID []byte) error

DeleteCredential removes a credential by its WebAuthn credential ID.

func (*MemoryStore) GetCredential added in v1.9.0

func (s *MemoryStore) GetCredential(_ context.Context, credentialID []byte) (*Credential, error)

GetCredential finds a credential by its WebAuthn credential ID.

func (*MemoryStore) ListUserCredentials added in v1.9.0

func (s *MemoryStore) ListUserCredentials(_ context.Context, userID id.UserID) ([]*Credential, error)

ListUserCredentials returns all credentials for a user.

func (*MemoryStore) UpdateSignCount added in v1.9.0

func (s *MemoryStore) UpdateSignCount(_ context.Context, credentialID []byte, count uint32) error

UpdateSignCount updates the sign counter for a credential.

type MongoStore added in v1.9.0

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

MongoStore implements passkey.Store using the Grove MongoDB driver.

func NewMongoStore added in v1.9.0

func NewMongoStore(db *grove.DB) *MongoStore

NewMongoStore creates a new MongoDB-backed passkey store.

func (*MongoStore) CreateCredential added in v1.9.0

func (s *MongoStore) CreateCredential(ctx context.Context, c *Credential) error

func (*MongoStore) DeleteCredential added in v1.9.0

func (s *MongoStore) DeleteCredential(ctx context.Context, credentialID []byte) error

func (*MongoStore) GetCredential added in v1.9.0

func (s *MongoStore) GetCredential(ctx context.Context, credentialID []byte) (*Credential, error)

func (*MongoStore) ListUserCredentials added in v1.9.0

func (s *MongoStore) ListUserCredentials(ctx context.Context, userID id.UserID) ([]*Credential, error)

func (*MongoStore) UpdateSignCount added in v1.9.0

func (s *MongoStore) UpdateSignCount(ctx context.Context, credentialID []byte, count uint32) error

type Plugin

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

Plugin is the passkey/WebAuthn plugin.

func New added in v1.9.0

func New(cfg ...Config) *Plugin

New creates a new passkey plugin.

func (*Plugin) DeclareSettings added in v1.9.0

func (p *Plugin) DeclareSettings(m *settings.Manager) error

DeclareSettings implements plugin.SettingsProvider.

func (*Plugin) ListUserAuthMethods added in v1.9.0

func (p *Plugin) ListUserAuthMethods(ctx context.Context, userID id.UserID) ([]*plugin.AuthMethod, error)

ListUserAuthMethods implements plugin.AuthMethodContributor. It returns one entry per registered passkey credential for the user.

func (*Plugin) MigrationGroups added in v1.9.0

func (p *Plugin) MigrationGroups(driverName string) []*migrate.Group

MigrationGroups implements plugin.MigrationProvider.

func (*Plugin) Name added in v1.9.0

func (p *Plugin) Name() string

Name returns the plugin name.

func (*Plugin) OnBeforeUserDelete added in v1.9.0

func (p *Plugin) OnBeforeUserDelete(ctx context.Context, userID id.UserID) error

OnBeforeUserDelete removes a deleted user's passkeys. A credential that outlives its account is a way back in that nobody owns.

func (*Plugin) OnInit added in v1.9.0

func (p *Plugin) OnInit(_ context.Context, engine plugin.Engine) error

OnInit initializes the WebAuthn library and optionally extracts bridges and the ceremony store from the engine.

func (*Plugin) RegisterContract added in v1.9.0

func (p *Plugin) RegisterContract(
	d *dispatcher.Dispatcher,
	reg contract.Registry,
	wreg contract.WardenRegistry,
	engine plugin.Engine,
) error

func (*Plugin) RegisterRoutes

func (p *Plugin) RegisterRoutes(router forge.Router) error

RegisterRoutes registers passkey/WebAuthn HTTP endpoints on a forge.Router.

func (*Plugin) SetStore added in v1.9.0

func (p *Plugin) SetStore(s Store)

SetStore sets the credential store for testing.

type PostgresStore added in v1.9.0

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

PostgresStore implements passkey.Store using the Grove ORM with PostgreSQL.

func NewPostgresStore added in v1.9.0

func NewPostgresStore(db *grove.DB) *PostgresStore

NewPostgresStore creates a new PostgreSQL-backed passkey store.

func (*PostgresStore) CreateCredential added in v1.9.0

func (s *PostgresStore) CreateCredential(ctx context.Context, c *Credential) error

func (*PostgresStore) DeleteCredential added in v1.9.0

func (s *PostgresStore) DeleteCredential(ctx context.Context, credentialID []byte) error

func (*PostgresStore) GetCredential added in v1.9.0

func (s *PostgresStore) GetCredential(ctx context.Context, credentialID []byte) (*Credential, error)

func (*PostgresStore) ListUserCredentials added in v1.9.0

func (s *PostgresStore) ListUserCredentials(ctx context.Context, userID id.UserID) ([]*Credential, error)

func (*PostgresStore) UpdateSignCount added in v1.9.0

func (s *PostgresStore) UpdateSignCount(ctx context.Context, credentialID []byte, count uint32) error

type RegisterBeginRequest added in v1.9.0

type RegisterBeginRequest struct {
	DisplayName string `json:"display_name,omitempty"`
}

RegisterBeginRequest is the request body for beginning passkey registration.

type RegisterBeginResponse added in v1.9.0

type RegisterBeginResponse struct {
	Options any `json:"options"`
}

RegisterBeginResponse contains the WebAuthn registration options.

type RegisterFinishRequest added in v1.9.0

type RegisterFinishRequest struct{}

RegisterFinishRequest is the raw attestation response from the browser.

type RegisterFinishResponse added in v1.9.0

type RegisterFinishResponse struct {
	ID          string `json:"id"`
	DisplayName string `json:"display_name"`
	Status      string `json:"status"`
}

RegisterFinishResponse confirms credential registration.

type SqliteStore added in v1.9.0

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

SqliteStore implements passkey.Store using the Grove ORM with SQLite.

func NewSqliteStore added in v1.9.0

func NewSqliteStore(db *grove.DB) *SqliteStore

NewSqliteStore creates a new SQLite-backed passkey store.

func (*SqliteStore) CreateCredential added in v1.9.0

func (s *SqliteStore) CreateCredential(ctx context.Context, c *Credential) error

func (*SqliteStore) DeleteCredential added in v1.9.0

func (s *SqliteStore) DeleteCredential(ctx context.Context, credentialID []byte) error

func (*SqliteStore) GetCredential added in v1.9.0

func (s *SqliteStore) GetCredential(ctx context.Context, credentialID []byte) (*Credential, error)

func (*SqliteStore) ListUserCredentials added in v1.9.0

func (s *SqliteStore) ListUserCredentials(ctx context.Context, userID id.UserID) ([]*Credential, error)

func (*SqliteStore) UpdateSignCount added in v1.9.0

func (s *SqliteStore) UpdateSignCount(ctx context.Context, credentialID []byte, count uint32) error

type Store added in v1.9.0

type Store interface {
	CreateCredential(ctx context.Context, c *Credential) error
	GetCredential(ctx context.Context, credentialID []byte) (*Credential, error)
	ListUserCredentials(ctx context.Context, userID id.UserID) ([]*Credential, error)
	DeleteCredential(ctx context.Context, credentialID []byte) error
	UpdateSignCount(ctx context.Context, credentialID []byte, count uint32) error
}

Store persists passkey/WebAuthn credential data.

Directories

Path Synopsis
Package contract wires the passkey plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the passkey plugin's deep-link settings page into the forge-dashboard contract registry.
Package passkeytest provides a backend-agnostic conformance suite for the passkey.Store interface.
Package passkeytest provides a backend-agnostic conformance suite for the passkey.Store interface.

Jump to

Keyboard shortcuts

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