auth

package module
v0.2.7 Latest Latest
Warning

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

Go to latest
Published: Apr 1, 2026 License: MIT Imports: 18 Imported by: 0

README

axon-auth

Domain package · Part of the lamina workspace

WebAuthn-based authentication with passkey registration, login, session management, and invite-based user onboarding. Defines store interfaces (UserStore, SessionStore, PasskeyStore, InviteStore) so any persistence backend can be plugged in. HTTP handlers are composed into a Server that mounts onto your existing mux.

Getting started

go get github.com/benaskins/axon-auth@latest

axon-auth is a domain package — it provides types, interfaces, and HTTP handlers but no main function. You assemble it in your own composition root by supplying store implementations and configuration. See example/main.go for a minimal wiring example.

cfg := auth.Config{
    RPID:            "example.com",
    RPDisplayName:   "Example App",
    RPOrigins:       []string{"https://example.com"},
    CookieDomain:    ".example.com",
    SecureCookie:    true,
    SessionDuration: 24 * time.Hour,
    InviteDuration:  7 * 24 * time.Hour,
}

// Use authtest stores for development; supply real implementations in production.
srv, err := auth.NewServer(cfg,
    authtest.NewMemoryUserStore(),
    authtest.NewMemorySessionStore(),
    authtest.NewMemoryPasskeyStore(),
    authtest.NewMemoryInviteStore(),
    nil, // optional embed.FS for static files
)
if err != nil {
    log.Fatal(err)
}

mux := http.NewServeMux()
mux.Handle("/auth/", http.StripPrefix("/auth", srv.Handler()))
log.Fatal(http.ListenAndServe(":8080", mux))

Key types

  • Config — relying party ID, origins, cookie domain, session/invite durations
  • Server — HTTP handler with registration, login, validation, and logout endpoints
  • User, Session, Invite — domain types
  • UserStore, SessionStore, PasskeyStore, InviteStore — persistence interfaces
  • WebAuthnWrapper — WebAuthn protocol wrapper around go-webauthn
  • authtest — in-memory mock stores for testing

License

MIT

Documentation

Overview

Package auth provides WebAuthn-based authentication with passkey registration, login, session management, and invite-based user onboarding. Store interfaces allow pluggable persistence backends.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNotFound          = axon.ErrNotFound
	ErrDuplicateUsername = errors.New("username already taken")
)
View Source
var StaticFiles embed.FS

Functions

func CreateBootstrapInvite

func CreateBootstrapInvite(ctx context.Context, invites InviteStore, email string, duration time.Duration) (string, error)

CreateBootstrapInvite creates an admin bootstrap invite for the given email. It returns the plaintext token for use in a registration URL.

func GenerateToken

func GenerateToken() (plaintext, hash string, err error)

GenerateToken creates a cryptographically random token and returns both the plaintext (for cookies) and the SHA-256 hash (for storage).

func HashToken

func HashToken(token string) string

HashToken returns the SHA-256 hash of a token, base64url-encoded.

func PrintBootstrapURL

func PrintBootstrapURL(baseURL, token string)

PrintBootstrapURL prints the registration URL for a bootstrap invite.

Types

type Config

type Config struct {
	RPID            string
	RPDisplayName   string
	RPOrigins       []string
	CookieDomain    string
	SecureCookie    bool
	BaseURL         string
	SessionDuration time.Duration
	InviteDuration  time.Duration
	InternalAPIKey  string
}

type Invite

type Invite struct {
	ID          string
	Email       string
	TokenHash   string
	IsBootstrap bool
	Used        bool
	CreatedAt   time.Time
	ExpiresAt   time.Time
}

type InviteStore

type InviteStore interface {
	CreateInvite(ctx context.Context, email, tokenHash string, expiresAt time.Time, isBootstrap bool) (*Invite, error)
	ValidateInviteByHash(ctx context.Context, tokenHash string) (*Invite, error)
	MarkInviteUsedByHash(ctx context.Context, tokenHash string) error
	CleanExpiredInvites(ctx context.Context) error
}

type PasskeyStore

type PasskeyStore interface {
	SavePasskey(ctx context.Context, userID string, credential *webauthn.Credential, deviceName string) error
	GetUserPasskeys(ctx context.Context, userID string) ([]webauthn.Credential, error)
	UpdateSignCount(ctx context.Context, credentialID []byte, signCount uint32) error
	DeletePasskey(ctx context.Context, credentialID []byte) error
}

type Server

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

func NewServer

func NewServer(cfg Config, users UserStore, sessions SessionStore, passkeys PasskeyStore, invites InviteStore, staticFiles *embed.FS) (*Server, error)

func (*Server) Handler

func (s *Server) Handler() http.Handler

type Session

type Session struct {
	ID         string
	UserID     string
	TokenHash  string
	ExpiresAt  time.Time
	CreatedAt  time.Time
	LastUsedAt time.Time
}

type SessionStore

type SessionStore interface {
	CreateSession(ctx context.Context, userID, tokenHash string, expiresAt time.Time) (*Session, error)
	ValidateSessionByHash(ctx context.Context, tokenHash string) (*Session, error)
	DeleteSessionByHash(ctx context.Context, tokenHash string) error
	DeleteUserSessions(ctx context.Context, userID string) error
	CleanExpiredSessions(ctx context.Context) error
}

type User

type User struct {
	ID          string
	Username    string
	Email       string
	DisplayName string
	IsAdmin     bool
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

type UserStore

type UserStore interface {
	CreateUser(ctx context.Context, username, email, displayName string, isAdmin bool) (*User, error)
	GetUserByEmail(ctx context.Context, email string) (*User, error)
	GetUserByUsername(ctx context.Context, username string) (*User, error)
	GetUserByID(ctx context.Context, id string) (*User, error)
	ListUsers(ctx context.Context) ([]*User, error)
	DeleteUser(ctx context.Context, id string) error
	SetAdmin(ctx context.Context, id string, isAdmin bool) error
}

type WebAuthnWrapper

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

func NewWebAuthnWrapper

func NewWebAuthnWrapper(rpID, rpName string, rpOrigins []string) (*WebAuthnWrapper, error)

func (*WebAuthnWrapper) BeginLogin

func (w *WebAuthnWrapper) BeginLogin(user *User, credentials []webauthn.Credential) (*protocol.CredentialAssertion, *webauthn.SessionData, error)

func (*WebAuthnWrapper) BeginRegistration

func (w *WebAuthnWrapper) BeginRegistration(user *User) (*protocol.CredentialCreation, *webauthn.SessionData, error)

func (*WebAuthnWrapper) FinishLogin

func (w *WebAuthnWrapper) FinishLogin(user *User, sessionData webauthn.SessionData, response *protocol.ParsedCredentialAssertionData, credentials []webauthn.Credential) (*webauthn.Credential, error)

func (*WebAuthnWrapper) FinishRegistration

func (w *WebAuthnWrapper) FinishRegistration(user *User, sessionData webauthn.SessionData, response *protocol.ParsedCredentialCreationData) (*webauthn.Credential, error)

Directories

Path Synopsis
Package authtest provides in-memory mock implementations of auth store interfaces for testing without a database.
Package authtest provides in-memory mock implementations of auth store interfaces for testing without a database.

Jump to

Keyboard shortcuts

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