oidc

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jun 17, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package oidc provides OpenID Connect authentication support for GraphDB.

Index

Constants

View Source
const (
	DefaultJWKSCacheTTL = time.Hour
	DefaultRole         = "viewer"
)

Default configuration values

View Source
const (
	// StateLength is the byte length of CSRF state tokens
	StateLength = 32
	// DefaultStateTTL is how long state tokens are valid
	DefaultStateTTL = 10 * time.Minute
	// MaxStateEntries is the maximum number of pending state entries (DoS protection)
	MaxStateEntries = 10000
)
View Source
const MaxKeyCacheSize = 1000

MaxKeyCacheSize limits the number of cached RSA keys to prevent unbounded growth

Variables

View Source
var (
	ErrKeyNotFound    = errors.New("key not found in JWKS")
	ErrInvalidKeyUse  = errors.New("key is not for signature verification")
	ErrUnsupportedKty = errors.New("unsupported key type")
)
View Source
var (
	ErrTokenMalformed   = errors.New("token is malformed")
	ErrTokenExpired     = errors.New("token has expired")
	ErrTokenNotYetValid = errors.New("token is not yet valid")
	ErrInvalidSignature = errors.New("invalid token signature")
	ErrInvalidIssuer    = errors.New("invalid token issuer")
	ErrInvalidAudience  = errors.New("invalid token audience")
	ErrMissingSubject   = errors.New("token missing subject claim")
	ErrUnsupportedAlg   = errors.New("unsupported signing algorithm")
)

Functions

func DefaultScopes

func DefaultScopes() []string

DefaultScopes returns the default OIDC scopes

func HigherPrivilegeRole

func HigherPrivilegeRole(a, b string) string

HigherPrivilegeRole returns the role with higher privilege

func RolePriority

func RolePriority(role string) int

RolePriority returns the priority of a role (higher = more privileged)

Types

type CachedDiscovery

type CachedDiscovery struct {
	Discovery *OIDCDiscovery
	FetchedAt time.Time
	ExpiresAt time.Time
}

CachedDiscovery holds a discovery document with cache metadata

type CachedJWKS

type CachedJWKS struct {
	JWKS      *JWKS
	FetchedAt time.Time
	ExpiresAt time.Time
}

CachedJWKS holds JWKS with cache metadata

type Config

type Config struct {
	// Enabled determines if OIDC authentication is active
	Enabled bool `json:"enabled"`

	// Issuer is the OIDC provider URL (e.g., "https://accounts.google.com")
	Issuer string `json:"issuer"`

	// ClientID is the OAuth2 client ID
	ClientID string `json:"client_id"`

	// ClientSecret is the OAuth2 client secret (required for authorization code flow)
	ClientSecret string `json:"client_secret"`

	// RedirectURI is the callback URL (auto-detected if empty)
	RedirectURI string `json:"redirect_uri"`

	// Scopes to request from the provider (default: openid, profile, email)
	Scopes []string `json:"scopes"`

	// RoleMappings define how to map OIDC claims to GraphDB roles
	RoleMappings []RoleMapping `json:"role_mappings"`

	// DefaultRole is assigned when no role mapping matches (default: viewer)
	DefaultRole string `json:"default_role"`

	// JWKSCacheTTL is how long to cache JWKS (default: 1 hour)
	JWKSCacheTTL time.Duration `json:"jwks_cache_ttl"`

	// AllowedAudiences are additional valid audience values
	AllowedAudiences []string `json:"allowed_audiences"`
}

Config holds OIDC provider configuration

func LoadConfigFromEnv

func LoadConfigFromEnv() (*Config, error)

LoadConfigFromEnv loads OIDC configuration from environment variables

func (*Config) GetAllowedAudiences

func (c *Config) GetAllowedAudiences() []string

GetAllowedAudiences returns all valid audience values (ClientID + configured audiences)

func (*Config) Validate

func (c *Config) Validate() error

Validate checks that all required configuration is present

type DiscoveryClient

type DiscoveryClient struct {
	// contains filtered or unexported fields
}

DiscoveryClient fetches and caches OIDC discovery documents

func NewDiscoveryClient

func NewDiscoveryClient() *DiscoveryClient

NewDiscoveryClient creates a new discovery client with default settings

func NewDiscoveryClientWithHTTP

func NewDiscoveryClientWithHTTP(client *http.Client, cacheTTL time.Duration) *DiscoveryClient

NewDiscoveryClientWithHTTP creates a discovery client with custom HTTP client Useful for testing with mock servers

func (*DiscoveryClient) ClearCache

func (c *DiscoveryClient) ClearCache()

ClearCache removes all cached discovery documents

func (*DiscoveryClient) ClearCacheForIssuer

func (c *DiscoveryClient) ClearCacheForIssuer(issuer string)

ClearCacheForIssuer removes the cached discovery for a specific issuer

func (*DiscoveryClient) GetDiscovery

