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
- func VerifyJWKSignature(raw json.RawMessage, message []byte, sigB64 string) error
- type Challenge
- type CompletePairingRequest
- type CompletePairingResult
- type DeviceWriter
- type Manager
- func (m *Manager) Authenticate(token string) (Session, bool)
- func (m *Manager) AuthenticateDeviceCookie(value string) (Session, error)
- func (m *Manager) CompleteChallenge(deviceID, challenge, signature string) (Session, error)
- func (m *Manager) CompletePairing(req CompletePairingRequest) (CompletePairingResult, error)
- func (m *Manager) IssueDeviceCookie(deviceID string) (string, error)
- func (m *Manager) StartChallenge(deviceID string) (Challenge, error)
- func (m *Manager) StartPairing(publicURL string) (PairingSession, error)
- func (m *Manager) StartReaper(ctx context.Context, every time.Duration)
- type PairingSession
- type Session
Constants ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.
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.