passkeys

package
v0.0.0-...-aca82e5 Latest Latest
Warning

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

Go to latest
Published: Jan 29, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

README

Package cloudeng.io/webapp/webauth/webauthn/passkeys

import cloudeng.io/webapp/webauth/webauthn/passkeys

Package passkeys provides support for creating and authenticating WebAuthn passkeys.

Constants

AuthenticationCookie, RegistrationCookie
// AuthenticationCookie is set during the login/authentication
// webauthn flow (set in Begin and cleared in Finish).
AuthenticationCookie = cookies.Secure("webauthn_authentication")
// RegistrationCookie is set during the registration webauthn flow
// (set in Begin and cleared in Finish).
RegistrationCookie = cookies.Secure("webauthn_registration")

Variables

BeginDiscoverableAuthenticationEndpoint
BeginDiscoverableAuthenticationEndpoint = jsonapi.Endpoint[struct{}, *protocol.CredentialAssertion]{}

BeginDiscoverableAuthenticationEndpoint represents the endpoint for beginning the authentication using a discoverable passkey. The user's identity will be determined by the user handle provided in the request. The response will contain the options for the authentication request.

BeginRegistrationEndpoint
BeginRegistrationEndpoint = jsonapi.Endpoint[
	BeginRegistrationRequest,
	*protocol.PublicKeyCredentialCreationOptions]{}

BeginRegistrationEndpoint represents the endpoint for beginning the registration process.

FinishAuthenticationEndpoint
FinishAuthenticationEndpoint = jsonapi.Endpoint[struct{}, struct{}]{}

FinishAuthenticationEndpoint represents the endpoint for finishing the authentication process. It expects a request with a JSON body containing the verification data as expected by the webauthn.FinishDiscoverableLogin method, this method parses the request directly and hence the ParseRequest is not used. The response on success is simply a http.StatusOK with an empty body. Strictly speaking this variable is not used but serves to document the endpoint.

FinishRegistrationEndpoint
FinishRegistrationEndpoint = jsonapi.Endpoint[struct{}, struct{}]{}

FinishRegistrationEndpoint represents the endpoint for finishing the registration process. It expects a request with a JSON body containing the verification data as expected by the webauthn.FinishRegistration method, this method parses the request directly and hence the ParseRequest is not used. The response on success is simply a http.StatusOK with an empty body. Strictly speaking this variable is not used but serves to document the endpoint.

VerifyAuthenticationEndpoint
VerifyAuthenticationEndpoint = jsonapi.Endpoint[struct{}, struct{}]{}

VerifyAuthenticationEndpoint represents the endpoint for verifying the authentication of a user. It expects the user to be authenticated and to have an entry in the user database.

Types

Type BeginRegistrationRequest
type BeginRegistrationRequest struct {
	Email       string `json:"email"`
	DisplayName string `json:"display_name"`
}

BeginRegistrationRequest represents the request body for beginning the registration process, the client should send a JSON object with the user's email address and display name.

Type EmailValidator
type EmailValidator interface {
	Validate(email string) error
}

EmailValidator defines an interface for validating email addresses.

Type Handler
type Handler struct {
	// contains filtered or unexported fields
}

Handler provides http Handlers that implement passkey registration and authentication using the WebAuthn protocol. These endpoints accept JSON requests and responses.

Functions
func NewHandler(w WebAuthn, sm SessionManager, um UserDatabase, lm LoginManager, opts ...HandlerOption) *Handler

NewHandler creates a new passkeys handler with the provided WebAuthn implementation, session and user managers.

Methods
func (h *Handler) BeginDiscoverableAuthentication(rw http.ResponseWriter, _ *http.Request)
func (h *Handler) BeginRegistration(rw http.ResponseWriter, r *http.Request)

BeginRegistration starts the registration process for a user. It expects a request with a JSON body containing the user's email address.

func (h *Handler) FinishAuthentication(rw http.ResponseWriter, r *http.Request)
func (h *Handler) FinishRegistration(rw http.ResponseWriter, r *http.Request)
func (h *Handler) VerifyAuthentication(rw http.ResponseWriter, r *http.Request)
Type HandlerOption
type HandlerOption func(*options)

HandlerOption represents an option for configuring the Handler.

Functions
func WithEmailValidator(validator EmailValidator) HandlerOption

WithEmailValidator sets the email validator for the handler.

func WithLogger(logger *slog.Logger) HandlerOption

WithLogger sets the logger for the handler. The default is discard log output.

