security

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Feb 27, 2026 License: MIT Imports: 16 Imported by: 0

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

View Source
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

View Source
const (
	DefaultAccessTokenExpiry  = 15 * 60       // 15 minutes
	DefaultRefreshTokenExpiry = 7 * 24 * 3600 // 7 days
)

Default token expiry durations

Variables

View Source
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

func IsTrustedProxy(remoteAddr string, trustedProxies []*net.IPNet) bool

IsTrustedProxy reports whether remoteAddr belongs to one of trusted CIDRs.

func ParseTrustedProxies

func ParseTrustedProxies(trustedProxies []string) ([]*net.IPNet, error)

ParseTrustedProxies parses CIDR and IP entries into concrete CIDR ranges.

func RequestBaseURL

func RequestBaseURL(r *http.Request, trustedProxies []*net.IPNet) (string, bool)

RequestBaseURL resolves scheme+host from an HTTP request for service discovery. Forwarded headers are used only when the remote address is trusted.

func RequestClientIP

func RequestClientIP(r *http.Request, trustedProxies []*net.IPNet) string

RequestClientIP resolves the client IP from the request. Forwarded headers are used only when the remote address is trusted.

func WebSocketURL

func WebSocketURL(httpURL string) string

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

func (r *AuthRegistry) PruneExpired(now time.Time) ([]string, []string, error)

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

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

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

func (tm *TokenManager) GeneratePairingTokenWithExpiry(expirySecs int) (string, time.Time, error)

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 TokenMode

type TokenMode string

TokenMode indicates how the token should be validated.

const (
	TokenModeLocal TokenMode = "local" // Validated by local agent (current)
	TokenModeCloud TokenMode = "cloud" // Validated by cloud relay (future)
)

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.

type TokenType

type TokenType string

TokenType represents the type of token.

const (
	TokenTypePairing TokenType = "pairing"
	TokenTypeSession TokenType = "session"
	TokenTypeRefresh TokenType = "refresh"
	TokenTypeAccess  TokenType = "access" // Alias for session (clearer semantics)
)

Jump to

Keyboard shortcuts

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