func (c *DiscoveryClient) GetDiscovery(ctx context.Context, issuer string) (*OIDCDiscovery, error)

GetDiscovery fetches the OIDC discovery document for an issuer. Results are cached for cacheTTL duration.

type ErrorResponse

type ErrorResponse struct {
	Error   string `json:"error"`
	Message string `json:"message"`
}

ErrorResponse represents an error response

type IDTokenClaims

type IDTokenClaims struct {
	// Standard claims
	Issuer    string `json:"iss"`
	Subject   string `json:"sub"`
	Audience  any    `json:"aud"` // Can be string or []string
	ExpiresAt int64  `json:"exp"`
	IssuedAt  int64  `json:"iat"`
	NotBefore int64  `json:"nbf,omitempty"`
	AuthTime  int64  `json:"auth_time,omitempty"`
	Nonce     string `json:"nonce,omitempty"`

	// Profile claims
	Name              string `json:"name,omitempty"`
	GivenName         string `json:"given_name,omitempty"`
	FamilyName        string `json:"family_name,omitempty"`
	PreferredUsername string `json:"preferred_username,omitempty"`
	Picture           string `json:"picture,omitempty"`

	// Email claims
	Email         string `json:"email,omitempty"`
	EmailVerified bool   `json:"email_verified,omitempty"`

	// Additional claims for role mapping
	Groups []string `json:"groups,omitempty"`
	Roles  []string `json:"roles,omitempty"`

	// Raw claims map for custom claim access
	Raw map[string]any `json:"-"`
}

IDTokenClaims represents standard OIDC ID token claims

func (*IDTokenClaims) GetAudiences

func (c *IDTokenClaims) GetAudiences() []string

GetAudiences returns the audience claim as a slice (handles both string and array)

type JWK

type JWK struct {
	Kty string `json:"kty"` // Key type (RSA, EC)
	Kid string `json:"kid"` // Key ID
	Use string `json:"use"` // Key use (sig)
	Alg string `json:"alg"` // Algorithm (RS256, RS384, RS512)

	// RSA key components
	N string `json:"n,omitempty"` // RSA modulus
	E string `json:"e,omitempty"` // RSA exponent

	// EC key components (for future support)
	Crv string `json:"crv,omitempty"` // Curve name
	X   string `json:"x,omitempty"`   // X coordinate
	Y   string `json:"y,omitempty"`   // Y coordinate
}

JWK represents a JSON Web Key

func FindKeyByKid

func FindKeyByKid(jwks *JWKS, kid string) *JWK

FindKeyByKid searches for a key by its kid in a JWKS

type JWKS

type JWKS struct {
	Keys []JWK `json:"keys"`
}

JWKS represents a JSON Web Key Set

type JWKSClient

type JWKSClient struct {
	// contains filtered or unexported fields
}

JWKSClient fetches and caches JSON Web Key Sets

func NewJWKSClient

func NewJWKSClient(cacheTTL time.Duration) *JWKSClient

NewJWKSClient creates a new JWKS client with default settings

func NewJWKSClientWithHTTP

func NewJWKSClientWithHTTP(client *http.Client, cacheTTL time.Duration) *JWKSClient

NewJWKSClientWithHTTP creates a JWKS client with custom HTTP client Useful for testing with mock servers

func (*JWKSClient) ClearCache

func (c *JWKSClient) ClearCache()

ClearCache removes all cached JWKS and keys

func (*JWKSClient) ClearCacheForURL

func (c *JWKSClient) ClearCacheForURL(jwksURL string)

ClearCacheForURL removes the cached JWKS for a specific URL

func (*JWKSClient) GetJWKS

func (c *JWKSClient) GetJWKS(ctx context.Context, jwksURL string) (*JWKS, error)

GetJWKS fetches the JWKS from the given URL, using cache if available

func (*JWKSClient) GetKey

func (c *JWKSClient) GetKey(ctx context.Context, jwksURL, kid string) (*rsa.PublicKey, error)

GetKey retrieves a specific key by kid from the JWKS If the key is not found, it will refresh the JWKS once and try again (handles key rotation)

type LoginResponse

type LoginResponse struct {
	AccessToken  string   `json:"access_token"`
	RefreshToken string   `json:"refresh_token"`
	TokenType    string   `json:"token_type"`
	ExpiresIn    int      `json:"expires_in"`
	User         UserInfo `json:"user"`
}

LoginResponse is returned after successful OIDC authentication

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"`
	ScopesSupported       []string `json:"scopes_supported"`
	ClaimsSupported       []string `json:"claims_supported"`
}

OIDCDiscovery represents the OIDC discovery document (.well-known/openid-configuration)

type OIDCHandler

type OIDCHandler struct {
	// contains filtered or unexported fields
}

OIDCHandler handles OIDC authentication endpoints

func NewOIDCHandler

func NewOIDCHandler(
	config *Config,
	userStore *auth.UserStore,
	jwtManager *auth.JWTManager,
) *OIDCHandler

