Documentation
¶
Overview ¶
Package security provides authentication and authorization for cdev.
Package security provides authentication and authorization for cdev.
Package security provides authentication and authorization for cdev.
Index ¶
- Constants
- Variables
- func DefaultTokenSecretPath() string
- func IsTrustedProxy(remoteAddr string, trustedProxies []*net.IPNet) bool
- func ParseTrustedProxies(trustedProxies []string) ([]*net.IPNet, error)
- func RequestBaseURL(r *http.Request, trustedProxies []*net.IPNet) (string, bool)
- func RequestClientIP(r *http.Request, trustedProxies []*net.IPNet) string
- func WebSocketURL(httpURL string) string
- type AuthRegistry
- func (r *AuthRegistry) BindWorkspace(deviceID, workspaceID string) error
- func (r *AuthRegistry) GetDevice(deviceID string) (*DeviceSession, bool)
- func (r *AuthRegistry) IsRefreshNonceValid(deviceID, nonce string) bool
- func (r *AuthRegistry) PruneExpired(now time.Time) ([]string, []string, error)
- func (r *AuthRegistry) RegisterDevice(deviceID, refreshNonce string, refreshExpiry time.Time, accessNonce string, ...) error
- func (r *AuthRegistry) RemoveDevice(deviceID string) ([]string, error)
- func (r *AuthRegistry) UnbindWorkspace(workspaceID string) error
- type DeviceSession
- type OriginChecker
- type PairingApprovalManager
- func (m *PairingApprovalManager) Approve(requestID string) (*PairingApprovalRequest, error)
- func (m *PairingApprovalManager) ClearTokenDecision(nonce string)
- func (m *PairingApprovalManager) EnsurePending(nonce, remoteAddr, userAgent string, expiresAt time.Time) (*PairingApprovalRequest, error)
- func (m *PairingApprovalManager) ListPending() []PairingApprovalRequest
- func (m *PairingApprovalManager) Reject(requestID string) (*PairingApprovalRequest, error)
- func (m *PairingApprovalManager) Status(nonce string) PairingApprovalStatus
- type PairingApprovalRequest
- type PairingApprovalStatus
- type TokenManager
- func (tm *TokenManager) CleanupExpiredRevocations(maxAge time.Duration)
- func (tm *TokenManager) ExchangePairingToken(pairingToken string) (*TokenPair, error)
- func (tm *TokenManager) GenerateAccessToken() (string, time.Time, error)
- func (tm *TokenManager) GeneratePairingToken() (string, time.Time, error)
- func (tm *TokenManager) GeneratePairingTokenWithExpiry(expirySecs int) (string, time.Time, error)
- func (tm *TokenManager) GenerateRefreshToken() (string, time.Time, error)
- func (tm *TokenManager) GenerateSessionToken() (string, time.Time, error)
- func (tm *TokenManager) GenerateTokenPair() (*TokenPair, error)
- func (tm *TokenManager) GenerateTokenPairWithDeviceID(deviceID string) (*TokenPair, error)
- func (tm *TokenManager) RefreshTokenPair(refreshToken string) (*TokenPair, error)
- func (tm *TokenManager) RevokeAllTokens() error
- func (tm *TokenManager) RevokeToken(token string) error
- func (tm *TokenManager) RevokeTokenByNonce(nonce string)
- func (tm *TokenManager) ServerID() string
- func (tm *TokenManager) ValidateToken(token string) (*TokenPayload, error)
- type TokenMode
- type TokenPair
- type TokenPayload
- type TokenType
Constants ¶
const ( PairingTokenPrefix = "cdev_p_" // Pairing token (for initial connection) SessionTokenPrefix = "cdev_s_" // Session token (for ongoing communication) RefreshTokenPrefix = "cdev_r_" // Refresh token (for obtaining new access tokens) )
Token prefixes for identification
const ( DefaultAccessTokenExpiry = 15 * 60 // 15 minutes DefaultRefreshTokenExpiry = 7 * 24 * 3600 // 7 days )
Default token expiry durations
Variables ¶
var ( ErrInvalidToken = errors.New("invalid token") ErrExpiredToken = errors.New("token has expired") ErrInvalidFormat = errors.New("invalid token format") ErrTokenNotFound = errors.New("token not found") ErrTokenRevoked = errors.New("token has been revoked") )
Common errors
Functions ¶
func DefaultTokenSecretPath ¶
func DefaultTokenSecretPath() string
DefaultTokenSecretPath returns the path used for the token secret file.
func IsTrustedProxy ¶
IsTrustedProxy reports whether remoteAddr belongs to one of trusted CIDRs.
func ParseTrustedProxies ¶
ParseTrustedProxies parses CIDR and IP entries into concrete CIDR ranges.
func RequestBaseURL ¶
RequestBaseURL resolves scheme+host from an HTTP request for service discovery. Forwarded headers are used only when the remote address is trusted.
func RequestClientIP ¶
RequestClientIP resolves the client IP from the request. Forwarded headers are used only when the remote address is trusted.
func WebSocketURL ¶
WebSocketURL creates a websocket URL from an HTTP URL.
Types ¶
type AuthRegistry ¶
type AuthRegistry struct {
Version int `json:"version"`
Devices map[string]*DeviceSession `json:"devices"`
Workspaces map[string]map[string]time.Time `json:"workspaces"` // workspaceID -> deviceID -> boundAt
// contains filtered or unexported fields
}
AuthRegistry tracks device sessions and workspace bindings. It persists refresh/access token nonces so tokens can be revoked on logout.
func LoadAuthRegistry ¶
func LoadAuthRegistry(path string) (*AuthRegistry, error)
LoadAuthRegistry loads the auth registry from disk or returns an empty registry.
func (*AuthRegistry) BindWorkspace ¶
func (r *AuthRegistry) BindWorkspace(deviceID, workspaceID string) error
BindWorkspace associates a workspace with a device.
func (*AuthRegistry) GetDevice ¶
func (r *AuthRegistry) GetDevice(deviceID string) (*DeviceSession, bool)
GetDevice returns the device session, if any.
func (*AuthRegistry) IsRefreshNonceValid ¶
func (r *AuthRegistry) IsRefreshNonceValid(deviceID, nonce string) bool
IsRefreshNonceValid reports whether nonce is the currently registered refresh nonce for any device. Returns false for unknown or removed devices.
func (*AuthRegistry) PruneExpired ¶
PruneExpired removes devices with expired refresh tokens and returns orphaned workspaces.
func (*AuthRegistry) RegisterDevice ¶
func (r *AuthRegistry) RegisterDevice(deviceID, refreshNonce string, refreshExpiry time.Time, accessNonce string, accessExpiry time.Time) error
RegisterDevice registers or updates a device session with its latest tokens.
func (*AuthRegistry) RemoveDevice ¶
func (r *AuthRegistry) RemoveDevice(deviceID string) ([]string, error)
RemoveDevice removes a device session and returns any orphaned workspaces.
func (*AuthRegistry) UnbindWorkspace ¶
func (r *AuthRegistry) UnbindWorkspace(workspaceID string) error
UnbindWorkspace removes all device bindings for a workspace.
type DeviceSession ¶
type DeviceSession struct {
DeviceID string `json:"device_id"`
RefreshNonce string `json:"refresh_nonce"`
RefreshExpiresAt time.Time `json:"refresh_expires_at"`
AccessNonce string `json:"access_nonce"`
AccessExpiresAt time.Time `json:"access_expires_at"`
UpdatedAt time.Time `json:"updated_at"`
}
DeviceSession tracks the latest issued tokens for a device.
type OriginChecker ¶
type OriginChecker struct {
// contains filtered or unexported fields
}
OriginChecker validates WebSocket and CORS origins.
func NewOriginChecker ¶
func NewOriginChecker(allowedOrigins []string, bindLocalhostOnly bool) *OriginChecker
NewOriginChecker creates a new origin checker.
func (*OriginChecker) CheckOrigin ¶
func (oc *OriginChecker) CheckOrigin(r *http.Request) bool
CheckOrigin validates the origin header in a request. Returns true if the origin is allowed.
func (*OriginChecker) CheckOriginFunc ¶
func (oc *OriginChecker) CheckOriginFunc() func(r *http.Request) bool
CheckOriginFunc returns a function suitable for websocket.Upgrader.CheckOrigin.
type PairingApprovalManager ¶
type PairingApprovalManager struct {
// contains filtered or unexported fields
}
PairingApprovalManager tracks pending/approved/rejected pairing requests.
func NewPairingApprovalManager ¶
func NewPairingApprovalManager() *PairingApprovalManager
NewPairingApprovalManager creates a new in-memory pairing approval manager.
func (*PairingApprovalManager) Approve ¶
func (m *PairingApprovalManager) Approve(requestID string) (*PairingApprovalRequest, error)
Approve marks a pending request as approved.
func (*PairingApprovalManager) ClearTokenDecision ¶
func (m *PairingApprovalManager) ClearTokenDecision(nonce string)
ClearTokenDecision clears approved/rejected/pending state for a token nonce.
func (*PairingApprovalManager) EnsurePending ¶
func (m *PairingApprovalManager) EnsurePending(nonce, remoteAddr, userAgent string, expiresAt time.Time) (*PairingApprovalRequest, error)
EnsurePending returns an existing pending request for nonce or creates a new one.
func (*PairingApprovalManager) ListPending ¶
func (m *PairingApprovalManager) ListPending() []PairingApprovalRequest
ListPending returns all currently pending pairing requests.
func (*PairingApprovalManager) Reject ¶
func (m *PairingApprovalManager) Reject(requestID string) (*PairingApprovalRequest, error)
Reject marks a pending request as rejected.
func (*PairingApprovalManager) Status ¶
func (m *PairingApprovalManager) Status(nonce string) PairingApprovalStatus
Status returns current approval status for a pairing token nonce.
type PairingApprovalRequest ¶
type PairingApprovalRequest struct {
RequestID string `json:"request_id"`
RemoteAddr string `json:"remote_addr"`
UserAgent string `json:"user_agent"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
// contains filtered or unexported fields
}
PairingApprovalRequest represents a pending device pairing request.
type PairingApprovalStatus ¶
type PairingApprovalStatus string
PairingApprovalStatus represents approval state for a pairing token nonce.
const ( PairingApprovalStatusNone PairingApprovalStatus = "none" PairingApprovalStatusPending PairingApprovalStatus = "pending" PairingApprovalStatusApproved PairingApprovalStatus = "approved" PairingApprovalStatusRejected PairingApprovalStatus = "rejected" )
type TokenManager ¶
type TokenManager struct {
// contains filtered or unexported fields
}
TokenManager handles token generation and validation.
func NewTokenManager ¶
func NewTokenManager(expirySecs int) (*TokenManager, error)
NewTokenManager creates a new token manager with a persisted server secret. The secret is stored in ~/.cdev/token_secret.json and reused across restarts.
func NewTokenManagerWithPath ¶
func NewTokenManagerWithPath(expirySecs int, secretPath string) (*TokenManager, error)
NewTokenManagerWithPath creates a token manager with a custom secret path. This is primarily useful for testing with isolated configurations.
func (*TokenManager) CleanupExpiredRevocations ¶
func (tm *TokenManager) CleanupExpiredRevocations(maxAge time.Duration)
CleanupExpiredRevocations removes old entries from the revoked nonces map.
func (*TokenManager) ExchangePairingToken ¶
func (tm *TokenManager) ExchangePairingToken(pairingToken string) (*TokenPair, error)
ExchangePairingToken exchanges a valid pairing token for an access/refresh token pair. The pairing token is revoked after successful exchange.
func (*TokenManager) GenerateAccessToken ¶
func (tm *TokenManager) GenerateAccessToken() (string, time.Time, error)
GenerateAccessToken generates a new access token.
func (*TokenManager) GeneratePairingToken ¶
func (tm *TokenManager) GeneratePairingToken() (string, time.Time, error)
GeneratePairingToken generates a new pairing token.
func (*TokenManager) GeneratePairingTokenWithExpiry ¶
GeneratePairingTokenWithExpiry generates a pairing token with custom expiry.
func (*TokenManager) GenerateRefreshToken ¶
func (tm *TokenManager) GenerateRefreshToken() (string, time.Time, error)
GenerateRefreshToken generates a new refresh token.
func (*TokenManager) GenerateSessionToken ¶
func (tm *TokenManager) GenerateSessionToken() (string, time.Time, error)
GenerateSessionToken generates a new session token (shorter expiry).
func (*TokenManager) GenerateTokenPair ¶
func (tm *TokenManager) GenerateTokenPair() (*TokenPair, error)
GenerateTokenPair generates a new access/refresh token pair. This is typically called after initial pairing.
func (*TokenManager) GenerateTokenPairWithDeviceID ¶
func (tm *TokenManager) GenerateTokenPairWithDeviceID(deviceID string) (*TokenPair, error)
GenerateTokenPairWithDeviceID generates a token pair tied to a device ID.
func (*TokenManager) RefreshTokenPair ¶
func (tm *TokenManager) RefreshTokenPair(refreshToken string) (*TokenPair, error)
RefreshTokenPair validates a refresh token and issues a new token pair. The old refresh token is revoked after successful refresh.
func (*TokenManager) RevokeAllTokens ¶
func (tm *TokenManager) RevokeAllTokens() error
RevokeAllTokens revokes all issued tokens. This is done by regenerating the server secret.
func (*TokenManager) RevokeToken ¶
func (tm *TokenManager) RevokeToken(token string) error
RevokeToken revokes a token by its nonce.
func (*TokenManager) RevokeTokenByNonce ¶
func (tm *TokenManager) RevokeTokenByNonce(nonce string)
RevokeTokenByNonce revokes a token by nonce without validating the token.
func (*TokenManager) ServerID ¶
func (tm *TokenManager) ServerID() string
ServerID returns the server's unique ID.
func (*TokenManager) ValidateToken ¶
func (tm *TokenManager) ValidateToken(token string) (*TokenPayload, error)
ValidateToken validates a token and returns the payload if valid.
type TokenPair ¶
type TokenPair struct {
AccessToken string `json:"access_token"`
AccessTokenExpiry time.Time `json:"access_token_expires_at"`
RefreshToken string `json:"refresh_token"`
RefreshTokenExpiry time.Time `json:"refresh_token_expires_at"`
DeviceID string `json:"-"`
AccessNonce string `json:"-"`
RefreshNonce string `json:"-"`
}
TokenPair represents an access token and refresh token pair.
type TokenPayload ¶
type TokenPayload struct {
// Version for payload format migration (default 1)
Version int `json:"v,omitempty"`
// Core fields
Type TokenType `json:"type"`
ServerID string `json:"server_id"` // Local agent ID (legacy name for compatibility)
IssuedAt int64 `json:"issued_at"`
ExpiresAt int64 `json:"expires_at"`
Nonce string `json:"nonce"`
// Cloud Relay fields (optional, for future use)
Mode TokenMode `json:"mode,omitempty"` // "local" or "cloud"
AgentID string `json:"agent_id,omitempty"` // Alias for ServerID (semantic name)
DeviceID string `json:"device_id,omitempty"` // Client device fingerprint
UserID string `json:"user_id,omitempty"` // Cloud user ID (future)
}
TokenPayload is the data encoded in a token. Fields are designed to be forward-compatible with Cloud Relay.