func WithMediation(mediation protocol.CredentialMediationRequirement) HandlerOption

WithMediation sets the mediation requirement for the handler.

func WithRegistrationOptions(opts ...webauthn.RegistrationOption) HandlerOption

WithRegistrationOptions sets the registration options for the handler.

func WithSessionCookieScopeAndDuration(ck cookies.ScopeAndDuration) HandlerOption

WithSessionCookieScopeAndDuration sets the session cookie's scope (domain, path) and duration.

Type JWTCookieLoginManager
type JWTCookieLoginManager struct {

	// LoginCookie is set when the user has successfully logged in using
	// webauthn and is used to inform the server that the user has
	// successfully logged in
	LoginCookie cookies.Secure // initialized as cookies.T("webauthn_login")
	// contains filtered or unexported fields
}

JWTCookieLoginManager implements the LoginManager interface using JWTs stored in cookies.

Functions
func NewJWTCookieLoginManager(signer jwtutil.Signer, issuer string, cookie cookies.ScopeAndDuration) JWTCookieLoginManager

NewJWTCookieLoginManager creates a new JWTCookieLoginManager instance.

Methods
func (m JWTCookieLoginManager) AuthenticateUser(r *http.Request) (UserID, error)
func (m JWTCookieLoginManager) UserAuthenticated(r *http.Request, rw http.ResponseWriter, user UserID) error
Type LoginManager
type LoginManager interface {
	// UserAuthenticated is called after a user has successfully logged in with a passkey.
	// It should be used to set a session Cookie, or a JWT token to be validated
	// on subsequent requests. The expiration parameter indicates how long the
	// login session should be valid.
	UserAuthenticated(r *http.Request, rw http.ResponseWriter, user UserID) error

	// AuthenticateUser is called to validate the user based on the request.
	// It should return the UserID of the authenticated user or an error if authentication fails.
	AuthenticateUser(r *http.Request) (UserID, error)
}

LoginManager defines the interface for managing logged in users who have authenticated using a passkey.

Type RAMUserDatabase
type RAMUserDatabase struct {
	// contains filtered or unexported fields
}
Functions
func NewRAMUserDatabase() *RAMUserDatabase
Methods
func (sm RAMUserDatabase) Authenticated(tmpKey string) (sessionData *webauthn.SessionData, err error)
func (sm RAMUserDatabase) Authenticating(sessionData *webauthn.SessionData) (tmpKey string, err error)
func (um RAMUserDatabase) Lookup(userID UserID) (*User, error)
func (sm RAMUserDatabase) Registered(tmpKey string) (user *User, sessionData *webauthn.SessionData, err error)
func (sm RAMUserDatabase) Registering(user *User, sessionData *webauthn.SessionData) (tmpKey string, exists bool, err error)
func (um RAMUserDatabase) Store(user *User) error
Type SessionManager
type SessionManager interface {
	// Used when creating a new passkey.
	Registering(user *User, sessionData *webauthn.SessionData) (tmpKey string, exists bool, err error)
	Registered(tmpKey string) (user *User, sessionData *webauthn.SessionData, err error)

	// Used when authenticating a passkey.
	Authenticating(sessionData *webauthn.SessionData) (tmpKey string, err error)
	Authenticated(tmpKey string) (sessionData *webauthn.SessionData, err error)
}

SessionManager is the interface used by passkeys.Server to manage state between 'begin' and 'finish' registration and authentication requests.

Type User
type User struct {
	// contains filtered or unexported fields
}

User represents a user that registers to use a passkey and implements webauthn.User

Functions
func NewUser(email, displayName string) (*User, error)

NewUser creates a new user with the given email and display name.

Methods
func (u *User) AddCredential(cred webauthn.Credential)

Implements webauthn.User.

func (u *User) ID() UserID

ID returns the unique identifier for the user.

func (u User) ParseUID(uid string) (UserID, error)

ParseUID parses a string representation of a UserID and returns the UserID. It returns an error if the string cannot be parsed. It is required to parse a UserID into the implementation of UserID used by the User struct.

func (u *User) UpdateCredential(cred webauthn.Credential) bool

UpdateCredential updates an existing credential for the user.

func (u *User) WebAuthnCredentials() []webauthn.Credential

Implements webauthn.User.

func (u *User) WebAuthnDisplayName() string

Implements webauthn.User.

func (u *User) WebAuthnID() []byte

Implements webauthn.User.

func (u *User) WebAuthnName() string

Implements webauthn.User.