NewOIDCHandler creates a new OIDC authentication handler

func NewOIDCHandlerWithClients

func NewOIDCHandlerWithClients(
	config *Config,
	discoveryClient *DiscoveryClient,
	tokenValidator *OIDCTokenValidator,
	userStore *auth.UserStore,
	jwtManager *auth.JWTManager,
	stateStore *StateStore,
	httpClient *http.Client,
) *OIDCHandler

NewOIDCHandlerWithClients creates an OIDC handler with custom clients (for testing)

func (*OIDCHandler) ServeHTTP

func (h *OIDCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler interface for routing

type OIDCTokenValidator

type OIDCTokenValidator struct {
	// contains filtered or unexported fields
}

OIDCTokenValidator validates OIDC ID tokens using RS256/384/512

func NewOIDCTokenValidator

func NewOIDCTokenValidator(config *Config) *OIDCTokenValidator

NewOIDCTokenValidator creates a new OIDC token validator

func NewOIDCTokenValidatorWithClients

func NewOIDCTokenValidatorWithClients(
	config *Config,
	discoveryClient *DiscoveryClient,
	jwksClient *JWKSClient,
) *OIDCTokenValidator

NewOIDCTokenValidatorWithClients creates a validator with custom clients (for testing)

func (*OIDCTokenValidator) GetOIDCClaims

func (v *OIDCTokenValidator) GetOIDCClaims(ctx context.Context, tokenString string) (*IDTokenClaims, error)

GetOIDCClaims extracts and returns the full OIDC claims from a token (useful for user provisioning)

func (*OIDCTokenValidator) Name

func (v *OIDCTokenValidator) Name() string

Name returns the validator name for logging/debugging. Implements the auth.TokenValidator interface.

func (*OIDCTokenValidator) ValidateToken

func (v *OIDCTokenValidator) ValidateToken(ctx context.Context, tokenString string) (*auth.Claims, error)

ValidateToken validates an OIDC ID token and returns GraphDB claims. Implements the auth.TokenValidator interface.

type RoleMapper

type RoleMapper struct {
	// contains filtered or unexported fields
}

RoleMapper maps OIDC claims to GraphDB roles

func NewRoleMapper

func NewRoleMapper(mappings []RoleMapping, defaultRole string) *RoleMapper

NewRoleMapper creates a new role mapper with the given mappings

func (*RoleMapper) MapRole

func (m *RoleMapper) MapRole(claims *IDTokenClaims) string

MapRole determines the GraphDB role based on OIDC claims. Mappings are evaluated in order; first match wins. Higher-privilege mappings should be listed first (admin before editor before viewer).

type RoleMapping

type RoleMapping struct {
	// ClaimName is the OIDC claim to check (e.g., "groups", "roles", "cognito:groups")
	ClaimName string `json:"claim_name"`

	// ClaimValues are the values that trigger this mapping (OR logic)
	ClaimValues []string `json:"claim_value"`

	// GraphDBRole is the target GraphDB role ("admin", "editor", "viewer")
	GraphDBRole string `json:"graphdb_role"`
}

RoleMapping defines how to map OIDC claims to GraphDB roles

type StateEntry

type StateEntry struct {
	CreatedAt time.Time
	Nonce     string
}

StateEntry holds CSRF state with expiration

type StateStore

type StateStore struct {
	// contains filtered or unexported fields
}

StateStore manages CSRF state for OAuth2 flows

func NewStateStore

func NewStateStore() *StateStore

NewStateStore creates a new state store with default settings

func NewStateStoreWithTTL

func NewStateStoreWithTTL(ttl time.Duration) *StateStore

NewStateStoreWithTTL creates a state store with custom TTL

func (*StateStore) Close

func (s *StateStore) Close()

Close stops the cleanup goroutine and releases resources

func (*StateStore) GenerateState

func (s *StateStore) GenerateState() (string, error)

GenerateState creates a new CSRF state token

func (*StateStore) Len

func (s *StateStore) Len() int

Len returns the number of pending state entries

func (*StateStore) ValidateAndConsume

func (s *StateStore) ValidateAndConsume(state string) (*StateEntry, bool)

ValidateAndConsume validates a state token and removes it (one-time use)

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,omitempty"`
	IDToken      string `json:"id_token"`
	Scope        string `json:"scope,omitempty"`
}

TokenResponse represents the response from the token endpoint

type TokenValidationRequest

type TokenValidationRequest struct {
	IDToken string `json:"id_token"`
}

TokenValidationRequest is the request body for POST /auth/oidc/token

type UserInfo

type UserInfo struct {
	ID          string `json:"id"`
	Username    string `json:"username"`
	Role        string `json:"role"`
	Email       string `json:"email,omitempty"`
	DisplayName string `json:"display_name,omitempty"`
}

UserInfo contains user details in the login response

Jump to

Keyboard shortcuts

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