Documentation
¶
Overview ¶
Package devicelogin pairs an unauthenticated browser with an explicitly approving signed-in user. Pending requests are memory-only: restart cancels them. Hosts must mount all handlers behind their existing network/origin protections, provide trusted client IPs, and issue their normal session.
Browser pairing storage, approval proofs, and bounded abuse budgets.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type AuditEvent ¶
type AuditEvent struct {
RequestID string
SessionID string
Kind AuditKind
Subject string
Approved bool
IP string
UserAgent string
CreatedAt time.Time
}
AuditEvent correlates decisions and session-completion outcomes without credentials or user codes.
type AuditKind ¶
type AuditKind string
AuditKind identifies a pairing or session-completion outcome. ResponseWritten records only a successful local write; client receipt is always unknown.
const ( AuditDecision AuditKind = "decision" AuditIssuanceStarted AuditKind = "issuance_started" AuditResponseFailed AuditKind = "response_failed" AuditResponseWritten AuditKind = "response_written" AuditRevocationFailed AuditKind = "revocation_failed" AuditSessionCreated AuditKind = "session_created" AuditSessionCreationUncertain AuditKind = "session_creation_uncertain" AuditSessionFailed AuditKind = "session_failed" AuditSessionRevoked AuditKind = "session_revoked" )
Audit kinds distinguish durable decisions, creation, local writes, and cleanup.
type Host ¶
type Host interface {
Enabled() bool
ClientIP(*http.Request) netip.Addr
VerificationURL(*http.Request) string
Identify(*http.Request) (string, error)
Eligible(context.Context, string) error
CreateSession(*http.Request, string) (Session, error)
Audit(context.Context, *AuditEvent) error
}
Host supplies host policy without sharing account or session types. Identity must be stable and sufficient to recheck current login policy. CreateSession must return a nonnil Session whenever it created one, including on error, so failure can revoke it. Use the host's normal issuer with its ordinary permissions, lifetime, and capabilities. Audit must persist the event before returning nil and must not mutate or retain it. Methods must support concurrent calls.
type LookupResponse ¶
type LookupResponse struct {
UserCode string `json:"user_code"`
IP string `json:"ip"`
UserAgent string `json:"user_agent"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
CSRFToken string `json:"csrf_token"`
}
LookupResponse describes the requesting browser and a short-lived subject-bound CSRF token.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server owns a bounded in-memory device login store. Use one Server per host process. Multi-instance hosts need sticky routing for these endpoints. Codes expire in ten minutes.
func (*Server) Approve ¶
func (s *Server) Approve(w http.ResponseWriter, r *http.Request)
Approve records an explicit approval or denial. X-Device-Login-CSRF must contain the token from Lookup for this subject and code; JSON and a custom header prevent cookie CSRF.
func (*Server) Lookup ¶
func (s *Server) Lookup(w http.ResponseWriter, r *http.Request)
Lookup requires an eligible signed-in approver, rate limits all guesses, and reveals no device secret.
func (*Server) Poll ¶
func (s *Server) Poll(w http.ResponseWriter, r *http.Request)
Poll consumes an approval before attempting session creation. Every completion failure (including cancellation or lost delivery) requires a new code; replay cannot create a second session. Failed writes and audit failures trigger revocation; cleanup failures are audited and logged. A successful write does not prove receipt.
type Session ¶
type Session interface {
SessionID() string
WriteResponse(http.ResponseWriter) error
Revoke(context.Context) error
}
Session is a newly persisted host session. The host retains its credential and response/account types. WriteResponse must report serialization or write failures; Revoke must durably invalidate the session even when the original request was cancelled.
type StartResponse ¶
type StartResponse struct {
RequestID string `json:"request_id"`
DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"`
VerificationURI string `json:"verification_uri"`
ExpiresIn int `json:"expires_in"`
Interval int `json:"interval"`
}
StartResponse is the display's private authorization request. Never send DeviceCode to the approver.