passkeys

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 31 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateUserID

func GenerateUserID() ([]byte, error)

GenerateUserID creates a random 32-byte user handle.

func RegisterShellCommand

func RegisterShellCommand(pk *Passkeys, baseURL string)

RegisterShellCommand registers the `passkey` shell command backed by the given Passkeys instance. baseURL is the public HTTP origin used to build the registration link printed to the agent.

Types

type Config

type Config struct {
	Enabled      bool
	RPID         string
	RPName       string
	RPOrigins    []string
	LorePath     string
	PasskeysFile string
	SessionTTL   time.Duration
}

Config holds the resolved passkey configuration.

type Passkeys

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

Passkeys orchestrates WebAuthn registration and login ceremonies and serves the HTTP endpoints that drive them. Credentials persist to a JSON file that agents can read and edit directly via the shell.

func New

func New(cfg Config, sessionKey []byte, logger *slog.Logger) (*Passkeys, error)

New constructs a Passkeys instance. The sessionKey seeds the HMAC used to sign browser session cookies.

func (*Passkeys) LoreBrowserHandler

func (p *Passkeys) LoreBrowserHandler(fsForIdentity func(identity string) vfs.FileSystem) http.Handler

LoreBrowserHandler serves an authenticated web browser over the filesystem. Unauthenticated requests are redirected to the passkey login page.

fsForIdentity returns the per-identity, read-scoped filesystem for a resolved session identity — the SAME layered session FS used by the SSH shell, SFTP, and MCP/HTTP transports. The browser performs all Stat/ReadDir/ReadFile calls through that scoped FS, so docset boundaries (including the carve-out of nested docsets from an ancestor grant) are enforced identically here. There is no separate allow-list in the browser: the scoped FS is the sole authority on what a session may see.

func (*Passkeys) RegisterHTTPHandlers

func (p *Passkeys) RegisterHTTPHandlers(mux *http.ServeMux)

RegisterHTTPHandlers implements httpserver.MuxExtender. It mounts the passkey registration and login ceremony endpoints.

func (*Passkeys) SetAuthConfig

func (p *Passkeys) SetAuthConfig(auth *config.AuthConfig)

SetAuthConfig provides the auth config used to map a lore spec to the docset paths a browser session may view.

func (*Passkeys) SetTokenIssuer added in v0.2.0

func (p *Passkeys) SetTokenIssuer(ti TokenIssuer)

SetTokenIssuer wires the token-minting seam used by the login-success hook to issue bearer tokens (docs/mcp-bearer-auth.md §8.2). When nil, login only sets the browser session cookie.

func (*Passkeys) Shutdown

func (p *Passkeys) Shutdown()

Shutdown stops background goroutines.

type PendingRegistration

type PendingRegistration struct {
	Token     string
	Identity  string
	Name      string
	UserID    []byte
	Session   *webauthn.SessionData
	ExpiresAt time.Time
}

PendingRegistration represents an in-flight passkey registration.

type PendingStore

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

PendingStore holds pending registrations in memory with automatic expiry.

func NewPendingStore

func NewPendingStore() *PendingStore

NewPendingStore creates a new in-memory pending registration store.

func (*PendingStore) Create

func (ps *PendingStore) Create(identity, passkeyName string) (*PendingRegistration, error)

Create generates a new pending registration and returns it.

func (*PendingStore) Delete

func (ps *PendingStore) Delete(token string)

Delete removes a pending registration.

func (*PendingStore) Get

func (ps *PendingStore) Get(token string) *PendingRegistration

Get retrieves a pending registration by token. Returns nil if not found or expired.

func (*PendingStore) Stop

func (ps *PendingStore) Stop()

Stop shuts down the cleanup goroutine.

type SessionInfo

type SessionInfo struct {
	Identity  string
	ExpiresAt time.Time
}

SessionInfo holds the decoded session values.

type SessionManager

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

SessionManager handles HMAC-signed session cookies.

