auth

package
v0.59.1 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2026 License: AGPL-3.0 Imports: 11 Imported by: 0

Documentation

Overview

Package auth is passkey (WebAuthn) registration and login: no password ever stored or transmitted, a session cookie once a ceremony succeeds, and the store both ceremonies and sessions persist to (SQLite).

Index

Constants

View Source
const CookieName = "forge_dashboard_session"

CookieName is the session cookie forge-dashboard sets and reads. Its value is an opaque token — Store.UserForSession is what resolves it to a user, never anything decoded client-side.

View Source
const SessionTTL = 30 * 24 * time.Hour

SessionTTL is how long an issued session stays valid.

Variables

View Source
var ErrAlreadyRegistered = errors.New("auth: username already registered")

ErrAlreadyRegistered is returned by BeginRegistration when the username is already taken.

View Source
var ErrNotFound = errors.New("auth: not found")

ErrNotFound is returned by a lookup that found nothing — a sentinel rather than a typed error, since every caller only ever needs to know which kind, not reach anything out of it.

Functions

func ClearSessionCookie

func ClearSessionCookie(w http.ResponseWriter, secure bool)

ClearSessionCookie expires the session cookie immediately — logout.

func IsHTTPS

func IsHTTPS(r *http.Request) bool

IsHTTPS reports whether r itself arrived over TLS, directly or via a reverse proxy that sets the standard forwarded-proto header (Traefik, in front of this service's own deployment, terminates TLS and always sets it) — the deciding factor for whether the session cookie can carry Secure without locking out a plain-HTTP local dev run.

func RequireAdmin added in v0.9.0

func RequireAdmin(next http.Handler) http.Handler

RequireAdmin wraps next so it only ever runs for the designated admin — everyone else gets a 403. Compose it inside RequireAuth (RequireAuth(store)(RequireAdmin(next))), since it reads the user RequireAuth already put in context rather than looking one up itself.

func RequireAuth

func RequireAuth(store *Store) func(http.Handler) http.Handler

RequireAuth wraps next so it only ever runs for a request carrying a valid, unexpired session — everyone else gets a 401 JSON body, never the handler underneath.

func SessionToken

func SessionToken(r *http.Request) (string, bool)

SessionToken reads the session cookie from r, if any.

func SetSessionCookie

func SetSessionCookie(w http.ResponseWriter, token string, secure bool)

SetSessionCookie sets token as the session cookie on w. secure mirrors whether the request itself arrived over HTTPS — see isHTTPS, called by the handler that has the *http.Request this cookie is a response to.

Types

type Service

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

Service drives WebAuthn registration and login ceremonies against the configured relying party, persisting every step through Store.

func NewService

func NewService(wa *webauthn.WebAuthn, store *Store) *Service

NewService returns a Service.

func (*Service) BeginLogin

func (s *Service) BeginLogin(ctx context.Context, username string) (*protocol.CredentialAssertion, error)

BeginLogin starts a login ceremony for an existing username.

func (*Service) BeginRegistration

func (s *Service) BeginRegistration(ctx context.Context, username, displayName string) (*protocol.CredentialCreation, error)

BeginRegistration starts a registration ceremony for a brand-new username. Returns ErrAlreadyRegistered if that username already has an account.

func (*Service) CreateSession

func (s *Service) CreateSession(ctx context.Context, u *User) (token string, err error)

CreateSession issues a session token for u, ready to set as a cookie.

func (*Service) FinishLogin

func (s *Service) FinishLogin(ctx context.Context, username string, r *http.Request) (*User, error)

FinishLogin completes a login ceremony, persisting the authenticator's bumped signature counter so a cloned authenticator is caught on a later login.

func (*Service) FinishRegistration

func (s *Service) FinishRegistration(ctx context.Context, username string, r *http.Request) (*User, error)

FinishRegistration completes a registration ceremony begun for username, attaching the new passkey to the account BeginRegistration already created.

type Store

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

Store persists users, their passkey credentials, in-flight WebAuthn ceremonies, and sessions. It owns no driver of its own — cmd wires up whichever database/sql driver is registered (modernc.org/sqlite) and hands Store a plain *sql.DB.

func NewStore

func NewStore(db *sql.DB) *Store

NewStore wraps db. Init must be called once before any other method.

func (*Store) AddCredential

func (s *Store) AddCredential(ctx context.Context, userID []byte, cred webauthn.Credential) error

AddCredential appends cred to userID's stored credentials — a brand-new passkey, from a just-finished registration ceremony.

func (*Store) CreateSession

func (s *Store) CreateSession(ctx context.Context, userID []byte, ttl time.Duration) (token string, err error)

CreateSession issues a new opaque session token for userID, valid for ttl.

func (*Store) CreateUser

func (s *Store) CreateUser(ctx context.Context, username, displayName string, isAdmin bool) (*User, error)

CreateUser registers a brand-new account with no credentials yet — AddCredential attaches the first one once the registration ceremony that follows actually succeeds.

func (*Store) DeleteCeremony

func (s *Store) DeleteCeremony(ctx context.Context, username, kind string) error

DeleteCeremony clears the in-flight ceremony once it's finished, win or lose — it's single-use either way.

func (*Store) DeleteSession

func (s *Store) DeleteSession(ctx context.Context, token string) error

DeleteSession revokes a session token — logout, or an admin revoking another user's session.

func (*Store) DeleteUnregisteredUser added in v0.7.1

func (s *Store) DeleteUnregisteredUser(ctx context.Context, username string) error

DeleteUnregisteredUser removes username's account if — and only if — it has no credentials attached: an abandoned registration (begun, never finished, so nothing was ever actually attached to the username) rather than a real account. A no-op if the account has since gained a credential, or doesn't exist at all.

func (*Store) DeleteUser added in v0.9.0

func (s *Store) DeleteUser(ctx context.Context, userID []byte) error

DeleteUser removes the account and every session it holds — irreversible, and the caller's job to also clean up anything outside this store (saved forge credentials, a running dashboard refresh).

func (*Store) GetUserByID

func (s *Store) GetUserByID(ctx context.Context, id []byte) (*User, error)

GetUserByID returns ErrNotFound when no such user is registered. id is the raw WebAuthn user handle, the same bytes User.ID holds.

func (*Store) GetUserByUsername

func (s *Store) GetUserByUsername(ctx context.Context, username string) (*User, error)

GetUserByUsername returns ErrNotFound when no such user is registered.

func (*Store) HasAnyRegisteredUser added in v0.11.0

func (s *Store) HasAnyRegisteredUser(ctx context.Context) (bool, error)

HasAnyRegisteredUser reports whether any account has completed registration — a row with no credentials (BeginRegistration begun, never finished) doesn't count. The first username to make this true is the one that becomes admin.

func (*Store) Init

func (s *Store) Init(ctx context.Context) error

Init creates the schema if it doesn't already exist. Safe to call every startup.

func (*Store) ListUsers added in v0.9.0

func (s *Store) ListUsers(ctx context.Context) ([]*User, error)

ListUsers returns every registered account, for the admin area.

func (*Store) LoadCeremony

func (s *Store) LoadCeremony(ctx context.Context, username, kind string) (webauthn.SessionData, error)

LoadCeremony returns ErrNotFound when there's no in-flight ceremony of that kind for username, or it already expired.

func (*Store) RevokeUser added in v0.9.0

func (s *Store) RevokeUser(ctx context.Context, userID []byte) error

RevokeUser signs userID out everywhere and clears every registered passkey, without deleting the account itself — they have to register a new passkey from scratch to get back in, same as BeginRegistration reclaiming an abandoned one.

func (*Store) SaveCeremony

func (s *Store) SaveCeremony(ctx context.Context, username, kind string, session webauthn.SessionData, ttl time.Duration) error

SaveCeremony records the in-flight WebAuthn session data for username's registration or login attempt, replacing any earlier one of the same kind — a user can only ever be partway through one ceremony at a time.

func (*Store) UpdateCredential

func (s *Store) UpdateCredential(ctx context.Context, userID []byte, cred webauthn.Credential) error

UpdateCredential replaces the stored credential sharing cred's ID — FinishLogin bumps the authenticator's signature counter on every successful login, and that has to be persisted to catch a cloned authenticator on a later login.

func (*Store) UserForSession

func (s *Store) UserForSession(ctx context.Context, token string) (*User, error)

UserForSession returns the user a valid, unexpired session token belongs to, or ErrNotFound.

type User

type User struct {
	ID          []byte
	Username    string
	DisplayName string
	IsAdmin     bool
	Credentials []webauthn.Credential
	CreatedAt   time.Time
}

User is a registered account. It implements webauthn.User directly, so it can be handed straight to the library's Begin/Finish calls with no adapter.

func UserFromContext

func UserFromContext(ctx context.Context) (*User, bool)

UserFromContext returns the authenticated user a RequireAuth-wrapped handler is running for. Only ever called from inside such a handler, so the zero value (found false) means RequireAuth itself has a bug, not a condition a caller needs to handle gracefully.

func (*User) WebAuthnCredentials

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

WebAuthnCredentials satisfies webauthn.User with every passkey on file.

func (*User) WebAuthnDisplayName

func (u *User) WebAuthnDisplayName() string

WebAuthnDisplayName satisfies webauthn.User with the account's display name.

func (*User) WebAuthnID

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

WebAuthnID satisfies webauthn.User with the account's random handle.

func (*User) WebAuthnName

func (u *User) WebAuthnName() string

WebAuthnName satisfies webauthn.User with the account's username.

Jump to

Keyboard shortcuts

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