Documentation
¶
Overview ¶
Package auth provides authentication and authorization middleware for the kube-workspaces API. It implements OIDC-based login flows, JWT session management, role-based access control (admin/editor/viewer), and integrates with the AuthConfig and User CRDs for configuration. When auth is disabled, all requests are treated as admin-level access.
Package auth provides authentication and authorization middleware for the kube-workspaces API. When auth is disabled (AuthConfig.spec.enabled=false), all middleware passes through transparently.
Index ¶
- Constants
- Variables
- func AuthEnabled(ctx context.Context) bool
- func BrowserOriginAllowed(r *http.Request, allowed []string) bool
- func BrowserRequestGuard(allowed []string) func(http.Handler) http.Handler
- func ContextWithSession(ctx context.Context, token *SessionToken) context.Context
- func ContextWithUser(ctx context.Context, user *UserInfo) context.Context
- func CreateDeviceToken(email, displayName, role string, groups []string, deviceName, jti string, ...) (string, error)
- func CreateSessionToken(email, displayName, role string, groups []string, signingKey []byte, ...) (string, error)
- func CreateSessionTokenWithProvenance(email, displayName, role string, groups []string, signingKey []byte, ...) (string, error)
- func FilterNamespaces(ctx context.Context, namespaces []string) []string
- func FilterWorkspaces(ctx context.Context, workspaces []map[string]interface{}) []map[string]interface{}
- func GeneratePassword() (string, error)
- func GetPasswordHash(secret *unstructured.Unstructured) (string, error)
- func HasExecutionPermission(user *UserInfo, namespace, workspace string, permission ExecutionPermission, ...) bool
- func HasMinimumRole(userRole, minRole string) bool
- func HashPassword(password string) (string, error)
- func IsAdmin(ctx context.Context) bool
- func LocalCredentialVersion(uid, secretName, key, passwordHash string) string
- func Middleware(provider *ConfigProvider) func(http.Handler) http.Handler
- func ProvisionLocalUser(ctx context.Context, provider *ConfigProvider, ...) (string, string, error)
- func ProvisionUser(ctx context.Context, provider *ConfigProvider, email, displayName, role string, ...) error
- func RequireNamespaceAccess(getNamespace func(*http.Request) string) func(http.Handler) http.Handler
- func RequireRole(minRole string) func(http.Handler) http.Handler
- func ResetLocalUserPassword(ctx context.Context, provider *ConfigProvider, userName string) (string, error)
- func RevalidateCredential(ctx context.Context, provider *ConfigProvider) error
- func UserHasNamespaceAccess(user *UserInfo, namespace string) bool
- func ValidatePasswordComplexity(password string) error
- func WatchCredentialRevocation(ctx context.Context, revalidate func(context.Context) error, ...) func()
- func WebSocketOriginAllowed(r *http.Request) bool
- type CodeStore
- type Config
- type ConfigProvider
- func (p *ConfigProvider) AuthorizeExecution(ctx context.Context, token, namespace, workspace string, ...) (*UserInfo, error)
- func (p *ConfigProvider) BindIdentity(ctx context.Context, name, uid string, id ExternalIdentity) error
- func (p *ConfigProvider) CreateOwnedPasswordSecret(ctx context.Context, user *unstructured.Unstructured, hash, password string) (string, error)
- func (p *ConfigProvider) CreatePasswordSecret(ctx context.Context, name, passwordHash, plaintextPassword string) error
- func (p *ConfigProvider) CreateUser(ctx context.Context, user *unstructured.Unstructured) (*unstructured.Unstructured, error)
- func (p *ConfigProvider) DeletePasswordSecret(ctx context.Context, name string) error
- func (p *ConfigProvider) DeleteUser(ctx context.Context, name string) error
- func (p *ConfigProvider) DynamicClient() dynamic.Interface
- func (p *ConfigProvider) GetAuthConfig(ctx context.Context) (*unstructured.Unstructured, error)
- func (p *ConfigProvider) GetConfig(ctx context.Context) (*Config, error)
- func (p *ConfigProvider) GetPasswordSecret(ctx context.Context, name string) (*unstructured.Unstructured, error)
- func (p *ConfigProvider) GetUser(ctx context.Context, name string) (*unstructured.Unstructured, error)
- func (p *ConfigProvider) GetUserByEmail(ctx context.Context, email string) (*unstructured.Unstructured, error)
- func (p *ConfigProvider) GetUserByIdentity(ctx context.Context, id ExternalIdentity) (*unstructured.Unstructured, error)
- func (p *ConfigProvider) HandleIdentityInventory(w http.ResponseWriter, r *http.Request)
- func (p *ConfigProvider) InvalidateCache()
- func (p *ConfigProvider) InventoryIdentities(ctx context.Context) (*IdentityInventory, error)
- func (p *ConfigProvider) ListUsers(ctx context.Context) (*unstructured.UnstructuredList, error)
- func (p *ConfigProvider) UpdateAuthConfig(ctx context.Context, obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
- func (p *ConfigProvider) UpdatePasswordHash(ctx context.Context, name, newHash string) error
- func (p *ConfigProvider) UpdateUser(ctx context.Context, user *unstructured.Unstructured) (*unstructured.Unstructured, error)
- func (p *ConfigProvider) UpdateUserSpec(ctx context.Context, original *unstructured.Unstructured, ...) (*unstructured.Unstructured, error)
- func (p *ConfigProvider) UpdateUserStatus(ctx context.Context, user *unstructured.Unstructured) (*unstructured.Unstructured, error)
- type DeviceHandler
- type DeviceInfo
- type DeviceStore
- func (s *DeviceStore) List(ctx context.Context, email string, isAdmin bool) ([]DeviceInfo, error)
- func (s *DeviceStore) Register(ctx context.Context, email, name, jti string, createdAt, expiresAt time.Time) error
- func (s *DeviceStore) Revoke(ctx context.Context, jti string) error
- func (s *DeviceStore) Revoked(ctx context.Context, jti, email string) bool
- type ExecutionGrant
- type ExecutionPermission
- type ExternalIdentity
- type IDTokenClaims
- type IdentityInventory
- type IdentityInventoryIssue
- type IdentityInventoryUser
- type LocalAuthHandler
- type OIDCDiscovery
- type OIDCHandler
- func (h *OIDCHandler) Close()
- func (h *OIDCHandler) HandleAuthConfig(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleBrowserSessionGrant(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleBrowserSessionRedeem(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleCallback(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleCreateIdentityAdoption(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleIdentityAdoption(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleLinkIdentity(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleLogin(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleLogout(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleMe(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleNativeToken(w http.ResponseWriter, r *http.Request)
- func (h *OIDCHandler) HandleUnlinkIdentity(w http.ResponseWriter, r *http.Request)
- type PersonalNamespacesConfig
- type RegistrationConfig
- type SessionProvenance
- type SessionToken
- type TokenResponse
- type UserInfo
Constants ¶
const ( // UserContextKey is the context key for the authenticated user. UserContextKey contextKey = "auth-user" // SessionContextKey is the context key for the validated session token. // Long-lived streams keep it so their credential can be revalidated // against live account state while the stream is open. SessionContextKey contextKey = "auth-session" // SessionCookieName is the name of the session cookie. SessionCookieName = "kw-session" )
const ( // DefaultRevalidateInterval is how often an open stream revalidates the // credential that authorised it. DefaultRevalidateInterval = 5 * time.Second )
const (
// DeviceTokenType marks a session token as a revocable device credential.
DeviceTokenType = "device"
)
const LocalAuthSystemNamespace = "kube-workspaces-system"
LocalAuthSystemNamespace is where local-auth password Secrets live, regardless of a user's personal namespace. Matches the controller's bootstrap-admin convention.
Variables ¶
var ( ErrIdentityNotBound = errors.New("external identity is not bound") ErrIdentityConflict = errors.New("external identity ownership conflict") )
var ErrCredentialRevoked = errors.New("credential revoked")
ErrCredentialRevoked marks a credential that must no longer authorise work. Long-lived streams end as soon as revalidation reports it.
var ErrUserNotFound = errors.New("user account not found")
ErrUserNotFound reports that no User CR matches the requested identity.
Functions ¶
func AuthEnabled ¶
AuthEnabled returns true if there is a user in the context (meaning auth is active).
func BrowserOriginAllowed ¶ added in v0.10.0
BrowserOriginAllowed validates an exact browser origin, independently of CORS. Headerless native clients may use bearer authentication, but a headerless cookie-authenticated WebSocket/mutation is rejected. Host/forwarded headers are not accepted as authority for the trusted browser-origin list.
func BrowserRequestGuard ¶ added in v0.10.0
BrowserRequestGuard protects WebSocket handshakes and state-changing requests before they reach logging, owner forwarding or authentication handlers. Native bearer clients do not need browser CSRF tokens; browser clients must supply an exact trusted Origin. Safe HTTP reads keep normal CORS response behavior.
func ContextWithSession ¶ added in v0.11.0
func ContextWithSession(ctx context.Context, token *SessionToken) context.Context
ContextWithSession adds the validated session token to the context.
func ContextWithUser ¶
ContextWithUser adds user info to the context.
func CreateDeviceToken ¶ added in v0.6.0
func CreateDeviceToken(email, displayName, role string, groups []string, deviceName, jti string, signingKey []byte, expiry time.Duration, userUID ...string) (string, error)
CreateDeviceToken mints a long-lived device token. jti must be fresh random hex; name is human metadata (e.g. "ada-laptop") and is not unique.
func CreateSessionToken ¶
func CreateSessionToken(email, displayName, role string, groups []string, signingKey []byte, expiry time.Duration, userUID ...string) (string, error)
CreateSessionToken creates a signed session token for the given user.
func CreateSessionTokenWithProvenance ¶ added in v0.11.0
func CreateSessionTokenWithProvenance(email, displayName, role string, groups []string, signingKey []byte, expiry time.Duration, userUID string, provenance SessionProvenance) (string, error)
CreateSessionTokenWithProvenance binds a credential to the proved login method. The low-level legacy constructor remains available for decoding/migration fixtures, but credentials without provenance cannot authorize requests.
func FilterNamespaces ¶
FilterNamespaces filters a list of namespaces to only those the user has access to. If the user is nil (auth disabled) or admin, all namespaces are returned.
func FilterWorkspaces ¶
func FilterWorkspaces(ctx context.Context, workspaces []map[string]interface{}) []map[string]interface{}
FilterWorkspaces filters workspace objects to only those in namespaces the user can access.
func GeneratePassword ¶ added in v0.3.0
GeneratePassword returns a cryptographically random password suitable for bootstrap/default use, drawn from passwordCharset.
func GetPasswordHash ¶ added in v0.3.0
func GetPasswordHash(secret *unstructured.Unstructured) (string, error)
GetPasswordHash extracts the bcrypt hash from a password Secret's .data.
func HasExecutionPermission ¶ added in v0.10.0
func HasExecutionPermission(user *UserInfo, namespace, workspace string, permission ExecutionPermission, now time.Time) bool
HasExecutionPermission is deliberately stricter than legacy auth-disabled helpers: no nil/anonymous or unbound principal, no implicit editor permission, and no grant can manufacture namespace membership. Policy/capability/recording admission must additionally be checked by the execution service.
func HasMinimumRole ¶
HasMinimumRole checks if a user role meets the minimum required role level. Role hierarchy: admin > editor > viewer.
func HashPassword ¶ added in v0.3.0
HashPassword bcrypt-hashes the given plaintext password.
func LocalCredentialVersion ¶ added in v0.11.0
LocalCredentialVersion is an opaque revision, not the password hash itself. Keep the wire construction aligned with proxy/internal/auth/provenance.go.
func Middleware ¶
func Middleware(provider *ConfigProvider) func(http.Handler) http.Handler
Middleware creates an HTTP middleware that validates session tokens. When auth is disabled, all requests pass through without authentication.
func ProvisionLocalUser ¶ added in v0.3.0
func ProvisionLocalUser(ctx context.Context, provider *ConfigProvider, email, displayName, role, password string, groups []string) (string, string, error)
ProvisionLocalUser creates a User CR with local auth enabled and a password Secret. If password is empty, a random one is generated. Returns the plaintext password that was set (for one-time display to an admin), the User CR name, and any error.
func ProvisionUser ¶
func ProvisionUser(ctx context.Context, provider *ConfigProvider, email, displayName, role string, groups []string) error
ProvisionUser creates a User CR for a new user during first login.
func RequireNamespaceAccess ¶
func RequireNamespaceAccess(getNamespace func(*http.Request) string) func(http.Handler) http.Handler
RequireNamespaceAccess checks that the user has access to the specified namespace. Admins have access to all namespaces.
func RequireRole ¶
RequireRole is an HTTP middleware that checks the user has at minimum the specified role. Role hierarchy: admin > editor > viewer.
func ResetLocalUserPassword ¶ added in v0.3.0
func ResetLocalUserPassword(ctx context.Context, provider *ConfigProvider, userName string) (string, error)
ResetLocalUserPassword generates a new random password for an existing local user, updates their password Secret, and sets mustChangePassword. Returns the new plaintext password for one-time display to an admin.
func RevalidateCredential ¶ added in v0.11.0
func RevalidateCredential(ctx context.Context, provider *ConfigProvider) error
RevalidateCredential re-checks the credential bound to the current request against live account state.
It returns nil while the credential is still usable, ErrCredentialRevoked once the account or credential is revoked, disabled, replaced or restricted, and a plain error when the state could not be verified. Callers must distinguish the two failure kinds: a revoked credential ends the stream immediately, while an unverifiable state is retried.
The check reads live cluster state rather than any per-replica cache, so a revocation performed against one API replica is observed by every replica that holds a stream for the same account.
func UserHasNamespaceAccess ¶
UserHasNamespaceAccess checks if a user has access to a given namespace.
func ValidatePasswordComplexity ¶ added in v0.3.0
ValidatePasswordComplexity enforces a minimum password policy.
func WatchCredentialRevocation ¶ added in v0.11.0
func WatchCredentialRevocation(ctx context.Context, revalidate func(context.Context) error, interval time.Duration, onRevoked func(reason string)) func()
WatchCredentialRevocation revalidates a credential on an interval and calls onRevoked once the credential is no longer usable. The returned function stops the watcher.
A revoked credential ends the stream immediately. A verification failure is retried, because the account state may only be momentarily unavailable; after repeated failures the stream is ended rather than left open on unverified state.
func WebSocketOriginAllowed ¶ added in v0.10.0
WebSocketOriginAllowed is also used at the upgrader boundary, so a bridge mounted outside the main HTTP stack cannot accidentally skip origin checks.
Types ¶
type CodeStore ¶ added in v0.6.0
type CodeStore interface {
Issue(code string, entry nativeAuthCode) error
Redeem(code string) (nativeAuthCode, bool)
Close()
}
CodeStore is the seam behind OIDCHandler.codes: single-use, TTL-bounded authorization codes shared by the RFC 8252 native login (native.go) and the browser-session grant (browsersession.go).
The in-memory nativeCodeStore implements this for single-replica use and for tests. The K8s-backed store below implements the same contract across replicas so a code issued on one pod redeems on any other.
func NewK8sCodeStore ¶ added in v0.6.0
NewK8sCodeStore returns a CodeStore backed by K8s Secrets when dyn is non-nil, or a pure in-memory store otherwise. TTL/max match the native flow defaults so callers do not repeat them.
type Config ¶
type Config struct {
Enabled bool
IssuerURL string
ClientID string
ClientSecret string
Scopes []string
UsernameClaim string
GroupsClaim string
SigningKey []byte
// PreviousSigningKey is the signing key that was in use before the current
// one. It is retained (for up to one TokenExpiry after a rotation) so that
// sessions minted with it keep validating while key rotation settles.
PreviousSigningKey []byte
TokenExpiry time.Duration
RefreshExpiry time.Duration
// DeviceTokenExpiry is the lifetime of long-lived revocable device tokens
// (spec.session.deviceTokenExpiry, default 90d). Session tokens keep using
// TokenExpiry.
DeviceTokenExpiry time.Duration
PersonalNamespaces PersonalNamespacesConfig
Registration RegistrationConfig
AdminEmails []string
RestrictNamespaceAccess bool
LocalAuthEnabled bool
}
Config holds the resolved authentication configuration.
func (*Config) SessionSigningKeys ¶ added in v0.4.0
SessionSigningKeys returns the signing keys acceptable for validating session tokens: the current key first, followed by any rotation predecessor that is still within its acceptance window.
type ConfigProvider ¶
type ConfigProvider struct {
// contains filtered or unexported fields
}
ConfigProvider loads and caches the AuthConfig from Kubernetes.
func NewConfigProvider ¶
func NewConfigProvider(dynamicClient dynamic.Interface) *ConfigProvider
NewConfigProvider creates a new ConfigProvider.
func (*ConfigProvider) AuthorizeExecution ¶ added in v0.10.0
func (p *ConfigProvider) AuthorizeExecution(ctx context.Context, token, namespace, workspace string, operation ExecutionPermission, elevated bool, now time.Time) (*UserInfo, error)
AuthorizeExecution requires a fresh signed credential and a fresh User lookup on every invocation (admission AND renewal). It must not accept a caller-built UserInfo or a desktop-controller grant as machine execution authority.
func (*ConfigProvider) BindIdentity ¶ added in v0.11.0
func (p *ConfigProvider) BindIdentity(ctx context.Context, name, uid string, id ExternalIdentity) error
BindIdentity reserves one identity atomically across API replicas, then appends it to the existing User using resourceVersion conflict retries. It never creates/replaces a User or infers ownership from email. A failed second write leaves an inert reservation that only the same User UID can resume; lookup requires BOTH the reservation and matching User binding. Claims deliberately survive account deletion, preventing implicit adoption by replacement accounts. This is a storage primitive, not an unauthenticated linking endpoint.
func (*ConfigProvider) CreateOwnedPasswordSecret ¶ added in v0.11.0
func (p *ConfigProvider) CreateOwnedPasswordSecret(ctx context.Context, user *unstructured.Unstructured, hash, password string) (string, error)
CreateOwnedPasswordSecret allocates a fresh credential for one persisted UID. It never adopts or overwrites an existing Secret. Callers publish its reference with a resourceVersion-conditional User update; a losing concurrent operation leaves an unreferenced, UID-owned Secret, never another account's credential.
func (*ConfigProvider) CreatePasswordSecret ¶ added in v0.3.0
func (p *ConfigProvider) CreatePasswordSecret(ctx context.Context, name, passwordHash, plaintextPassword string) error
CreatePasswordSecret creates a new Secret holding a local user's bcrypt password hash (and, transiently, plaintext password) in LocalAuthSystemNamespace.
func (*ConfigProvider) CreateUser ¶
func (p *ConfigProvider) CreateUser(ctx context.Context, user *unstructured.Unstructured) (*unstructured.Unstructured, error)
CreateUser creates a new User CR.
func (*ConfigProvider) DeletePasswordSecret ¶ added in v0.3.0
func (p *ConfigProvider) DeletePasswordSecret(ctx context.Context, name string) error
DeletePasswordSecret deletes a local user's password Secret.
func (*ConfigProvider) DeleteUser ¶
func (p *ConfigProvider) DeleteUser(ctx context.Context, name string) error
DeleteUser deletes a User CR by name.
func (*ConfigProvider) DynamicClient ¶ added in v0.6.0
func (p *ConfigProvider) DynamicClient() dynamic.Interface
DynamicClient exposes the underlying dynamic client for stores that persist alongside the config (auth codes, device tokens). May be nil in tests.
func (*ConfigProvider) GetAuthConfig ¶
func (p *ConfigProvider) GetAuthConfig(ctx context.Context) (*unstructured.Unstructured, error)
GetAuthConfig returns the raw AuthConfig CR.
func (*ConfigProvider) GetConfig ¶
func (p *ConfigProvider) GetConfig(ctx context.Context) (*Config, error)
GetConfig returns the current auth configuration, fetching from Kubernetes if needed. Failed refreshes are cached too, but never expose a stale or disabled configuration.
func (*ConfigProvider) GetPasswordSecret ¶ added in v0.3.0
func (p *ConfigProvider) GetPasswordSecret(ctx context.Context, name string) (*unstructured.Unstructured, error)
GetPasswordSecret fetches the raw Secret backing a local user's password, from LocalAuthSystemNamespace.
func (*ConfigProvider) GetUser ¶
func (p *ConfigProvider) GetUser(ctx context.Context, name string) (*unstructured.Unstructured, error)
GetUser fetches a User CR by name.
func (*ConfigProvider) GetUserByEmail ¶
func (p *ConfigProvider) GetUserByEmail(ctx context.Context, email string) (*unstructured.Unstructured, error)
GetUserByEmail fetches a User CR by email (list and filter).
func (*ConfigProvider) GetUserByIdentity ¶ added in v0.11.0
func (p *ConfigProvider) GetUserByIdentity(ctx context.Context, id ExternalIdentity) (*unstructured.Unstructured, error)
GetUserByIdentity resolves only a durably claimed binding, never an email. Ambiguous or partially persisted bindings, stale UIDs and disabled accounts fail closed. No claim is adopted or repaired as a side effect of login.
func (*ConfigProvider) HandleIdentityInventory ¶ added in v0.11.0
func (p *ConfigProvider) HandleIdentityInventory(w http.ResponseWriter, r *http.Request)
func (*ConfigProvider) InvalidateCache ¶
func (p *ConfigProvider) InvalidateCache()
InvalidateCache forces the config to be reloaded on next access.
func (*ConfigProvider) InventoryIdentities ¶ added in v0.11.0
func (p *ConfigProvider) InventoryIdentities(ctx context.Context) (*IdentityInventory, error)
func (*ConfigProvider) ListUsers ¶
func (p *ConfigProvider) ListUsers(ctx context.Context) (*unstructured.UnstructuredList, error)
ListUsers returns all User CRs.
func (*ConfigProvider) UpdateAuthConfig ¶
func (p *ConfigProvider) UpdateAuthConfig(ctx context.Context, obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
UpdateAuthConfig updates the AuthConfig CR.
func (*ConfigProvider) UpdatePasswordHash ¶ added in v0.3.0
func (p *ConfigProvider) UpdatePasswordHash(ctx context.Context, name, newHash string) error
UpdatePasswordHash replaces the password hash in an existing Secret and removes any plaintext password key (used once a user sets their own password).
func (*ConfigProvider) UpdateUser ¶
func (p *ConfigProvider) UpdateUser(ctx context.Context, user *unstructured.Unstructured) (*unstructured.Unstructured, error)
UpdateUser updates an existing User CR.
func (*ConfigProvider) UpdateUserSpec ¶ added in v0.11.0
func (p *ConfigProvider) UpdateUserSpec(ctx context.Context, original *unstructured.Unstructured, spec map[string]interface{}) (*unstructured.Unstructured, error)
UpdateUserSpec publishes an explicitly approved spec without overwriting concurrent account edits. Controller finalizer/status writes may be retried, but a replacement UID or changed spec requires a fresh administrative decision.
func (*ConfigProvider) UpdateUserStatus ¶
func (p *ConfigProvider) UpdateUserStatus(ctx context.Context, user *unstructured.Unstructured) (*unstructured.Unstructured, error)
UpdateUserStatus updates the status subresource of a User CR.
type DeviceHandler ¶ added in v0.6.0
type DeviceHandler struct {
// contains filtered or unexported fields
}
DeviceHandler serves the device-token endpoints.
func NewDeviceHandler ¶ added in v0.6.0
func NewDeviceHandler(provider *ConfigProvider, devices *DeviceStore) *DeviceHandler
NewDeviceHandler creates the handler. devices may be a nil-client store.
func (*DeviceHandler) HandleDeviceCreate ¶ added in v0.6.0
func (h *DeviceHandler) HandleDeviceCreate(w http.ResponseWriter, r *http.Request)
HandleDeviceCreate mints a long-lived device token for the caller's own session. Body: {"name": "ada-laptop"} (human label, optional).
func (*DeviceHandler) HandleDeviceList ¶ added in v0.6.0
func (h *DeviceHandler) HandleDeviceList(w http.ResponseWriter, r *http.Request)
HandleDeviceList returns the caller's device registrations (admins see all, unless ?scope=own is supplied by the profile UI).
func (*DeviceHandler) HandleDeviceRevoke ¶ added in v0.6.0
func (h *DeviceHandler) HandleDeviceRevoke(w http.ResponseWriter, r *http.Request)
HandleDeviceRevoke deletes a device registration. Body: {"device_id": "..."}. Owners revoke their own devices; admins may revoke any.
type DeviceInfo ¶ added in v0.6.0
type DeviceInfo struct {
DeviceID string `json:"deviceId"`
Name string `json:"name"`
CreatedAt int64 `json:"createdAt"`
ExpiresAt int64 `json:"expiresAt"`
}
DeviceInfo is the metadata returned by list/create (never the token itself, except on create).
type DeviceStore ¶ added in v0.6.0
type DeviceStore struct {
// contains filtered or unexported fields
}
DeviceStore persists device registrations as Secrets. A nil dynamic client (unit tests, local runs) degrades to a closed store: create fails with a clear error, revocation checks pass through so session tokens keep working.
func NewDeviceStore ¶ added in v0.6.0
func NewDeviceStore(dyn dynamic.Interface) *DeviceStore
NewDeviceStore creates the store. dyn may be nil.
func (*DeviceStore) List ¶ added in v0.6.0
func (s *DeviceStore) List(ctx context.Context, email string, isAdmin bool) ([]DeviceInfo, error)
List returns the caller's device registrations (metadata only).
func (*DeviceStore) Register ¶ added in v0.6.0
func (s *DeviceStore) Register(ctx context.Context, email, name, jti string, createdAt, expiresAt time.Time) error
Register creates the backing Secret for a freshly minted device token.
type ExecutionGrant ¶ added in v0.10.0
type ExecutionGrant struct {
Namespace string `json:"namespace"`
Workspaces []string `json:"workspaces,omitempty"`
Permissions []ExecutionPermission `json:"permissions"`
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
}
ExecutionGrant grants no namespace access by itself. Permissions from matching unexpired grants are additive, including separately granted elevation.
func ParseExecutionGrants ¶ added in v0.10.0
func ParseExecutionGrants(raw interface{}) ([]ExecutionGrant, error)
ParseExecutionGrants validates admin input as well as CR data. Strict decoding prevents misspelled scope fields from silently widening a grant.
type ExecutionPermission ¶ added in v0.10.0
type ExecutionPermission string
ExecutionPermission mirrors the controller-owned User.executionGrants schema.
const ( PermissionExec ExecutionPermission = "workspace.exec" PermissionShell ExecutionPermission = "workspace.shell" PermissionElevated ExecutionPermission = "workspace.exec.elevated" PermissionManage ExecutionPermission = "workspace.execution.manage" PermissionReadRecordings ExecutionPermission = "workspace.execution.recordings.read" )
type ExternalIdentity ¶ added in v0.11.0
ExternalIdentity is an exact OIDC issuer/subject pair. Callers must verify the ID token and authorize the linking operation before calling BindIdentity.
type IDTokenClaims ¶
type IDTokenClaims struct {
Sub string `json:"sub"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Name string `json:"name"`
Picture string `json:"picture"`
Groups []string `json:"groups"`
}
IDTokenClaims represents relevant claims from the ID token.
type IdentityInventory ¶ added in v0.11.0
type IdentityInventory struct {
Users []IdentityInventoryUser `json:"users"`
Issues []IdentityInventoryIssue `json:"issues"`
}
IdentityInventory is a read-only pre-migration report. It never returns Secret contents, adopts identities, renames Users or changes credentials.
type IdentityInventoryIssue ¶ added in v0.11.0
type IdentityInventoryUser ¶ added in v0.11.0
type LocalAuthHandler ¶ added in v0.3.0
type LocalAuthHandler struct {
// contains filtered or unexported fields
}
LocalAuthHandler handles local (username/password) authentication.
func NewLocalAuthHandler ¶ added in v0.3.0
func NewLocalAuthHandler(provider *ConfigProvider) *LocalAuthHandler
NewLocalAuthHandler creates a new LocalAuthHandler.
func (*LocalAuthHandler) HandleChangePassword ¶ added in v0.3.0
func (h *LocalAuthHandler) HandleChangePassword(w http.ResponseWriter, r *http.Request)
HandleChangePassword lets an authenticated local user set a new password. Note: the auth middleware skips /auth/* paths, so this handler must validate the session token directly (mirrors OIDCHandler.HandleMe): session cookie first, then an Authorization: Bearer header for native/API clients.
func (*LocalAuthHandler) HandleLocalLogin ¶ added in v0.3.0
func (h *LocalAuthHandler) HandleLocalLogin(w http.ResponseWriter, r *http.Request)
HandleLocalLogin authenticates a user via email/password and, on success, issues the same kind of session token/cookie as the OIDC flow.
type OIDCDiscovery ¶
type OIDCDiscovery struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
UserinfoEndpoint string `json:"userinfo_endpoint"`
JwksURI string `json:"jwks_uri"`
// contains filtered or unexported fields
}
OIDCDiscovery holds the endpoints from .well-known/openid-configuration.
type OIDCHandler ¶
type OIDCHandler struct {
// contains filtered or unexported fields
}
OIDCHandler handles the OIDC authentication flow.
func NewOIDCHandler ¶
func NewOIDCHandler(provider *ConfigProvider) *OIDCHandler
NewOIDCHandler creates a new OIDC handler.
func NewOIDCHandlerWithCodes ¶ added in v0.6.0
func NewOIDCHandlerWithCodes(provider *ConfigProvider, codes CodeStore) *OIDCHandler
NewOIDCHandlerWithCodes creates a handler with an explicit code store. The HTTP wiring passes a K8s-backed store when cluster access exists so native and browser-session codes redeem on any replica; tests pass the in-memory store (or a fake-backed K8s store) directly.
func (*OIDCHandler) Close ¶ added in v0.5.0
func (h *OIDCHandler) Close()
Close releases background resources held by the handler (the native authorization code sweeper).
func (*OIDCHandler) HandleAuthConfig ¶
func (h *OIDCHandler) HandleAuthConfig(w http.ResponseWriter, r *http.Request)
HandleAuthConfig returns public auth configuration for the frontend.
func (*OIDCHandler) HandleBrowserSessionGrant ¶ added in v0.5.0
func (h *OIDCHandler) HandleBrowserSessionGrant(w http.ResponseWriter, r *http.Request)
HandleBrowserSessionGrant mints a single-use code that, redeemed in a browser, sets a session cookie and redirects to the given /proxy/... target.
The endpoint authenticates with the caller's existing session token (Bearer header for the desktop client, cookie for a browser) — it never accepts an unauthenticated request, because there is no session to grant then.
func (*OIDCHandler) HandleBrowserSessionRedeem ¶ added in v0.5.0
func (h *OIDCHandler) HandleBrowserSessionRedeem(w http.ResponseWriter, r *http.Request)
HandleBrowserSessionRedeem sets the session cookie for a code issued by OIDCHandler.HandleBrowserSessionGrant and redirects to the grant's target.
This is a browser navigation, so failures render a small HTML page rather than a JSON error: the person looking at it is the user, not an API client.
func (*OIDCHandler) HandleCallback ¶
func (h *OIDCHandler) HandleCallback(w http.ResponseWriter, r *http.Request)
HandleCallback handles the OIDC callback after authentication.
func (*OIDCHandler) HandleCreateIdentityAdoption ¶ added in v0.11.0
func (h *OIDCHandler) HandleCreateIdentityAdoption(w http.ResponseWriter, r *http.Request)
HandleCreateIdentityAdoption returns a five-minute, single-use invitation. Administrator credentials stay in the server-side Secret store, never URLs or the recipient's signed (readable) OIDC state cookie.
func (*OIDCHandler) HandleIdentityAdoption ¶ added in v0.11.0
func (h *OIDCHandler) HandleIdentityAdoption(w http.ResponseWriter, r *http.Request)
GET does not consume the invitation (mail scanners/prefetch must not do so). The recipient explicitly continues using a POST before the OIDC round trip.
func (*OIDCHandler) HandleLinkIdentity ¶ added in v0.11.0
func (h *OIDCHandler) HandleLinkIdentity(w http.ResponseWriter, r *http.Request)
HandleLinkIdentity starts an explicit administrator-authorized link for the administrator's own account. The browser must retain that exact session until callback. The external identity is learned only from a verified OIDC roundtrip, never from an email or caller-supplied subject. General delegated linking and unlink/revocation UI are separate lifecycle operations.
func (*OIDCHandler) HandleLogin ¶
func (h *OIDCHandler) HandleLogin(w http.ResponseWriter, r *http.Request)
HandleLogin initiates the OIDC login flow.
func (*OIDCHandler) HandleLogout ¶
func (h *OIDCHandler) HandleLogout(w http.ResponseWriter, r *http.Request)
HandleLogout clears the session.
func (*OIDCHandler) HandleMe ¶
func (h *OIDCHandler) HandleMe(w http.ResponseWriter, r *http.Request)
HandleMe returns the current user's information. Note: The auth middleware skips /auth/* paths, so this handler must validate the session token directly. It accepts the session cookie (browser) or an Authorization: Bearer header (native/API clients, which cannot read an HttpOnly cookie); the cookie takes precedence.
func (*OIDCHandler) HandleNativeToken ¶ added in v0.5.0
func (h *OIDCHandler) HandleNativeToken(w http.ResponseWriter, r *http.Request)
HandleNativeToken exchanges a single-use authorization code, issued by the native branch of /auth/callback, for the session token it stands for.
Unlike every other token delivery path in this service the token is returned in the response body: a native app cannot read an HttpOnly cookie, which is the whole reason this endpoint exists.
func (*OIDCHandler) HandleUnlinkIdentity ¶ added in v0.11.0
func (h *OIDCHandler) HandleUnlinkIdentity(w http.ResponseWriter, r *http.Request)
type PersonalNamespacesConfig ¶
PersonalNamespacesConfig holds personal namespace settings.
type RegistrationConfig ¶
type RegistrationConfig struct {
AutoProvision bool
DefaultRole string
AllowedDomains []string
AllowedEmails []string
RequireApproval bool
}
RegistrationConfig holds user registration settings.
type SessionProvenance ¶ added in v0.11.0
type SessionProvenance struct {
AuthMethod string `json:"auth_method,omitempty"`
CredentialVersion string `json:"credential_version,omitempty"`
Issuer string `json:"issuer,omitempty"`
Subject string `json:"subject,omitempty"`
}
SessionProvenance travels unchanged through native/browser grants and devices.
type SessionToken ¶
type SessionToken struct {
SessionProvenance
// UserUID binds new credentials to a User incarnation. Legacy credentials
// remain usable for existing routes, but cannot authorize machine execution.
UserUID string `json:"user_uid,omitempty"`
Email string `json:"email"`
DisplayName string `json:"displayName,omitempty"`
Role string `json:"role"`
Groups []string `json:"groups,omitempty"`
// Type is empty for short-lived session tokens and "device" for
// long-lived revocable device tokens (see device.go).
Type string `json:"typ,omitempty"`
// JTI is the device id for device tokens; empty otherwise.
JTI string `json:"jti,omitempty"`
IssuedAt int64 `json:"iat"`
ExpiresAt int64 `json:"exp"`
}
SessionToken represents a JWT-like session token.
func SessionFromContext ¶ added in v0.11.0
func SessionFromContext(ctx context.Context) *SessionToken
SessionFromContext returns the validated session token bound to the request. Returns nil when the request carried no credential (auth disabled).
func ValidateSessionToken ¶
func ValidateSessionToken(tokenStr string, signingKey []byte) (*SessionToken, error)
ValidateSessionToken validates and decodes a session token signed with the given signing key.
func ValidateSessionTokenWithKeys ¶ added in v0.4.0
func ValidateSessionTokenWithKeys(tokenStr string, signingKeys [][]byte) (*SessionToken, error)
ValidateSessionTokenWithKeys validates and decodes a session token against any of the given signing keys. The current key should be first; keys retired by rotation should follow so that sessions minted before the rotation remain valid while they expire naturally.
type TokenResponse ¶
type TokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
IDToken string `json:"id_token"`
}
TokenResponse represents the OIDC token response.
type UserInfo ¶
type UserInfo struct {
// UID is the current User CR identity, not a caller-selected label.
UID string `json:"uid,omitempty"`
Email string `json:"email"`
DisplayName string `json:"displayName,omitempty"`
Role string `json:"role"`
Groups []string `json:"groups,omitempty"`
// Namespaces the user has access to (populated at request time)
Namespaces []string `json:"namespaces,omitempty"`
// PersonalNamespace is the user's personal namespace
PersonalNamespace string `json:"personalNamespace,omitempty"`
// AvatarURL is the URL to the user's avatar image
AvatarURL string `json:"avatarURL,omitempty"`
// MustChangePassword indicates a local-auth user must set a new password
// before continuing (set on creation/reset by an admin).
MustChangePassword bool `json:"mustChangePassword,omitempty"`
AuthMethod string `json:"authMethod,omitempty"`
LocalPasswordEnabled bool `json:"localPasswordEnabled"`
OIDCLinked bool `json:"oidcLinked"`
CanChangePassword bool `json:"canChangePassword"`
// ExecutionGrants are loaded from the current User CR, never from a token.
ExecutionGrants []ExecutionGrant `json:"-"`
// contains filtered or unexported fields
}
UserInfo represents the authenticated user's identity extracted from the session token.
func ExecutionPrincipal ¶ added in v0.10.0
ExecutionPrincipal is the middleware-authenticated, current User identity. Legacy credentials remain valid for older routes but cannot access management.
func UserFromContext ¶
UserFromContext extracts the authenticated user from the request context. Returns nil if no user is authenticated (auth disabled or unauthenticated request).