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
- func AuthEnabled(ctx context.Context) bool
- func ContextWithUser(ctx context.Context, user *UserInfo) context.Context
- func CreateSessionToken(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 HasMinimumRole(userRole, minRole string) bool
- func HashPassword(password string) (string, error)
- func IsAdmin(ctx context.Context) bool
- 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 UserHasNamespaceAccess(user *UserInfo, namespace string) bool
- func ValidatePasswordComplexity(password string) error
- type Config
- type ConfigProvider
- 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) 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) InvalidateCache()
- 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) UpdateUserStatus(ctx context.Context, user *unstructured.Unstructured) (*unstructured.Unstructured, error)
- type IDTokenClaims
- 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) 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)
- type PersonalNamespacesConfig
- type RegistrationConfig
- type SessionToken
- type TokenResponse
- type UserInfo
Constants ¶
const ( // UserContextKey is the context key for the authenticated user. UserContextKey contextKey = "auth-user" // SessionCookieName is the name of the session cookie. SessionCookieName = "kw-session" )
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 ¶
This section is empty.
Functions ¶
func AuthEnabled ¶
AuthEnabled returns true if there is a user in the context (meaning auth is active).
func ContextWithUser ¶
ContextWithUser adds user info to the context.
func CreateSessionToken ¶
func CreateSessionToken(email, displayName, role string, groups []string, signingKey []byte, expiry time.Duration) (string, error)
CreateSessionToken creates a signed session token for the given user.
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 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 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 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.
Types ¶
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
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) 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) 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.
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) InvalidateCache ¶
func (p *ConfigProvider) InvalidateCache()
InvalidateCache forces the config to be reloaded on next access.
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) UpdateUserStatus ¶
func (p *ConfigProvider) UpdateUserStatus(ctx context.Context, user *unstructured.Unstructured) (*unstructured.Unstructured, error)
UpdateUserStatus updates the status subresource of a User CR.
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 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 {
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
UserinfoEndpoint string `json:"userinfo_endpoint"`
JwksURI string `json:"jwks_uri"`
}
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 (*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) 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.
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 SessionToken ¶
type SessionToken struct {
Email string `json:"email"`
DisplayName string `json:"displayName,omitempty"`
Role string `json:"role"`
Groups []string `json:"groups,omitempty"`
IssuedAt int64 `json:"iat"`
ExpiresAt int64 `json:"exp"`
}
SessionToken represents a JWT-like session token.
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 {
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"`
}
UserInfo represents the authenticated user's identity extracted from the session token.
func UserFromContext ¶
UserFromContext extracts the authenticated user from the request context. Returns nil if no user is authenticated (auth disabled or unauthenticated request).