Type UserDatabase
type UserDatabase interface {

	// Store persists the user in the database, using the user.ID().String() as the key.
	Store(user *User) error

	// Lookup retrieves a user using the UUID it was original created with.
	Lookup(uid UserID) (*User, error)
}

UserDatabase is an interface for a user database that supports registering and authenticating passkeys.

Type UserID
type UserID interface {
	String() string               // Returns a string representation of the user ID that can be used usable as a key in a map. String should return the same value as MarshalText and hence UnmarshalText(String()) == UnmarshalText(MarshalText()).
	UnmarshalBinary([]byte) error // Converts a byte slice to a UserID.
	MarshalText() ([]byte, error) // Converts the UserID to base64.RawURLEncoding representation.
	UnmarshalText([]byte) error   // Converts a base64.RawURLEncoding text representation to a UserID.
}

UserID is used to uniquely identify users in the passkey system. It must be a cryptographically secure randomly generated value, (eg. 64 bytes read crypto.rand.Reader).

Functions
func UserIDFromBytes(b []byte) (UserID, error)

UserIDFromBytes creates a UserID from a byte slice.

func UserIDFromString(s string) (UserID, error)

UserIDFromString creates a UserID from a base64.RawURLEncoding string.

Type WebAuthn
type WebAuthn interface {
	BeginMediatedRegistration(user webauthn.User, mediation protocol.CredentialMediationRequirement, opts ...webauthn.RegistrationOption) (creation *protocol.CredentialCreation, session *webauthn.SessionData, err error)
	FinishRegistration(user webauthn.User, session webauthn.SessionData, r *http.Request) (*webauthn.Credential, error)
	BeginDiscoverableMediatedLogin(mediation protocol.CredentialMediationRequirement, opts ...webauthn.LoginOption) (*protocol.CredentialAssertion, *webauthn.SessionData, error)
	FinishPasskeyLogin(handler webauthn.DiscoverableUserHandler, session webauthn.SessionData, request *http.Request) (user webauthn.User, credential *webauthn.Credential, err error)
}

WebAuthn defines the subset of webauthn.WebAuthn used by this package.

Documentation

Overview

Package passkeys provides support for creating and authenticating WebAuthn passkeys.

Index

Constants

View Source
const (
	// AuthenticationCookie is set during the login/authentication
	// webauthn flow (set in Begin and cleared in Finish).
	AuthenticationCookie = cookies.Secure("webauthn_authentication")
	// RegistrationCookie is set during the registration webauthn flow
	// (set in Begin and cleared in Finish).
	RegistrationCookie = cookies.Secure("webauthn_registration")
)

Variables

View Source
var BeginDiscoverableAuthenticationEndpoint = jsonapi.Endpoint[struct{}, *protocol.CredentialAssertion]{}

BeginDiscoverableAuthenticationEndpoint represents the endpoint for beginning the authentication using a discoverable passkey. The user's identity will be determined by the user handle provided in the request. The response will contain the options for the authentication request.

BeginRegistrationEndpoint represents the endpoint for beginning the registration process.

View Source
var FinishAuthenticationEndpoint = jsonapi.Endpoint[struct{}, struct{}]{}

FinishAuthenticationEndpoint represents the endpoint for finishing the authentication process. It expects a request with a JSON body containing the verification data as expected by the webauthn.FinishDiscoverableLogin method, this method parses the request directly and hence the ParseRequest is not used. The response on success is simply a http.StatusOK with an empty body. Strictly speaking this variable is not used but serves to document the endpoint.

View Source
var FinishRegistrationEndpoint = jsonapi.Endpoint[struct{}, struct{}]{}

FinishRegistrationEndpoint represents the endpoint for finishing the registration process. It expects a request with a JSON body containing the verification data as expected by the webauthn.FinishRegistration method, this method parses the request directly and hence the ParseRequest is not used. The response on success is simply a http.StatusOK with an empty body. Strictly speaking this variable is not used but serves to document the endpoint.

View Source
var VerifyAuthenticationEndpoint = jsonapi.Endpoint[struct{}, struct{}]{}

VerifyAuthenticationEndpoint represents the endpoint for verifying the authentication of a user. It expects the user to be authenticated and to have an entry in the user database.

Functions

This section is empty.

Types

type BeginRegistrationRequest

type BeginRegistrationRequest struct {
	Email       string `json:"email"`
	DisplayName string `json:"display_name"`
}

BeginRegistrationRequest represents the request body for beginning the registration process, the client should send a JSON object with the user's email address and display name.