func NewSessionManager

func NewSessionManager(key []byte, ttl time.Duration) *SessionManager

NewSessionManager creates a session manager keyed from the given secret.

func (*SessionManager) ClearCookie

func (sm *SessionManager) ClearCookie(w http.ResponseWriter)

ClearCookie removes the session cookie.

func (*SessionManager) SetCookie

func (sm *SessionManager) SetCookie(w http.ResponseWriter, identity string)

SetCookie creates and sets a signed session cookie on the response.

func (*SessionManager) ValidateRequest

func (sm *SessionManager) ValidateRequest(r *http.Request) (*SessionInfo, bool)

ValidateRequest checks the session cookie and returns session info if valid.

type Store

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

Store manages passkey credentials on disk as a JSON file.

func NewStore

func NewStore(path string) (*Store, error)

NewStore creates or loads a passkey store from the given file path.

func (*Store) Add

func (s *Store) Add(cred StoredCredential) error

Add persists a new credential.

func (*Store) AllCredentials

func (s *Store) AllCredentials() []StoredCredential

AllCredentials returns all stored credentials.

func (*Store) FindByCredentialID

func (s *Store) FindByCredentialID(credID []byte) (*StoredCredential, bool)

FindByCredentialID returns the stored credential matching the given WebAuthn credential ID.

func (*Store) FindByUserID

func (s *Store) FindByUserID(userID []byte) (*StoredCredential, bool)

FindByUserID returns the stored credential matching the given user handle.

func (*Store) Remove

func (s *Store) Remove(name string) (bool, error)

Remove deletes a credential by name. Returns true if found and removed.

func (*Store) UpdateSignCount

func (s *Store) UpdateSignCount(credID []byte, newCount uint32) error

UpdateSignCount updates the sign count for a credential after successful auth.

type StoreData

type StoreData struct {
	Credentials []StoredCredential `json:"credentials"`
}

StoreData is the on-disk JSON format.

type StoredCredential

type StoredCredential struct {
	// UserID is the WebAuthn user handle (random bytes, base64url-encoded in JSON).
	UserID []byte `json:"user_id"`
	// Name is a human-readable device label for this passkey.
	Name string `json:"name"`
	// Identity is the OpenLore identity name this passkey authenticates as. It
	// becomes the token `sub` at login, from which authority (lore, capabilities,
	// home) is resolved live via the identity table (docs/mcp-bearer-auth.md §7).
	Identity string `json:"identity"`
	// CreatedAt is when the passkey was registered.
	CreatedAt time.Time `json:"created_at"`
	// Credential is the WebAuthn credential data.
	Credential webauthn.Credential `json:"credential"`
}

StoredCredential wraps a webauthn.Credential with metadata.

type TokenIssuer added in v0.2.0

type TokenIssuer interface {
	// IdentityExists reports whether name is a registered identity in the auth
	// table. Registration references an identity by name (Q6/§8.3), so this
	// validates the target at register time.
	IdentityExists(name string) bool
	// IssueAuthCode mints a single-use OAuth authorization code for sub, to be
	// exchanged at /oauth/token. ok is false when token auth is disabled, in
	// which case login still sets the browser cookie but issues no bearer token.
	IssueAuthCode(sub, scope string) (code string, ok bool)
	// CompleteAuthorize finalizes an in-flight OAuth authorization-code request
	// (started at GET /authorize) for the authenticated sub, returning the
	// redirect URL (redirect_uri?code=&state=) the browser should navigate to.
	// ok is false when the request id is unknown/expired or token auth is off.
	CompleteAuthorize(requestID, sub string) (redirectURL string, ok bool)
}

TokenIssuer is the seam through which a successful passkey login mints a bearer token for the MCP + HTTP API. pkg/openlore injects a *Server here; internal/passkeys cannot import pkg/openlore (import cycle), so the contract lives on this side. See docs/mcp-bearer-auth.md §8.2.

Jump to

Keyboard shortcuts

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