auth

package
v2.8.5 Latest Latest
Warning

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

Go to latest
Published: Aug 9, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package auth manages Canary device pairing and app-session credentials. Durable device grants and credential hashes belong to the app state store; pairing sessions, challenges, and bearer sessions are process-local, time-bounded values. Callers must treat all raw nonces, secrets, signatures, cookie values, and session tokens as sensitive untrusted input.

Index

Constants

View Source
const SessionTTL = 12 * time.Hour

SessionTTL is the lifetime of an in-memory bearer session minted after successful device authentication.

Variables

This section is empty.

Functions

func VerifyJWKSignature

func VerifyJWKSignature(raw json.RawMessage, message []byte, sigB64 string) error

VerifyJWKSignature verifies a SHA-256 ECDSA signature over message using an EC P-256 public JWK. sigB64 must use unpadded base64url and may contain either a 64-byte raw r||s signature or an ASN.1 DER signature. Invalid JSON, key coordinates, encoding, curve, or signature returns an error.

Types

type Challenge

type Challenge struct {
	DeviceID  string    `json:"device_id"`
	Challenge string    `json:"challenge"`
	ExpiresAt time.Time `json:"expires_at"`
}

Challenge is a two-minute, one-use proof challenge for a previously paired device. Challenge is sensitive until it has been consumed or expired.

type CompletePairingRequest

type CompletePairingRequest struct {
	PairingID    string          `json:"pairing_id"`
	Nonce        string          `json:"nonce"`
	DeviceName   string          `json:"device_name"`
	PublicKeyJWK json.RawMessage `json:"public_key_jwk"`
	Signature    string          `json:"signature"`
}

CompletePairingRequest contains untrusted device enrollment proof. Nonce and Signature are sensitive. PublicKeyJWK and Signature prove possession of the device key; a client with no WebCrypto omits both and enrolls a cookie-only grant, whose continuity rests entirely on the HttpOnly device cookie.

type CompletePairingResult

type CompletePairingResult struct {
	DeviceID  string    `json:"device_id"`
	Token     string    `json:"token"`
	ExpiresAt time.Time `json:"expires_at"`
}

CompletePairingResult identifies the durable device grant and its initial process-local session. Token is a bearer secret.

type DeviceWriter

type DeviceWriter interface {
	AddDevice(state.DeviceGrant) error
}

DeviceWriter persists paired-device creation and revocation through the app's serialized alert-delivery controller.

type Manager

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

Manager coordinates process-local pairing sessions, challenges, and bearer sessions with durable device grants in the app state store. Its in-memory credential maps are mutex-protected for concurrent HTTP handlers.

func NewManager

func NewManager(store *state.Store, deviceWriter DeviceWriter, pairingTTL time.Duration) *Manager

NewManager constructs a Manager backed by store. Device writes are routed through deviceWriter so revocation cannot race confirmed alert transport. A pairingTTL of zero or less uses five minutes. Both authorities must be non-nil before authentication methods are used.

func (*Manager) Authenticate

func (m *Manager) Authenticate(token string) (Session, bool)

Authenticate validates a process-local bearer token, removes it if expired, and confirms that its durable device grant still exists. On success it returns a copy of the Session and best-effort updates device last-seen time.

func (*Manager) AuthenticateDeviceCookie

func (m *Manager) AuthenticateDeviceCookie(value string) (Session, error)

AuthenticateDeviceCookie verifies a deviceID.secret continuity credential against the hashes on the durable grant and returns a new SessionTTL session. It updates the device's last-seen time on a best-effort basis. The input and returned token are bearer secrets and must not be logged.

func (*Manager) CompleteChallenge

func (m *Manager) CompleteChallenge(deviceID, challenge, signature string) (Session, error)

CompleteChallenge consumes challenge and verifies the paired device against its stored P-256 public key. A successful proof returns a new SessionTTL bearer session. A known challenge is consumed even when device, expiry, or proof validation fails. A cookie-only grant holds no key and is rejected here; its continuity path is the device cookie, not this challenge.

func (*Manager) CompletePairing

func (m *Manager) CompletePairing(req CompletePairingRequest) (CompletePairingResult, error)

CompletePairing consumes the referenced pairing session, validates its expiry, nonce, and device proof, persists a new device grant, and returns a SessionTTL bearer session. Any completion attempt consumes a known pairing ID even when later validation fails, so the invitation cannot be retried.

func (*Manager) IssueDeviceCookie

func (m *Manager) IssueDeviceCookie(deviceID string) (string, error)

IssueDeviceCookie creates a durable continuity credential for a paired device, stores only its SHA-256 hash on the device grant, and returns the raw deviceID.secret value once. The returned value is a bearer secret and must be protected by the HTTP cookie layer. This method does not mint a session.

func (*Manager) StartChallenge

func (m *Manager) StartChallenge(deviceID string) (Challenge, error)

StartChallenge creates a two-minute, one-use challenge for a device that is present in the durable grant store. The challenge uses cryptographic randomness and is held only in memory.

func (*Manager) StartPairing

func (m *Manager) StartPairing(publicURL string) (PairingSession, error)

StartPairing creates a one-use pairing ID and nonce using cryptographic randomness. The returned URL appends both values to publicURL and expires after the Manager's pairing TTL. publicURL is trimmed but otherwise trusted as supplied by the caller.

func (*Manager) StartReaper

func (m *Manager) StartReaper(ctx context.Context, every time.Duration)

StartReaper blocks while periodically removing expired pairing sessions, challenges, and bearer sessions. An every value of zero or less uses one minute. The loop returns when ctx is cancelled.

type PairingSession

type PairingSession struct {
	ID        string    `json:"id"`
	Nonce     string    `json:"nonce"`
	URL       string    `json:"url"`
	ExpiresAt time.Time `json:"expires_at"`
	CreatedAt time.Time `json:"created_at"`
}

PairingSession is a short-lived, one-use invitation to enroll a device. ID, Nonce, and URL are sensitive because URL embeds both credentials.

type Session

type Session struct {
	Token     string    `json:"token"`
	DeviceID  string    `json:"device_id"`
	ExpiresAt time.Time `json:"expires_at"`
	CreatedAt time.Time `json:"created_at"`
}

Session is a process-local authenticated device session. Token is a bearer secret and must not be logged or persisted; ExpiresAt is fixed at issuance.

Jump to

Keyboard shortcuts

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