type EmailValidator

type EmailValidator interface {
	Validate(email string) error
}

EmailValidator defines an interface for validating email addresses.

type Handler

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

Handler provides http Handlers that implement passkey registration and authentication using the WebAuthn protocol. These endpoints accept JSON requests and responses.

func NewHandler

func NewHandler(w WebAuthn, sm SessionManager, um UserDatabase, lm LoginManager, opts ...HandlerOption) *Handler

NewHandler creates a new passkeys handler with the provided WebAuthn implementation, session and user managers.

func (*Handler) BeginDiscoverableAuthentication

func (h *Handler) BeginDiscoverableAuthentication(rw http.ResponseWriter, _ *http.Request)

func (*Handler) BeginRegistration

func (h *Handler) BeginRegistration(rw http.ResponseWriter, r *http.Request)

BeginRegistration starts the registration process for a user. It expects a request with a JSON body containing the user's email address.

func (*Handler) FinishAuthentication

func (h *Handler) FinishAuthentication(rw http.ResponseWriter, r *http.Request)

func (*Handler) FinishRegistration

func (h *Handler) FinishRegistration(rw http.ResponseWriter, r *http.Request)

func (*Handler) VerifyAuthentication

func (h *Handler) VerifyAuthentication(rw http.ResponseWriter, r *http.Request)

type HandlerOption

type HandlerOption func(*options)

HandlerOption represents an option for configuring the Handler.

func WithEmailValidator

func WithEmailValidator(validator EmailValidator) HandlerOption

WithEmailValidator sets the email validator for the handler.

func WithLogger

func WithLogger(logger *slog.Logger) HandlerOption

WithLogger sets the logger for the handler. The default is discard log output.

func WithMediation

func WithMediation(mediation protocol.CredentialMediationRequirement) HandlerOption

WithMediation sets the mediation requirement for the handler.

func WithRegistrationOptions

func WithRegistrationOptions(opts ...webauthn.RegistrationOption) HandlerOption

WithRegistrationOptions sets the registration options for the handler.

func WithSessionCookieScopeAndDuration

func WithSessionCookieScopeAndDuration(ck cookies.ScopeAndDuration) HandlerOption

WithSessionCookieScopeAndDuration sets the session cookie's scope (domain, path) and duration.

type JWTCookieLoginManager

type JWTCookieLoginManager struct {

	// LoginCookie is set when the user has successfully logged in using
	// webauthn and is used to inform the server that the user has
	// successfully logged in
	LoginCookie cookies.Secure // initialized as cookies.T("webauthn_login")
	// contains filtered or unexported fields
}

JWTCookieLoginManager implements the LoginManager interface using JWTs stored in cookies.

func NewJWTCookieLoginManager

func NewJWTCookieLoginManager(signer jwtutil.Signer, issuer string, cookie cookies.ScopeAndDuration) JWTCookieLoginManager

NewJWTCookieLoginManager creates a new JWTCookieLoginManager instance.

func (JWTCookieLoginManager) AuthenticateUser

func (m JWTCookieLoginManager) AuthenticateUser(r *http.Request) (UserID, error)

func (JWTCookieLoginManager) UserAuthenticated

func (m JWTCookieLoginManager) UserAuthenticated(r *http.Request, rw http.ResponseWriter, user UserID) error

type LoginManager

type LoginManager interface {
	// UserAuthenticated is called after a user has successfully logged in with a passkey.
	// It should be used to set a session Cookie, or a JWT token to be validated
	// on subsequent requests. The expiration parameter indicates how long the
	// login session should be valid.
	UserAuthenticated(r *http.Request, rw http.ResponseWriter, user UserID) error

	// AuthenticateUser is called to validate the user based on the request.
	// It should return the UserID of the authenticated user or an error if authentication fails.
	AuthenticateUser(r *http.Request) (UserID, error)
}

LoginManager defines the interface for managing logged in users who have authenticated using a passkey.

type RAMUserDatabase

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

func NewRAMUserDatabase

func NewRAMUserDatabase() *RAMUserDatabase

func (RAMUserDatabase) Authenticated

func (sm RAMUserDatabase) Authenticated(tmpKey string) (sessionData *webauthn.SessionData, err error)

func (RAMUserDatabase) Authenticating

func (sm RAMUserDatabase) Authenticating(sessionData *webauthn.SessionData) (tmpKey string, err error)

func (RAMUserDatabase) Lookup

func (um RAMUserDatabase) Lookup(userID UserID) (*User, error)

func (RAMUserDatabase) Registered

func (sm RAMUserDatabase) Registered(tmpKey string) (user *User, sessionData *webauthn.SessionData, err error)

func (RAMUserDatabase) Registering

func (sm RAMUserDatabase) Registering(user *User, sessionData *webauthn.SessionData) (tmpKey string, exists bool, err error)

func (RAMUserDatabase) Store

func (um RAMUserDatabase) Store(user *User) error

type SessionManager

type SessionManager interface {
	// Used when creating a new passkey.
	Registering(user *User, sessionData *webauthn.SessionData) (tmpKey string, exists bool, err error)
	Registered(tmpKey string) (user *User, sessionData *webauthn.SessionData, err error)

	// Used when authenticating a passkey.
	Authenticating(sessionData *webauthn.SessionData) (tmpKey string, err error)
	Authenticated(tmpKey string) (sessionData *webauthn.SessionData, err error)
}

SessionManager is the interface used by passkeys.Server to manage state between 'begin' and 'finish' registration and authentication requests.

type User

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

User represents a user that registers to use a passkey and implements webauthn.User

func NewUser

func NewUser(email, displayName string) (*User, error)

NewUser creates a new user with the given email and display name.

func (*User) AddCredential

func (u *User) AddCredential(cred webauthn.Credential)

Implements webauthn.User.

func (*User) ID

func (u *User) ID() UserID

ID returns the unique identifier for the user.

func (User) ParseUID

func (u User) ParseUID(uid string) (UserID, error)

ParseUID parses a string representation of a UserID and returns the UserID. It returns an error if the string cannot be parsed. It is required to parse a UserID into the implementation of UserID used by the User struct.

func (*User) UpdateCredential

func (u *User) UpdateCredential(cred webauthn.Credential) bool

UpdateCredential updates an existing credential for the user.

func (*User) WebAuthnCredentials

func (u *User) WebAuthnCredentials() []webauthn.Credential

Implements webauthn.User.

func (*User) WebAuthnDisplayName

func (u *User) WebAuthnDisplayName() string

Implements webauthn.User.

func (*User) WebAuthnID

func (u *User) WebAuthnID() []byte

Implements webauthn.User.

func (*User) WebAuthnName

func (u *User) WebAuthnName() string

Implements webauthn.User.

type UserDatabase

type UserDatabase interface {

	// Store persists the user in the database, using the user.ID().String() as the key.
	Store(user *User) error

	// Lookup retrieves a user using the UUID it was original created with.
	Lookup(uid UserID) (*User, error)
}

UserDatabase is an interface for a user database that supports registering and authenticating passkeys.

type UserID

type UserID interface {
	String() string               // Returns a string representation of the user ID that can be used usable as a key in a map. String should return the same value as MarshalText and hence UnmarshalText(String()) == UnmarshalText(MarshalText()).
	UnmarshalBinary([]byte) error // Converts a byte slice to a UserID.
	MarshalText() ([]byte, error) // Converts the UserID to base64.RawURLEncoding representation.
	UnmarshalText([]byte) error   // Converts a base64.RawURLEncoding text representation to a UserID.
}

UserID is used to uniquely identify users in the passkey system. It must be a cryptographically secure randomly generated value, (eg. 64 bytes read crypto.rand.Reader).

func UserIDFromBytes

func UserIDFromBytes(b []byte) (UserID, error)

UserIDFromBytes creates a UserID from a byte slice.

func UserIDFromString

func UserIDFromString(s string) (UserID, error)

UserIDFromString creates a UserID from a base64.RawURLEncoding string.

type WebAuthn

type WebAuthn interface {
	BeginMediatedRegistration(user webauthn.User, mediation protocol.CredentialMediationRequirement, opts ...webauthn.RegistrationOption) (creation *protocol.CredentialCreation, session *webauthn.SessionData, err error)
	FinishRegistration(user webauthn.User, session webauthn.SessionData, r *http.Request) (*webauthn.Credential, error)
	BeginDiscoverableMediatedLogin(mediation protocol.CredentialMediationRequirement, opts ...webauthn.LoginOption) (*protocol.CredentialAssertion, *webauthn.SessionData, error)
	FinishPasskeyLogin(handler webauthn.DiscoverableUserHandler, session webauthn.SessionData, request *http.Request) (user webauthn.User, credential *webauthn.Credential, err error)
}

WebAuthn defines the subset of webauthn.WebAuthn used by this package.

Jump to

Keyboard shortcuts

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