Documentation
¶
Overview ¶
Package auth — /me/identities/link/* handlers (#383).
Package auth — /oauth2/step_up handler (#397).
Index ¶
- Constants
- Variables
- func AssembleOAuthProviders(settings []ProviderSetting) map[string]OAuthProviderConfig
- func AssembleSAMLProviders(settings []ProviderSetting) map[string]SAMLProviderConfig
- func BuildIdentityLinkAuthorizationURL(provider Provider, cfg OAuthProviderConfig, state string) (string, error)
- func BuildStepUpAuthorizationURL(provider Provider, cfg OAuthProviderConfig, state, loginHint string) (string, error)
- func ClearTokenCookies(c *gin.Context, opts CookieOptions)
- func ComputeS256Challenge(codeVerifier string) string
- func ExpectUserCreate(mock sqlmock.Sqlmock)
- func ExpectUserDelete(mock sqlmock.Sqlmock, userID string)
- func ExpectUserQuery(mock sqlmock.Sqlmock, email string, user *User)
- func ExpectUserUpdate(mock sqlmock.Sqlmock)
- func ExtractAccessTokenFromCookie(c *gin.Context) string
- func ExtractRefreshTokenFromCookie(c *gin.Context) string
- func GenerateCodeVerifier() (string, error)
- func GetDatabaseManager() *db.Managerdeprecated
- func HashToken(token string) string
- func InitAuth(router *gin.Engine) error
- func IntegrationExample()
- func MockEmptyUserRows(mock sqlmock.Sqlmock) *sqlmock.Rows
- func MockUserRow(mock sqlmock.Sqlmock, user User) *sqlmock.Rows
- func SetTokenCookies(c *gin.Context, tokenPair TokenPair, opts CookieOptions)
- func SetupMockDB(t *testing.T) (*sql.DB, sqlmock.Sqlmock)
- func SetupMockRedis(t *testing.T) (*redis.Client, *miniredis.Miniredis)
- func Shutdown(ctx context.Context) error
- func ValidateAllOAuthProviders(ctx context.Context, client *DiscoveryClient, ...) []string
- func ValidateClassifiedProvider(p ClassifiedProvider, cfg OAuthProviderConfig) []string
- func ValidateCodeChallenge(codeVerifier, codeChallenge, method string) error
- func ValidateCodeChallengeFormat(challenge string) error
- func ValidateCodeVerifierFormat(verifier string) error
- func ValidateOAuthProvider(p OAuthProviderConfig) []string
- func ValidateSAMLProvider(p SAMLProviderConfig) []string
- func ValidateTokenClaims(t *testing.T, claims *Claims, user User)
- type AdminChecker
- type AuthScenario
- type BaseProvider
- func (p *BaseProvider) ExchangeCode(ctx context.Context, code string) (*TokenResponse, error)
- func (p *BaseProvider) GetAuthorizationURL(state string) string
- func (p *BaseProvider) GetOAuth2Config() *oauth2.Config
- func (p *BaseProvider) GetUserInfo(ctx context.Context, accessToken string) (*UserInfo, error)
- func (p *BaseProvider) ValidateIDToken(ctx context.Context, idToken string) (*IDTokenClaims, error)
- type Claims
- type ClaimsEnricher
- type ClassifiedProvider
- type ClientCallbackAllowList
- type ClientCredential
- type ClientCredentialCreateParams
- type ClientCredentialTestCase
- type Config
- func (c *Config) GetEnabledProviders() []OAuthProviderConfig
- func (c *Config) GetJWTDuration() time.Duration
- func (c *Config) GetProvider(providerID string) (OAuthProviderConfig, bool)
- func (c *Config) ToGormConfig() db.GormConfig
- func (c *Config) ToRedisConfig() db.RedisConfig
- func (c *Config) ValidateConfig() error
- type CookieOptions
- type DatabaseConfig
- type DefaultProviderRegistry
- func (r *DefaultProviderRegistry) GetEnabledOAuthProviders() map[string]OAuthProviderConfig
- func (r *DefaultProviderRegistry) GetEnabledSAMLProviders() map[string]SAMLProviderConfig
- func (r *DefaultProviderRegistry) GetOAuthProvider(id string) (OAuthProviderConfig, bool)
- func (r *DefaultProviderRegistry) GetSAMLProvider(id string) (SAMLProviderConfig, bool)
- func (r *DefaultProviderRegistry) InvalidateCache()
- type DelegationContext
- type DeletionChallenge
- type DeletionResult
- type DiscoveryClient
- type GenericOIDCProvider
- type GormLinkedIdentityStore
- func (s *GormLinkedIdentityStore) Create(ctx context.Context, input LinkedIdentityInput) (models.LinkedIdentity, error)
- func (s *GormLinkedIdentityStore) CreateExclusive(ctx context.Context, input LinkedIdentityInput) (models.LinkedIdentity, error)
- func (s *GormLinkedIdentityStore) Delete(ctx context.Context, id, ownerUUID string) error
- func (s *GormLinkedIdentityStore) GetByProviderSub(ctx context.Context, provider, providerUserID string) (models.LinkedIdentity, error)
- func (s *GormLinkedIdentityStore) ListByUser(ctx context.Context, userInternalUUID string) ([]models.LinkedIdentity, error)
- func (s *GormLinkedIdentityStore) TouchLastUsed(ctx context.Context, id string) error
- type GroupDeletionResult
- type Handlers
- func (h *Handlers) Authorize(c *gin.Context)
- func (h *Handlers) Callback(c *gin.Context)
- func (h *Handlers) Config() Config
- func (h *Handlers) ConfirmIdentityLink(c *gin.Context)
- func (h *Handlers) Exchange(c *gin.Context)
- func (h *Handlers) GetJWKS(c *gin.Context)
- func (h *Handlers) GetOAuthAuthorizationServerMetadata(c *gin.Context)
- func (h *Handlers) GetOAuthProtectedResourceMetadata(c *gin.Context)
- func (h *Handlers) GetOpenIDConfiguration(c *gin.Context)
- func (h *Handlers) GetPendingIdentityLink(c *gin.Context)
- func (h *Handlers) GetProviders(c *gin.Context)
- func (h *Handlers) GetSAMLMetadata(c *gin.Context, providerID string)
- func (h *Handlers) GetSAMLProviders(c *gin.Context)
- func (h *Handlers) HandleIdentityLinkCallback(c *gin.Context, code string, stateData *callbackStateData) error
- func (h *Handlers) InitiateSAMLLogin(c *gin.Context, providerID string, clientCallback *string)
- func (h *Handlers) IntrospectToken(c *gin.Context)
- func (h *Handlers) Logout(c *gin.Context)
- func (h *Handlers) Me(c *gin.Context)
- func (h *Handlers) MeLogout(c *gin.Context)
- func (h *Handlers) ProcessSAMLLogout(c *gin.Context, providerID string, samlRequest string)
- func (h *Handlers) ProcessSAMLResponse(c *gin.Context, providerID string, samlResponse string, relayState string)
- func (h *Handlers) Refresh(c *gin.Context)
- func (h *Handlers) RevokeToken(c *gin.Context)
- func (h *Handlers) RuntimeConfigReader() RuntimeConfigReader
- func (h *Handlers) Service() *Service
- func (h *Handlers) SetAdminChecker(checker AdminChecker)
- func (h *Handlers) SetCookieOptions(opts CookieOptions)
- func (h *Handlers) SetIdentityLinkAuditor(a *IdentityLinkAuditor)
- func (h *Handlers) SetIdentityLinkStore(store LinkedIdentityStore)
- func (h *Handlers) SetProviderRegistry(registry ProviderRegistry)
- func (h *Handlers) SetRuntimeConfigReader(r RuntimeConfigReader)
- func (h *Handlers) SetStepUpAuditor(a *StepUpAuditor)
- func (h *Handlers) SetTokenLockout(l *OAuthTokenLockout)
- func (h *Handlers) SetUserGroupsFetcher(fetcher UserGroupsFetcher)
- func (h *Handlers) StartIdentityLink(c *gin.Context)
- func (h *Handlers) StepUp(c *gin.Context)
- func (h *Handlers) Token(c *gin.Context)
- type IDTokenClaims
- type IdentityLinkActor
- type IdentityLinkAuditor
- func (a *IdentityLinkAuditor) LogComplete(ctx context.Context, actor IdentityLinkActor, ...) error
- func (a *IdentityLinkAuditor) LogFailed(ctx context.Context, actor IdentityLinkActor, reason string, ...) error
- func (a *IdentityLinkAuditor) LogRejected(ctx context.Context, actor IdentityLinkActor, reason string, ...) error
- func (a *IdentityLinkAuditor) LogUnlink(ctx context.Context, actor IdentityLinkActor, linkedProvider, linkedSub string) error
- type InMemoryStateStore
- func (s *InMemoryStateStore) Close()
- func (s *InMemoryStateStore) DeletePKCEChallenge(ctx context.Context, state string) error
- func (s *InMemoryStateStore) DeleteState(ctx context.Context, state string) error
- func (s *InMemoryStateStore) GetCallbackURL(ctx context.Context, state string) (string, error)
- func (s *InMemoryStateStore) GetPKCEChallenge(ctx context.Context, state string) (challenge, method string, err error)
- func (s *InMemoryStateStore) StoreCallbackURL(ctx context.Context, state, callbackURL string, ttl time.Duration) error
- func (s *InMemoryStateStore) StorePKCEChallenge(ctx context.Context, state, codeChallenge, challengeMethod string, ...) error
- func (s *InMemoryStateStore) StoreState(ctx context.Context, state, data string, ttl time.Duration) error
- func (s *InMemoryStateStore) ValidateState(ctx context.Context, state string) (string, error)
- type JWK
- type JWKSResponse
- type JWTConfig
- type JWTKeyManager
- type LinkedIdentityInput
- type LinkedIdentityStore
- type LockoutDecision
- type OAuthAuthorizationServerMetadata
- type OAuthConfig
- type OAuthProtectedResourceMetadata
- type OAuthProviderConfig
- type OAuthTokenLockout
- type OIDCDiscoveryDoc
- type OpenIDConfiguration
- type Provider
- type ProviderClassification
- type ProviderInfo
- type ProviderRegistry
- type ProviderSetting
- type ProviderSettingsReader
- type RedisConfig
- type RuntimeConfigReader
- type SAMLConfig
- type SAMLManager
- func (m *SAMLManager) EnsureProvider(id string, config SAMLProviderConfig) error
- func (m *SAMLManager) GetProvider(id string) (*saml.SAMLProvider, error)
- func (m *SAMLManager) InitializeProviders(config SAMLConfig, stateStore StateStore) error
- func (m *SAMLManager) IsProviderInitialized(id string) bool
- func (m *SAMLManager) ListProviders() []string
- func (m *SAMLManager) ProcessSAMLResponse(ctx context.Context, providerID string, samlResponse string, relayState string) (*User, *TokenPair, error)
- type SAMLProviderConfig
- type SAMLProviderInfo
- type Service
- func (s *Service) BlacklistToken(ctx context.Context, tokenString string) error
- func (s *Service) CacheUser(ctx context.Context, user User) error
- func (s *Service) CacheUserGroups(ctx context.Context, email, idp string, groups []string) error
- func (s *Service) ClearUserGroups(ctx context.Context, email string) error
- func (s *Service) CreateClientCredential(ctx context.Context, params ClientCredentialCreateParams) (*ClientCredential, error)
- func (s *Service) CreateUser(ctx context.Context, user User) (User, error)
- func (s *Service) DeactivateClientCredential(ctx context.Context, id uuid.UUID, ownerUUID uuid.UUID) error
- func (s *Service) DeleteClientCredential(ctx context.Context, id uuid.UUID, ownerUUID uuid.UUID) error
- func (s *Service) DeleteGroupAndData(ctx context.Context, internalUUID string) (*GroupDeletionResult, error)
- func (s *Service) DeleteUser(ctx context.Context, id string) error
- func (s *Service) DeleteUserAndData(ctx context.Context, userEmail string) (*DeletionResult, error)
- func (s *Service) DeleteUserByInternalUUID(ctx context.Context, internalUUID string) (*DeletionResult, error)
- func (s *Service) GenerateDeletionChallenge(ctx context.Context, userEmail string) (*DeletionChallenge, error)
- func (s *Service) GenerateTokens(ctx context.Context, user User) (TokenPair, error)
- func (s *Service) GenerateTokensWithAuthTime(ctx context.Context, user User, userInfo *UserInfo, authTime time.Time) (TokenPair, error)
- func (s *Service) GenerateTokensWithUserInfo(ctx context.Context, user User, userInfo *UserInfo) (TokenPair, error)
- func (s *Service) GetCachedGroups(ctx context.Context, email string) (string, []string, error)
- func (s *Service) GetCachedUserByEmail(ctx context.Context, email string) (*User, error)
- func (s *Service) GetCachedUserByID(ctx context.Context, userID string) (*User, error)
- func (s *Service) GetCachedUserByProvider(ctx context.Context, provider, providerUserID string) (*User, error)
- func (s *Service) GetClientCredentialByClientID(ctx context.Context, clientID string) (*ClientCredential, error)
- func (s *Service) GetKeyManager() *JWTKeyManager
- func (s *Service) GetLinkedIdentityByProviderSub(ctx context.Context, provider, providerUserID string) (models.LinkedIdentity, error)
- func (s *Service) GetPrimaryProviderID(ctx context.Context, userID string) (string, error)
- func (s *Service) GetSAMLManager() *SAMLManager
- func (s *Service) GetUserByAnyProviderID(ctx context.Context, providerUserID string) (User, error)
- func (s *Service) GetUserByEmail(ctx context.Context, email string) (User, error)
- func (s *Service) GetUserByID(ctx context.Context, id string) (User, error)
- func (s *Service) GetUserByInternalUUID(ctx context.Context, uuid string) (User, error)
- func (s *Service) GetUserByProviderAndEmail(ctx context.Context, provider, email string) (User, error)
- func (s *Service) GetUserByProviderID(ctx context.Context, provider, providerUserID string) (User, error)
- func (s *Service) GetUserProviders(ctx context.Context, userID string) ([]UserProvider, error)
- func (s *Service) GormDB() *gorm.DB
- func (s *Service) HandleClientCredentialsGrant(ctx context.Context, clientID, clientSecret string) (*TokenPair, error)
- func (s *Service) InvalidateUserCache(ctx context.Context, user User) error
- func (s *Service) InvalidateUserSessions(ctx context.Context, userID string) error
- func (s *Service) IssueAddonDelegationToken(ctx context.Context, invoker *User, ...) (string, error)
- func (s *Service) ListClientCredentialsByOwner(ctx context.Context, ownerUUID uuid.UUID) ([]*ClientCredential, error)
- func (s *Service) RefreshToken(ctx context.Context, refreshToken string) (TokenPair, error)
- func (s *Service) RevokeToken(ctx context.Context, refreshToken string) error
- func (s *Service) SetClaimsEnricher(enricher ClaimsEnricher)
- func (s *Service) SetLinkedIdentityStore(store LinkedIdentityStore)
- func (s *Service) SetPreUserDeleteHook(h UserContentTokenRevoker)
- func (s *Service) SetProviderRegistry(registry ProviderRegistry)
- func (s *Service) TouchLinkedIdentityLastUsed(ctx context.Context, id string) error
- func (s *Service) TransferOwnership(ctx context.Context, sourceUserUUID, targetUserUUID string) (*TransferResult, error)
- func (s *Service) UpdateClientCredentialLastUsed(ctx context.Context, id uuid.UUID) error
- func (s *Service) UpdateUser(ctx context.Context, user User) error
- func (s *Service) ValidateDeletionChallenge(ctx context.Context, userEmail, challengeText string) error
- func (s *Service) ValidateToken(tokenString string) (*Claims, error)
- type StateStore
- type StepUpActor
- type StepUpAuditor
- func (a *StepUpAuditor) LogComplete(ctx context.Context, actor StepUpActor, strength StepUpStrength, ...) error
- func (a *StepUpAuditor) LogFailed(ctx context.Context, actor StepUpActor, reason string, ...) error
- func (a *StepUpAuditor) LogRejected(ctx context.Context, actor StepUpActor, reason string, ...) error
- type StepUpStrength
- type SystemAuditRecord
- type SystemAuditWriter
- type TestHelper
- type TestProvider
- func (p *TestProvider) ExchangeCode(ctx context.Context, code string) (*TokenResponse, error)
- func (p *TestProvider) GetAuthorizationURL(state string) string
- func (p *TestProvider) GetAuthorizationURLWithHint(state, hint string) string
- func (p *TestProvider) GetUserInfo(ctx context.Context, accessToken string) (*UserInfo, error)
- func (p *TestProvider) ValidateIDToken(ctx context.Context, idToken string) (*IDTokenClaims, error)
- type TokenBlacklist
- func (tb *TokenBlacklist) BlacklistToken(ctx context.Context, tokenString string) error
- func (tb *TokenBlacklist) CleanupExpiredTokens(ctx context.Context) error
- func (tb *TokenBlacklist) IsCredentialRevoked(ctx context.Context, credentialID string) (bool, error)
- func (tb *TokenBlacklist) IsTokenBlacklisted(ctx context.Context, tokenString string) (bool, error)
- func (tb *TokenBlacklist) IsTokenHashBlacklisted(ctx context.Context, tokenHash string) (bool, error)
- func (tb *TokenBlacklist) RevokeCredential(ctx context.Context, credentialID string, ttl time.Duration) error
- type TokenIntrospectionResponse
- type TokenPair
- type TokenResponse
- type TokenTestCase
- type TransferResult
- type User
- type UserContentTokenRevoker
- type UserGroupInfo
- type UserGroupsFetcher
- type UserInfo
- type UserInfoEndpoint
- type UserProvider
Constants ¶
const ( // AccessTokenCookieName is the cookie name for the JWT access token AccessTokenCookieName = "tmi_access_token" // RefreshTokenCookieName is the cookie name for the refresh token RefreshTokenCookieName = "tmi_refresh_token" // #nosec G101 -- cookie name constant, not a credential // MaxCookieSize is the practical browser cookie size limit MaxCookieSize = 4093 )
const ( // MinVerifierLength is the minimum length for a code verifier (43 characters) MinVerifierLength = 43 // MaxVerifierLength is the maximum length for a code verifier (128 characters) MaxVerifierLength = 128 // VerifierByteLength is the number of random bytes to generate (32 bytes = 43 base64url chars) VerifierByteLength = 32 )
PKCE constants per RFC 7636
const DefaultProviderCacheTTL = 60 * time.Second
DefaultProviderCacheTTL is the default TTL for the database provider cache.
const DelegationTokenTTL = 60 * time.Second
DelegationTokenTTL is the wall-clock budget a delegation token is valid for. The threat-model spec (T18) calls for a tight bound — the addon's invocation window — so that a leaked token has a small attack surface. 60 seconds matches the addon-invocation budget called out in #358.
const ( // UserCacheTTL defines how long user data is cached UserCacheTTL = 15 * time.Minute )
const (
// UserContextKey is the key for the user in the Gin context
UserContextKey contextKey = "user"
)
Variables ¶
var DefaultClaimMappings = map[string]string{
"subject_claim": "sub",
"email_claim": "email",
"name_claim": "name",
"given_name_claim": "given_name",
"family_name_claim": "family_name",
"picture_claim": "picture",
"email_verified_claim": "email_verified",
"groups_claim": "groups",
}
DefaultClaimMappings provides standard claim names for common OAuth providers
var ErrLinkedIdentityNotFound = errors.New("linked identity not found")
ErrLinkedIdentityNotFound is returned when a linked identity row is not found or the caller is not the owner.
var ErrRevocationStorage = errors.New("revocation storage unavailable")
ErrRevocationStorage marks a revocation that could not be recorded because the backing store (Redis) is unavailable or rejected the write (e.g. at the memory cap under noeviction). Callers fail closed (503) instead of reporting success.
var ErrUserNotFound = fmt.Errorf("user not found: %w", repository.ErrUserNotFound)
ErrUserNotFound is returned by Service user-lookup methods when no user matches. It wraps repository.ErrUserNotFound (itself wrapping dberrors.ErrNotFound) so callers can use errors.Is at any level instead of string-matching; the message keeps the historical "user not found" prefix that legacy substring checks rely on (#719).
var TestUsers = struct { Admin User Regular User External User }{ Admin: User{ InternalUUID: "admin-internal-uuid", Provider: "tmi", ProviderUserID: "admin@example.com", Email: "admin@example.com", Name: "Admin User", EmailVerified: true, Groups: []string{"admins"}, IsAdmin: true, CreatedAt: time.Now(), ModifiedAt: time.Now(), }, Regular: User{ InternalUUID: "regular-internal-uuid", Provider: "tmi", ProviderUserID: "user@example.com", Email: "user@example.com", Name: "Regular User", EmailVerified: true, Groups: []string{}, IsAdmin: false, CreatedAt: time.Now(), ModifiedAt: time.Now(), }, External: User{ InternalUUID: "external-internal-uuid", Provider: "google", ProviderUserID: "external@gmail.com", Email: "external@gmail.com", Name: "External User", EmailVerified: true, Groups: []string{}, IsAdmin: false, CreatedAt: time.Now(), ModifiedAt: time.Now(), }, }
TestUsers provides standard test user identities for auth testing
Functions ¶
func AssembleOAuthProviders ¶
func AssembleOAuthProviders(settings []ProviderSetting) map[string]OAuthProviderConfig
AssembleOAuthProviders groups settings by provider ID and assembles OAuthProviderConfig structs. Exported so the api package can use it for enable-validation.
func AssembleSAMLProviders ¶
func AssembleSAMLProviders(settings []ProviderSetting) map[string]SAMLProviderConfig
AssembleSAMLProviders groups settings by provider ID and assembles SAMLProviderConfig structs. Exported so the api package can use it for enable-validation.
func BuildIdentityLinkAuthorizationURL ¶
func BuildIdentityLinkAuthorizationURL(provider Provider, cfg OAuthProviderConfig, state string) (string, error)
BuildIdentityLinkAuthorizationURL builds the upstream authorize URL for an identity-link round-trip. Appends prompt=select_account to force account selection at the provider. Strong providers (those that honor prompt=consent) also get prompt="select_account consent" so the user explicitly re-authorizes scope grants. SAML providers are not supported for identity-link.
func BuildStepUpAuthorizationURL ¶
func BuildStepUpAuthorizationURL(provider Provider, cfg OAuthProviderConfig, state, loginHint string) (string, error)
BuildStepUpAuthorizationURL builds the upstream authorize URL for a step-up round-trip. For OAuth/OIDC providers it appends prompt=login and max_age=0 to the URL returned by provider.GetAuthorizationURL(state). SAML callers must not use this function; they call GetAuthorizationURLForceAuthn on the SAML provider directly.
func ClearTokenCookies ¶
func ClearTokenCookies(c *gin.Context, opts CookieOptions)
ClearTokenCookies clears both token cookies by setting MaxAge=-1. Cookie attributes (Path, Domain, HttpOnly, Secure, SameSite) must match the values used when setting for browsers to clear correctly.
func ComputeS256Challenge ¶
ComputeS256Challenge computes the S256 code challenge from a code verifier Returns base64url(SHA256(codeVerifier))
func ExpectUserCreate ¶
ExpectUserCreate sets up mock expectation for user creation
func ExpectUserDelete ¶
ExpectUserDelete sets up mock expectation for user deletion
func ExpectUserQuery ¶
ExpectUserQuery sets up mock expectation for a user query by email
func ExpectUserUpdate ¶
ExpectUserUpdate sets up mock expectation for user update
func ExtractAccessTokenFromCookie ¶
ExtractAccessTokenFromCookie returns the access token from the request cookie, or empty string if not present.
func ExtractRefreshTokenFromCookie ¶
ExtractRefreshTokenFromCookie returns the refresh token from the request cookie, or empty string if not present.
func GenerateCodeVerifier ¶
GenerateCodeVerifier generates a cryptographically secure random code verifier Returns a 43-character base64url-encoded string (32 random bytes)
func GetDatabaseManager
deprecated
func IntegrationExample ¶
func IntegrationExample()
IntegrationExample shows how to integrate the authentication system with the main application
func MockEmptyUserRows ¶
MockEmptyUserRows returns empty rows for user queries
func MockUserRow ¶
MockUserRow returns mock SQL rows for a user query
func SetTokenCookies ¶
func SetTokenCookies(c *gin.Context, tokenPair TokenPair, opts CookieOptions)
SetTokenCookies sets HttpOnly cookies for access and refresh tokens on the response. Both cookies are HttpOnly to prevent JavaScript access (XSS protection). The access token cookie uses SameSite=Lax (safe for REST APIs that don't mutate on GET). The refresh token cookie uses SameSite=Strict with Path=/oauth2 for maximum protection.
func SetupMockDB ¶
SetupMockDB creates a mock SQL database for testing
func SetupMockRedis ¶
SetupMockRedis creates a mock Redis client using miniredis
func ValidateAllOAuthProviders ¶
func ValidateAllOAuthProviders(ctx context.Context, client *DiscoveryClient, providers map[string]OAuthProviderConfig) []string
ValidateAllOAuthProviders classifies and validates every enabled OAuth provider. Returns a slice of human-readable error messages; an empty slice means all enabled providers are safe to start. Disabled providers are skipped.
func ValidateClassifiedProvider ¶
func ValidateClassifiedProvider(p ClassifiedProvider, cfg OAuthProviderConfig) []string
ValidateClassifiedProvider returns a slice of error messages describing reasons the provider config is not safe to enable. An empty slice means the provider is OK.
func ValidateCodeChallenge ¶
ValidateCodeChallenge validates that a code verifier matches the code challenge Uses constant-time comparison to prevent timing attacks
func ValidateCodeChallengeFormat ¶
ValidateCodeChallengeFormat validates the format of a code challenge
func ValidateCodeVerifierFormat ¶
ValidateCodeVerifierFormat validates the format of a code verifier
func ValidateOAuthProvider ¶
func ValidateOAuthProvider(p OAuthProviderConfig) []string
ValidateOAuthProvider checks that required fields are present for an enabled OAuth provider. Returns a list of missing field names, or nil if valid.
func ValidateSAMLProvider ¶
func ValidateSAMLProvider(p SAMLProviderConfig) []string
ValidateSAMLProvider checks that required fields are present for an enabled SAML provider. Returns a list of missing field names, or nil if valid.
Types ¶
type AdminChecker ¶
type AdminChecker interface {
IsAdmin(ctx context.Context, userInternalUUID *string, provider string, groupUUIDs []string) (bool, error)
IsSecurityReviewer(ctx context.Context, userInternalUUID *string, provider string, groupUUIDs []string) (bool, error)
GetGroupUUIDsByNames(ctx context.Context, provider string, groupNames []string) ([]string, error)
}
AdminChecker is an interface for checking if a user is an administrator or security reviewer
type AuthScenario ¶
type AuthScenario struct {
Name string
User User
RequiredRole string
ExpectedAccess bool
SetupContext func(context.Context) context.Context
}
AuthScenario represents an authorization test scenario
type BaseProvider ¶
type BaseProvider struct {
// contains filtered or unexported fields
}
BaseProvider provides common functionality for all providers
func NewBaseProvider ¶
func NewBaseProvider(config OAuthProviderConfig, callbackURL string) (*BaseProvider, error)
NewBaseProvider creates a new base OAuth provider
func (*BaseProvider) ExchangeCode ¶
func (p *BaseProvider) ExchangeCode(ctx context.Context, code string) (*TokenResponse, error)
ExchangeCode exchanges an authorization code for tokens
func (*BaseProvider) GetAuthorizationURL ¶
func (p *BaseProvider) GetAuthorizationURL(state string) string
GetAuthorizationURL returns the authorization URL with the given state
func (*BaseProvider) GetOAuth2Config ¶
func (p *BaseProvider) GetOAuth2Config() *oauth2.Config
GetOAuth2Config returns the OAuth2 configuration
func (*BaseProvider) GetUserInfo ¶
GetUserInfo gets user information from the provider
func (*BaseProvider) ValidateIDToken ¶
func (p *BaseProvider) ValidateIDToken(ctx context.Context, idToken string) (*IDTokenClaims, error)
ValidateIDToken validates an ID token
type Claims ¶
type Claims struct {
Email string `json:"email"`
EmailVerified bool `json:"email_verified,omitempty"`
Name string `json:"name"`
IdentityProvider string `json:"idp,omitempty"` // Identity provider
Groups []string `json:"groups,omitempty"` // User's groups from IdP
IsAdministrator *bool `json:"tmi_is_administrator,omitempty"` // TMI Administrators group membership
IsSecurityReviewer *bool `json:"tmi_is_security_reviewer,omitempty"` // TMI Security Reviewers group membership
Delegation *DelegationContext `json:"delegation,omitempty"` // T18: scoped delegation token for addon invocations
DirectWrite *bool `json:"tmi_direct_write,omitempty"` // #856: SA token may pass the invoker-only gate (owner's roles still apply)
AddonID string `json:"tmi_addon_id,omitempty"` // #883: addon whose subscription must not receive events caused by this token
ServiceAccount bool `json:"tmi_service_account,omitempty"` // set only on client-credentials tokens; an "sa:" subject without it is rejected
// AuthTime is the timestamp (Unix seconds) of the user's last interactive
// IdP authentication. OIDC-standard claim. #355 step-up middleware reads
// this to decide whether a /admin/* write requires re-authentication.
// Refresh-token rotation preserves this value (refresh proves possession
// of the refresh token, not freshness of the human).
AuthTime *jwt.NumericDate `json:"auth_time,omitempty"`
jwt.RegisteredClaims
}
Claims represents the JWT claims
type ClaimsEnricher ¶
type ClaimsEnricher interface {
// EnrichClaims checks built-in group membership and resolves TMI-managed group names for a user.
// Returns whether the user is an administrator, security reviewer, and the user's TMI group names.
EnrichClaims(ctx context.Context, userInternalUUID string, provider string, groupNames []string) (isAdmin bool, isSecurityReviewer bool, tmiGroupNames []string, err error)
// IsAdministrator reports Administrators membership (direct or via nested
// TMI-managed groups) for a user id alone; used to gate direct_write (#856).
IsAdministrator(ctx context.Context, userInternalUUID string) (bool, error)
}
ClaimsEnricher enriches JWT claims with application-specific data (e.g., group membership) that cannot be directly accessed from the auth package without creating circular dependencies.
type ClassifiedProvider ¶
type ClassifiedProvider struct {
ProviderID string
Classification ProviderClassification
DiscoveryDoc *OIDCDiscoveryDoc // nil for ClassificationNonOIDC
}
func ClassifyProvider ¶
func ClassifyProvider(ctx context.Context, client *DiscoveryClient, providerID string, cfg OAuthProviderConfig) ClassifiedProvider
ClassifyProvider buckets a provider based on discovery probe results and configured userinfo URL. Compares the *primary* userinfo endpoint only (cfg.UserInfo[0]); secondary/additional endpoints are operator extensions and never affect classification.
type ClientCallbackAllowList ¶
type ClientCallbackAllowList struct {
// contains filtered or unexported fields
}
ClientCallbackAllowList validates client_callback URLs supplied to /oauth2/authorize against a configured set of allowed patterns:
- "http://host/cb" exact match
- "http://host/*" prefix match (the part before "*"); the URL's host must also equal the pattern's host exactly
- "http://127.0.0.1:*" loopback, any port, any path (RFC 8252 §7.3)
- "http://127.0.0.1:*/cb" loopback, any port, exact path ("/cb*" = path prefix)
The ":*" port wildcard is honored only for loopback hosts (127.0.0.1, [::1], localhost); anywhere else the pattern matches nothing.
Every candidate URL must parse as an absolute URL with a host and no userinfo, whatever the pattern. Without that, a prefix such as "http://127.0.0.1:" would admit "http://127.0.0.1:1@evil.example/", whose real host is evil.example.
An allowlist with zero patterns rejects every URL (fail-closed). This closes the open-redirect / OAuth phishing surface (T16) by ensuring an attacker cannot smuggle a malicious client_callback through the authorize endpoint.
func NewClientCallbackAllowList ¶
func NewClientCallbackAllowList(patterns []string) *ClientCallbackAllowList
NewClientCallbackAllowList creates an allow-list from the given URL patterns. Empty entries are dropped.
func (*ClientCallbackAllowList) Allowed ¶
func (a *ClientCallbackAllowList) Allowed(rawURL string) bool
Allowed returns true if rawURL matches at least one configured pattern. An empty allowlist always returns false (fail-closed).
func (*ClientCallbackAllowList) Configured ¶
func (a *ClientCallbackAllowList) Configured() bool
Configured returns true if the allowlist has at least one pattern. Used by /oauth2/authorize to surface a startup warning when the allowlist is empty.
type ClientCredential ¶
type ClientCredential struct {
ID uuid.UUID
OwnerUUID uuid.UUID
ClientID string
ClientSecretHash string
Name string
Description string
IsActive bool
DirectWrite bool
AddonID string // linked addon for self-delivery suppression (#883); empty when unlinked
LastUsedAt *time.Time
CreatedAt time.Time
ModifiedAt time.Time
ExpiresAt *time.Time
}
ClientCredential represents an OAuth 2.0 client credential for machine-to-machine authentication
func CreateTestClientCredential ¶
func CreateTestClientCredential(ownerUUID uuid.UUID, name string) *ClientCredential
CreateTestClientCredential creates a test client credential
type ClientCredentialCreateParams ¶
type ClientCredentialCreateParams struct {
OwnerUUID uuid.UUID
ClientID string
ClientSecretHash string
Name string
Description string
DirectWrite bool
AddonID string
ExpiresAt *time.Time
}
ClientCredentialCreateParams contains parameters for creating a new client credential
type ClientCredentialTestCase ¶
type ClientCredentialTestCase struct {
Name string
ClientID string
ClientSecret string
ExpectSuccess bool
ExpectedError string
SetupMock func(sqlmock.Sqlmock)
VerifyResult func(*testing.T, *TokenPair)
}
ClientCredentialTestCase represents a test case for client credential operations
type Config ¶
type Config struct {
Database DatabaseConfig // Database config with URL-based connection string
Redis RedisConfig
JWT JWTConfig
OAuth OAuthConfig
SAML SAMLConfig
BuildMode string // dev, test, or production
}
Config holds all authentication configuration
func ConfigFromUnified ¶
ConfigFromUnified converts unified config to auth-specific config
func LoadConfig ¶
LoadConfig loads configuration from environment variables.
func (*Config) GetEnabledProviders ¶
func (c *Config) GetEnabledProviders() []OAuthProviderConfig
GetEnabledProviders returns a slice of enabled OAuth providers
func (*Config) GetJWTDuration ¶
GetJWTDuration returns the JWT expiration duration
func (*Config) GetProvider ¶
func (c *Config) GetProvider(providerID string) (OAuthProviderConfig, bool)
GetProvider returns a specific OAuth provider configuration
func (*Config) ToGormConfig ¶
func (c *Config) ToGormConfig() db.GormConfig
ToGormConfig converts Config to db.GormConfig for GORM database connections. It parses the DATABASE_URL to extract connection parameters.
func (*Config) ToRedisConfig ¶
func (c *Config) ToRedisConfig() db.RedisConfig
ToRedisConfig converts Config to db.RedisConfig
func (*Config) ValidateConfig ¶
ValidateConfig validates the configuration
type CookieOptions ¶
type CookieOptions struct {
Domain string // Cookie domain (hostname)
Secure bool // Require HTTPS
Enabled bool // Whether cookie-based auth is enabled
ExpiresIn int // Access token cookie MaxAge in seconds
RefreshTTL int // Refresh token cookie MaxAge in seconds
}
CookieOptions holds configuration for session cookie operations
type DatabaseConfig ¶
type DatabaseConfig struct {
URL string // DATABASE_URL - contains all connection parameters
OracleWalletLocation string // path to Oracle wallet for ADB (cannot be in URL)
// Connection pool configuration
MaxOpenConns int // Maximum open connections (default: 10)
MaxIdleConns int // Maximum idle connections (default: 2)
ConnMaxLifetime int // Max connection lifetime in seconds (default: 240)
ConnMaxIdleTime int // Max idle time in seconds (default: 30)
}
DatabaseConfig holds unified database configuration. Database type is determined from the URL scheme (postgres://, mysql://, etc.)
type DefaultProviderRegistry ¶
type DefaultProviderRegistry struct {
// contains filtered or unexported fields
}
DefaultProviderRegistry merges immutable config/env providers with mutable database-sourced providers assembled from system_settings rows.
func NewDefaultProviderRegistry ¶
func NewDefaultProviderRegistry( configOAuth map[string]OAuthProviderConfig, configSAML map[string]SAMLProviderConfig, settings ProviderSettingsReader, ) *DefaultProviderRegistry
NewDefaultProviderRegistry creates a new DefaultProviderRegistry with the given config/env providers and a settings reader for database-sourced providers.
func (*DefaultProviderRegistry) GetEnabledOAuthProviders ¶
func (r *DefaultProviderRegistry) GetEnabledOAuthProviders() map[string]OAuthProviderConfig
GetEnabledOAuthProviders returns all enabled OAuth providers from all sources. Config/env providers shadow database-sourced providers with the same ID.
func (*DefaultProviderRegistry) GetEnabledSAMLProviders ¶
func (r *DefaultProviderRegistry) GetEnabledSAMLProviders() map[string]SAMLProviderConfig
GetEnabledSAMLProviders returns all enabled SAML providers from all sources. Config/env providers shadow database-sourced providers with the same ID.
func (*DefaultProviderRegistry) GetOAuthProvider ¶
func (r *DefaultProviderRegistry) GetOAuthProvider(id string) (OAuthProviderConfig, bool)
GetOAuthProvider returns the OAuth provider configuration for the given ID. Config/env providers take precedence over database-sourced providers.
func (*DefaultProviderRegistry) GetSAMLProvider ¶
func (r *DefaultProviderRegistry) GetSAMLProvider(id string) (SAMLProviderConfig, bool)
GetSAMLProvider returns the SAML provider configuration for the given ID. Config/env providers take precedence over database-sourced providers.
func (*DefaultProviderRegistry) InvalidateCache ¶
func (r *DefaultProviderRegistry) InvalidateCache()
InvalidateCache marks the database provider cache as dirty so it will be refreshed on the next access.
type DelegationContext ¶
type DelegationContext struct {
// AddonID is the addon being invoked.
AddonID string `json:"addon_id"`
// DeliveryID is the unique webhook delivery record this token was
// minted for. Replays of the same delivery will mint distinct tokens
// (one per attempt), all sharing this DeliveryID.
DeliveryID string `json:"delivery_id"`
// ThreatModelID is the parent threat model the invocation targets.
// Writes to other threat models with this token are out of scope; a
// future hardening pass can add per-resource allowlist enforcement
// (the schema field `subject_authority: invoker` is the route-level
// gate today; the resource-level scope check is residual scope).
ThreatModelID string `json:"threat_model_id"`
}
DelegationContext is the addon-invocation scope embedded in a delegation JWT (T18, #358). Its presence on a token means: "this token impersonates the invoker for the duration of one specific addon invocation against one specific threat model — do not allow it to escape that scope".
Routes that addons hit on the write-back path declare `x-tmi-authz: { subject_authority: "invoker" }` to require this token shape (and to reject service-account-only tokens). The token's `sub` is the invoker's provider_user_id; the rest of the user-identity claims (email, name, provider, groups, tmi_is_security_reviewer) are copied from the invoker so existing handler code reads the invoker's identity transparently.
type DeletionChallenge ¶
type DeletionChallenge struct {
ChallengeText string `json:"challenge_text"`
ExpiresAt time.Time `json:"expires_at"`
}
DeletionChallenge contains challenge information for user deletion
type DeletionResult ¶
type DeletionResult struct {
ThreatModelsTransferred int `json:"threat_models_transferred"`
ThreatModelsDeleted int `json:"threat_models_deleted"`
UserEmail string `json:"user_email"`
}
DeletionResult contains statistics about the user deletion operation
type DiscoveryClient ¶
type DiscoveryClient struct {
// contains filtered or unexported fields
}
func NewDiscoveryClient ¶
func NewDiscoveryClient(timeout, cacheTTL time.Duration) *DiscoveryClient
func (*DiscoveryClient) Discover ¶
func (c *DiscoveryClient) Discover(ctx context.Context, issuerURL string) (*OIDCDiscoveryDoc, error)
Discover fetches and validates the OIDC discovery doc for issuerURL. Returns a valid doc on success. Returns (nil, nil) when the issuer is not OIDC-compliant (404, network error, invalid JSON, or doc fails IsValid) — callers should treat nil-doc as "not OIDC" rather than as an error. Returns (nil, err) only for programmer errors (e.g. invalid issuerURL).
Concurrent first-fetches for the same issuer collapse into a single upstream request via singleflight (#292); subsequent calls hit the cache.
type GenericOIDCProvider ¶
type GenericOIDCProvider struct {
BaseProvider
// contains filtered or unexported fields
}
GenericOIDCProvider is a generic OIDC provider
func NewGenericOIDCProvider ¶
func NewGenericOIDCProvider(config OAuthProviderConfig, callbackURL string) (*GenericOIDCProvider, error)
NewGenericOIDCProvider creates a new generic OIDC provider
func (*GenericOIDCProvider) ValidateIDToken ¶
func (p *GenericOIDCProvider) ValidateIDToken(ctx context.Context, idToken string) (*IDTokenClaims, error)
ValidateIDToken validates an ID token
type GormLinkedIdentityStore ¶
type GormLinkedIdentityStore struct {
// contains filtered or unexported fields
}
GormLinkedIdentityStore is the GORM-backed implementation of LinkedIdentityStore.
func NewGormLinkedIdentityStore ¶
func NewGormLinkedIdentityStore(db *gorm.DB) *GormLinkedIdentityStore
NewGormLinkedIdentityStore returns a new GormLinkedIdentityStore.
func (*GormLinkedIdentityStore) Create ¶
func (s *GormLinkedIdentityStore) Create(ctx context.Context, input LinkedIdentityInput) (models.LinkedIdentity, error)
Create inserts a new linked identity row.
func (*GormLinkedIdentityStore) CreateExclusive ¶
func (s *GormLinkedIdentityStore) CreateExclusive(ctx context.Context, input LinkedIdentityInput) (models.LinkedIdentity, error)
CreateExclusive performs a check-then-create inside a single serializable transaction, eliminating the TOCTOU race that exists when the re-check and the insert run in separate statements. The unique index remains the final backstop; this method surfaces the conflict as dberrors.ErrDuplicate before reaching the constraint so that callers get a typed error in both the serializable-read-caught and the constraint-caught paths.
PKCE protects a public-client code exchange that does not exist in the link flow; the pending-token + UUID-matched step-up-fresh confirm is the binding mechanism. A serializable transaction here is the correct concurrency guard.
func (*GormLinkedIdentityStore) Delete ¶
func (s *GormLinkedIdentityStore) Delete(ctx context.Context, id, ownerUUID string) error
Delete removes the linked identity identified by id, scoped to ownerUUID.
func (*GormLinkedIdentityStore) GetByProviderSub ¶
func (s *GormLinkedIdentityStore) GetByProviderSub(ctx context.Context, provider, providerUserID string) (models.LinkedIdentity, error)
GetByProviderSub looks up a linked identity by provider and provider-user-id.
func (*GormLinkedIdentityStore) ListByUser ¶
func (s *GormLinkedIdentityStore) ListByUser(ctx context.Context, userInternalUUID string) ([]models.LinkedIdentity, error)
ListByUser returns all linked identities owned by userInternalUUID.
func (*GormLinkedIdentityStore) TouchLastUsed ¶
func (s *GormLinkedIdentityStore) TouchLastUsed(ctx context.Context, id string) error
TouchLastUsed updates last_used_at to now for the given identity id.
type GroupDeletionResult ¶
type GroupDeletionResult struct {
ThreatModelsDeleted int `json:"threat_models_deleted"`
ThreatModelsRetained int `json:"threat_models_retained"`
GroupName string `json:"group_name"`
}
GroupDeletionResult contains statistics about the group deletion operation
type Handlers ¶
type Handlers struct {
// contains filtered or unexported fields
}
Handlers provides HTTP handlers for authentication
func InitAuthWithConfig
deprecated
InitAuthWithConfig initializes the auth system with unified configuration.
Deprecated: Use InitAuthWithDB for explicit dependency injection. This function creates its own database manager internally, which can lead to duplicate initialization and DRY violations. Prefer passing a pre-initialized db.Manager to InitAuthWithDB instead.
func InitAuthWithDB ¶
InitAuthWithDB initializes the auth system with an existing database manager. This is the preferred initialization method for explicit dependency injection. The caller is responsible for initializing the database connections before calling this function.
func NewHandlers ¶
NewHandlers creates new authentication handlers
func (*Handlers) ConfirmIdentityLink ¶
ConfirmIdentityLink handles POST /me/identities/link/confirm. Consumes the one-time pending link token and inserts the linked identity row. Returns 201 with the new LinkedIdentity on success.
func (*Handlers) Exchange ¶
Exchange exchanges an authorization code for tokens (legacy endpoint, delegates to handleAuthorizationCodeGrant)
func (*Handlers) GetOAuthAuthorizationServerMetadata ¶
GetOAuthAuthorizationServerMetadata returns OAuth 2.0 Authorization Server metadata
func (*Handlers) GetOAuthProtectedResourceMetadata ¶
GetOAuthProtectedResourceMetadata returns OAuth 2.0 protected resource metadata as per RFC 9728
func (*Handlers) GetOpenIDConfiguration ¶
GetOpenIDConfiguration returns OpenID Connect Discovery metadata
func (*Handlers) GetPendingIdentityLink ¶
GetPendingIdentityLink handles GET /me/identities/link/pending/{link_id}. Returns the pending link details (both sides) if the token exists and belongs to the authenticated user. Returns 404 on any mismatch.
func (*Handlers) GetProviders ¶
GetProviders returns the available OAuth providers
func (*Handlers) GetSAMLMetadata ¶
GetSAMLMetadata returns SAML service provider metadata
func (*Handlers) GetSAMLProviders ¶
GetSAMLProviders returns the available SAML providers
func (*Handlers) HandleIdentityLinkCallback ¶
func (h *Handlers) HandleIdentityLinkCallback(c *gin.Context, code string, stateData *callbackStateData) error
HandleIdentityLinkCallback is called from the shared Callback handler when stateData.IdentityLink is true. It performs a server-side code exchange to obtain the provider's user info (provider, sub, email, name) WITHOUT storing the IdP tokens. It stages a pending link record in Redis and redirects to the client_callback with link_pending={token}.
func (*Handlers) InitiateSAMLLogin ¶
InitiateSAMLLogin starts SAML authentication flow
func (*Handlers) IntrospectToken ¶
IntrospectToken handles token introspection requests per RFC 7662
func (*Handlers) Logout ¶
Logout is deprecated - use RevokeToken for RFC 7009 compliance or MeLogout for self-logout Kept for backward compatibility, delegates to MeLogout
func (*Handlers) MeLogout ¶
MeLogout revokes the caller's own JWT token This is a convenience endpoint that doesn't require passing the token in the body
func (*Handlers) ProcessSAMLLogout ¶
ProcessSAMLLogout handles SAML single logout
func (*Handlers) ProcessSAMLResponse ¶
func (h *Handlers) ProcessSAMLResponse(c *gin.Context, providerID string, samlResponse string, relayState string)
ProcessSAMLResponse handles SAML assertion consumer service
func (*Handlers) RevokeToken ¶
RevokeToken revokes a token per RFC 7009 OAuth 2.0 Token Revocation The token to revoke is passed in the request body, not the Authorization header. Authentication: Bearer token OR client credentials (client_id/client_secret)
func (*Handlers) RuntimeConfigReader ¶
func (h *Handlers) RuntimeConfigReader() RuntimeConfigReader
RuntimeConfigReader returns the wired reader, or nil if none was set.
Exposed so components built outside the auth package — the JWT authenticator in cmd/server, which needs auth.everyone_is_a_reviewer — can resolve operational settings through the same reader rather than reading a boot-time config snapshot that ignores the database (#794).
func (*Handlers) SetAdminChecker ¶
func (h *Handlers) SetAdminChecker(checker AdminChecker)
SetAdminChecker sets the admin checker for the handlers
func (*Handlers) SetCookieOptions ¶
func (h *Handlers) SetCookieOptions(opts CookieOptions)
SetCookieOptions sets the cookie configuration for session cookie management
func (*Handlers) SetIdentityLinkAuditor ¶
func (h *Handlers) SetIdentityLinkAuditor(a *IdentityLinkAuditor)
SetIdentityLinkAuditor wires the identity-link audit writer. Safe to call multiple times; nil disables identity-link auditing. #383.
func (*Handlers) SetIdentityLinkStore ¶
func (h *Handlers) SetIdentityLinkStore(store LinkedIdentityStore)
SetIdentityLinkStore wires the linked identity store. Safe to call multiple times; nil disables server-side identity lookups during link operations. #383.
func (*Handlers) SetProviderRegistry ¶
func (h *Handlers) SetProviderRegistry(registry ProviderRegistry)
SetProviderRegistry sets the provider registry for unified provider lookup.
func (*Handlers) SetRuntimeConfigReader ¶
func (h *Handlers) SetRuntimeConfigReader(r RuntimeConfigReader)
SetRuntimeConfigReader wires the DB-backed operational config reader used by handlers at request time. Safe to call multiple times. A nil reader makes handlers fall back to the YAML snapshot in h.config — see RuntimeConfigReader for the contract. (#419)
func (*Handlers) SetStepUpAuditor ¶
func (h *Handlers) SetStepUpAuditor(a *StepUpAuditor)
SetStepUpAuditor wires the step-up audit writer. Safe to call multiple times; nil disables step-up auditing (used in tests AND in production when admin-audit middleware is disabled — see cmd/server/main.go). #397.
func (*Handlers) SetTokenLockout ¶
func (h *Handlers) SetTokenLockout(l *OAuthTokenLockout)
SetTokenLockout overrides the per-client_id lockout. Used in tests.
func (*Handlers) SetUserGroupsFetcher ¶
func (h *Handlers) SetUserGroupsFetcher(fetcher UserGroupsFetcher)
SetUserGroupsFetcher sets the user groups fetcher for the handlers
func (*Handlers) StartIdentityLink ¶
StartIdentityLink handles POST /me/identities/link/start. It validates the request, builds OAuth state, stores it in Redis, and returns the authorization URL + state token for the client to use.
type IDTokenClaims ¶
type IDTokenClaims struct {
Subject string `json:"sub"`
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
Name string `json:"name,omitempty"`
GivenName string `json:"given_name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
Picture string `json:"picture,omitempty"`
Locale string `json:"locale,omitempty"`
Issuer string `json:"iss"`
Audience string `json:"aud"`
ExpiresAt int64 `json:"exp"`
IssuedAt int64 `json:"iat"`
}
IDTokenClaims contains the claims from an ID token
type IdentityLinkActor ¶
type IdentityLinkActor struct {
Email string
Provider string
ProviderUserID string
DisplayName string
UserUUID string
}
IdentityLinkActor identifies the user performing a link operation. All four identity fields are denormalized into the audit row (matches SystemAuditEntry pattern; rows survive user deletion).
type IdentityLinkAuditor ¶
type IdentityLinkAuditor struct {
// contains filtered or unexported fields
}
IdentityLinkAuditor wraps a SystemAuditWriter with the field shapes specific to identity-link events. Fail-open: write failures are logged but do not propagate.
func NewIdentityLinkAuditor ¶
func NewIdentityLinkAuditor(writer SystemAuditWriter) *IdentityLinkAuditor
NewIdentityLinkAuditor returns an auditor. writer may be nil (in which case audit calls are no-ops with a debug log; matches the existing fail-open posture).
func (*IdentityLinkAuditor) LogComplete ¶
func (a *IdentityLinkAuditor) LogComplete( ctx context.Context, actor IdentityLinkActor, accountProvider, accountSub, linkedProvider, linkedSub string, ) error
LogComplete records a successful identity-link completion. Both sides' (provider, sub) are redacted in the audit payload.
func (*IdentityLinkAuditor) LogFailed ¶
func (a *IdentityLinkAuditor) LogFailed( ctx context.Context, actor IdentityLinkActor, reason string, extras map[string]string, ) error
LogFailed records an identity-link attempt that failed (e.g. upstream error, code-exchange failure). reason is a short stable code. extras are inlined into the payload.
func (*IdentityLinkAuditor) LogRejected ¶
func (a *IdentityLinkAuditor) LogRejected( ctx context.Context, actor IdentityLinkActor, reason string, extras map[string]string, ) error
LogRejected records an identity-link attempt that was rejected before any upstream round-trip (e.g. service-account caller, already-bound identity).
func (*IdentityLinkAuditor) LogUnlink ¶
func (a *IdentityLinkAuditor) LogUnlink( ctx context.Context, actor IdentityLinkActor, linkedProvider, linkedSub string, ) error
LogUnlink records the removal of a linked identity. Both sides' (provider, sub) are redacted in the audit payload.
type InMemoryStateStore ¶
type InMemoryStateStore struct {
// contains filtered or unexported fields
}
InMemoryStateStore implements StateStore using in-memory storage
func NewInMemoryStateStore ¶
func NewInMemoryStateStore() *InMemoryStateStore
NewInMemoryStateStore creates a new in-memory state store
func (*InMemoryStateStore) Close ¶
func (s *InMemoryStateStore) Close()
Close stops the cleanup goroutine
func (*InMemoryStateStore) DeletePKCEChallenge ¶
func (s *InMemoryStateStore) DeletePKCEChallenge(ctx context.Context, state string) error
DeletePKCEChallenge removes PKCE challenge from store
func (*InMemoryStateStore) DeleteState ¶
func (s *InMemoryStateStore) DeleteState(ctx context.Context, state string) error
DeleteState removes a state from the store
func (*InMemoryStateStore) GetCallbackURL ¶
GetCallbackURL retrieves the callback URL for a state
func (*InMemoryStateStore) GetPKCEChallenge ¶
func (s *InMemoryStateStore) GetPKCEChallenge(ctx context.Context, state string) (challenge, method string, err error)
GetPKCEChallenge retrieves PKCE code challenge and method for a state
func (*InMemoryStateStore) StoreCallbackURL ¶
func (s *InMemoryStateStore) StoreCallbackURL(ctx context.Context, state, callbackURL string, ttl time.Duration) error
StoreCallbackURL stores a callback URL with a state
func (*InMemoryStateStore) StorePKCEChallenge ¶
func (s *InMemoryStateStore) StorePKCEChallenge(ctx context.Context, state, codeChallenge, challengeMethod string, ttl time.Duration) error
StorePKCEChallenge stores PKCE code challenge with associated method
func (*InMemoryStateStore) StoreState ¶
func (s *InMemoryStateStore) StoreState(ctx context.Context, state, data string, ttl time.Duration) error
StoreState stores state with associated data
func (*InMemoryStateStore) ValidateState ¶
ValidateState validates state and returns associated data
type JWK ¶
type JWK struct {
KeyType string `json:"kty"`
Use string `json:"use,omitempty"`
KeyOps []string `json:"key_ops,omitempty"`
KeyID string `json:"kid,omitempty"`
Algorithm string `json:"alg,omitempty"`
// RSA parameters
N string `json:"n,omitempty"` // RSA modulus
E string `json:"e,omitempty"` // RSA exponent
// ECDSA parameters
Curve string `json:"crv,omitempty"` // Elliptic curve
X string `json:"x,omitempty"` // X coordinate
Y string `json:"y,omitempty"` // Y coordinate
}
JWK represents a JSON Web Key
type JWKSResponse ¶
type JWKSResponse struct {
Keys []JWK `json:"keys"`
}
JWKSResponse represents a JSON Web Key Set response
type JWTConfig ¶
type JWTConfig struct {
Secret string
ExpirationSeconds int
SigningMethod string // HS256, RS256, ES256
KeyID string // Key ID for JWKS (defaults to "1")
RefreshTokenDays int // Refresh token TTL in days (default: 7)
SessionLifetimeDays int // Absolute session lifetime in days (default: 7)
// RSA Keys (for RS256)
RSAPrivateKeyPath string // Path to RSA private key file
RSAPublicKeyPath string // Path to RSA public key file
RSAPrivateKey string // RSA private key as string (alternative to file path)
RSAPublicKey string // RSA public key as string (alternative to file path)
// ECDSA Keys (for ES256)
ECDSAPrivateKeyPath string // Path to ECDSA private key file
ECDSAPublicKeyPath string // Path to ECDSA public key file
ECDSAPrivateKey string // ECDSA private key as string (alternative to file path)
ECDSAPublicKey string // ECDSA public key as string (alternative to file path)
}
JWTConfig holds JWT configuration
type JWTKeyManager ¶
type JWTKeyManager struct {
// contains filtered or unexported fields
}
JWTKeyManager manages JWT signing and verification keys
func NewJWTKeyManager ¶
func NewJWTKeyManager(config JWTConfig) (*JWTKeyManager, error)
NewJWTKeyManager creates a new JWT key manager
func SetupTestKeyManager ¶
func SetupTestKeyManager(t *testing.T) *JWTKeyManager
SetupTestKeyManager creates a key manager for testing
func (*JWTKeyManager) CreateToken ¶
func (m *JWTKeyManager) CreateToken(claims jwt.Claims) (string, error)
CreateToken creates a new JWT token with the configured signing method
func (*JWTKeyManager) GetPublicKey ¶
func (m *JWTKeyManager) GetPublicKey() any
GetPublicKey returns the public key for JWKS endpoint (for asymmetric methods)
func (*JWTKeyManager) GetSigningMethod ¶
func (m *JWTKeyManager) GetSigningMethod() string
GetSigningMethod returns the current signing method
func (*JWTKeyManager) VerifyToken ¶
VerifyToken verifies a JWT token using the configured verification key
type LinkedIdentityInput ¶
type LinkedIdentityInput struct {
UserInternalUUID string
Provider string
ProviderUserID string
Email string
Name string
}
LinkedIdentityInput holds the fields needed to create a new linked identity.
type LinkedIdentityStore ¶
type LinkedIdentityStore interface {
// Create inserts a new linked identity row. Returns a dberrors.ErrDuplicate-
// wrapped error if the (provider, provider_user_id) pair already exists.
Create(ctx context.Context, input LinkedIdentityInput) (models.LinkedIdentity, error)
// CreateExclusive checks for an existing binding (in linked_identities) and
// creates the row inside a single serializable transaction, eliminating the
// check-then-act race between concurrent confirm calls. The caller is
// responsible for checking the users (primary identity) table before calling
// this method — that cross-table check is performed by the handler using the
// same serializable transaction indirectly via the retry wrapper.
// Returns dberrors.ErrDuplicate if the (provider, provider_user_id) pair is
// already present in linked_identities.
CreateExclusive(ctx context.Context, input LinkedIdentityInput) (models.LinkedIdentity, error)
// GetByProviderSub looks up a linked identity by provider and provider-user-id.
// Returns ErrLinkedIdentityNotFound if no row matches.
GetByProviderSub(ctx context.Context, provider, providerUserID string) (models.LinkedIdentity, error)
// ListByUser returns all linked identities owned by userInternalUUID.
// Returns an empty slice (not an error) when none exist.
ListByUser(ctx context.Context, userInternalUUID string) ([]models.LinkedIdentity, error)
// TouchLastUsed updates last_used_at to now for the given identity id.
TouchLastUsed(ctx context.Context, id string) error
// Delete removes the linked identity identified by id, scoped to ownerUUID.
// Returns ErrLinkedIdentityNotFound if no row matches both id and ownerUUID.
Delete(ctx context.Context, id, ownerUUID string) error
}
LinkedIdentityStore is the persistence interface for the linked_identities table.
type LockoutDecision ¶
type LockoutDecision struct {
Locked bool // true when the caller should be rejected with 429
RetryAfter time.Duration // Retry-After hint to surface in HTTP headers
Count int64 // current failure count (0 if no lock)
}
LockoutDecision is the result of a Check call.
type OAuthAuthorizationServerMetadata ¶
type OAuthAuthorizationServerMetadata struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
JWKSURI string `json:"jwks_uri,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
ResponseTypesSupported []string `json:"response_types_supported"`
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
}
OAuthAuthorizationServerMetadata represents OAuth 2.0 Authorization Server Metadata
type OAuthConfig ¶
type OAuthConfig struct {
CallbackURL string
Providers map[string]OAuthProviderConfig
// ClientCallbackAllowList is the list of allowed client_callback URLs for
// /oauth2/authorize. Each entry is an exact URL or a wildcard pattern
// ending in "*". An empty list rejects any client_callback (fail-closed).
ClientCallbackAllowList []string
}
OAuthConfig holds OAuth configuration
type OAuthProtectedResourceMetadata ¶
type OAuthProtectedResourceMetadata struct {
Resource string `json:"resource"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
AuthorizationServers []string `json:"authorization_servers,omitempty"`
JWKSURI string `json:"jwks_uri,omitempty"`
BearerMethodsSupported []string `json:"bearer_methods_supported,omitempty"`
ResourceName string `json:"resource_name,omitempty"`
ResourceDocumentation string `json:"resource_documentation,omitempty"`
TLSClientCertificateBoundAccessTokens bool `json:"tls_client_certificate_bound_access_tokens"`
}
OAuthProtectedResourceMetadata represents OAuth 2.0 protected resource metadata as defined in RFC 9728
type OAuthProviderConfig ¶
type OAuthProviderConfig struct {
ID string `json:"id"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
Icon string `json:"icon"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
AuthorizationURL string `json:"authorization_url"`
TokenURL string `json:"token_url"`
UserInfo []UserInfoEndpoint `json:"userinfo"`
Issuer string `json:"issuer"`
JWKSURL string `json:"jwks_url"`
Scopes []string `json:"scopes"`
AdditionalParams map[string]string `json:"additional_params"`
AuthHeaderFormat string `json:"auth_header_format,omitempty"` // Default: "Bearer %s"
AcceptHeader string `json:"accept_header,omitempty"` // Default: "application/json"
}
OAuthProviderConfig holds configuration for an OAuth provider
type OAuthTokenLockout ¶
type OAuthTokenLockout struct {
// contains filtered or unexported fields
}
OAuthTokenLockout is a Redis-backed exponential-backoff lockout for the /oauth2/token endpoint. It counts failed grant attempts per key (typically the client_id) and emits a Retry-After hint that grows with the failure count. Closes T15 (brute-force of client_credentials) — a per-IP rate limiter does not catch an attacker rotating IPs against a single client.
The counter is stored as a plain integer at key `oauth_token_failures:{key}` with a 1h TTL. A successful grant deletes the key; a quiet period also expires it.
func NewOAuthTokenLockout ¶
func NewOAuthTokenLockout(client *redis.Client) *OAuthTokenLockout
NewOAuthTokenLockout constructs a lockout backed by the given Redis client. A nil client returns a no-op lockout.
func (*OAuthTokenLockout) Check ¶
func (l *OAuthTokenLockout) Check(ctx context.Context, key string) LockoutDecision
Check returns the current lockout state for the given subject. Returns {Locked: false} if Redis is unavailable — failing open is safer than rejecting valid clients during a Redis outage.
func (*OAuthTokenLockout) RecordFailure ¶
func (l *OAuthTokenLockout) RecordFailure(ctx context.Context, key string) (LockoutDecision, error)
RecordFailure increments the counter and (re)applies the TTL. Returns the post-increment count and the new lockout decision so the caller can surface the updated Retry-After to the client.
type OIDCDiscoveryDoc ¶
type OIDCDiscoveryDoc 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"`
SubjectTypesSupported []string `json:"subject_types_supported"`
ResponseTypesSupported []string `json:"response_types_supported"`
}
OIDCDiscoveryDoc represents the subset of an OpenID Connect Discovery 1.0 metadata document we need to classify an OAuth provider. Field names match the spec; only the fields we consume are declared.
func (*OIDCDiscoveryDoc) IsValid ¶
func (d *OIDCDiscoveryDoc) IsValid() bool
IsValid reports whether doc has the minimum fields required by the OIDC Discovery 1.0 spec. userinfo_endpoint is RECOMMENDED rather than REQUIRED; callers that need it should check separately.
type OpenIDConfiguration ¶
type OpenIDConfiguration 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"`
ResponseTypesSupported []string `json:"response_types_supported"`
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
SubjectTypesSupported []string `json:"subject_types_supported"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`
ClaimsSupported []string `json:"claims_supported,omitempty"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
}
OpenIDConfiguration represents the OpenID Connect Discovery metadata
type Provider ¶
type Provider interface {
// GetOAuth2Config returns the OAuth2 configuration
GetOAuth2Config() *oauth2.Config
// GetAuthorizationURL returns the authorization URL with the given state
GetAuthorizationURL(state string) string
// ExchangeCode exchanges an authorization code for tokens
ExchangeCode(ctx context.Context, code string) (*TokenResponse, error)
// GetUserInfo gets user information from the provider
GetUserInfo(ctx context.Context, accessToken string) (*UserInfo, error)
// ValidateIDToken validates an ID token
ValidateIDToken(ctx context.Context, idToken string) (*IDTokenClaims, error)
}
Provider is the interface for OAuth providers
func NewProvider ¶
func NewProvider(config OAuthProviderConfig, callbackURL string) (Provider, error)
NewProvider creates a new OAuth provider based on the provider ID
type ProviderClassification ¶
type ProviderClassification int
const ( // ClassificationNonOIDC is the zero value (fail-closed default): discovery // failed or no issuer configured. No guarantee about userinfo response shape. // Explicit subject_claim is required. ClassificationNonOIDC ProviderClassification = iota // ClassificationOIDCCustomUserinfo: discovery succeeds but the configured // userinfo URL differs from the discovery doc's userinfo_endpoint. The // operator is calling a non-OIDC userinfo endpoint (e.g. Microsoft Graph // /me instead of Microsoft's OIDC userinfo). Explicit subject_claim is // required. ClassificationOIDCCustomUserinfo // ClassificationOIDCCompliant: discovery succeeds AND configured userinfo // URL matches the discovery doc's userinfo_endpoint. Default `sub` mapping // is safe. ClassificationOIDCCompliant )
func (ProviderClassification) String ¶
func (c ProviderClassification) String() string
type ProviderInfo ¶
type ProviderInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Icon string `json:"icon"`
AuthURL string `json:"auth_url"`
TokenURL string `json:"token_url"`
RedirectURI string `json:"redirect_uri"`
ClientID string `json:"client_id"`
}
ProviderInfo contains information about an OAuth provider
type ProviderRegistry ¶
type ProviderRegistry interface {
GetOAuthProvider(id string) (OAuthProviderConfig, bool)
GetEnabledOAuthProviders() map[string]OAuthProviderConfig
GetSAMLProvider(id string) (SAMLProviderConfig, bool)
GetEnabledSAMLProviders() map[string]SAMLProviderConfig
InvalidateCache()
}
ProviderRegistry provides unified access to OAuth and SAML provider configurations from all sources (config, environment, database).
type ProviderSetting ¶
ProviderSetting is a minimal representation of a setting key/value pair.
type ProviderSettingsReader ¶
type ProviderSettingsReader interface {
ListByPrefix(ctx context.Context, prefix string) ([]ProviderSetting, error)
}
ProviderSettingsReader is a minimal interface defined in the auth package to avoid a circular dependency on the api package. The api.SettingsService satisfies this interface via the ProviderSettingsReaderAdapter.
type RedisConfig ¶
type RedisConfig struct {
Host string
Port string
Password string
DB int
TLSEnabled bool
TLSCAFile string
}
RedisConfig holds Redis configuration
type RuntimeConfigReader ¶
type RuntimeConfigReader interface {
// GetClientCallbackAllowList returns the configured allowlist for the
// /oauth2/authorize and /oauth2/step_up client_callback parameter.
//
// Three outcomes:
// - exists == false: no DB row. Caller falls back to the YAML
// snapshot, which preserves first-run dev workflows.
// - exists == true, err == nil: returns the parsed allowlist.
// - exists == true, err != nil: DB row is present but unusable
// (corrupt JSON, decryption failure, etc.). Caller MUST treat
// this as fail-closed (reject every client_callback) — silently
// falling back to YAML would defeat the open-redirect mitigation.
GetClientCallbackAllowList(ctx context.Context) (list []string, exists bool, err error)
// IsSAMLEnabled reports whether SAML auth is enabled.
IsSAMLEnabled(ctx context.Context) bool
// GetOAuthCallbackURL returns the configured OAuth callback URL used
// when redirecting back from an external provider.
GetOAuthCallbackURL(ctx context.Context) string
// IsEveryoneAReviewer reports whether every authenticated user should be
// auto-added to the Security Reviewers group.
//
// Before #794 this setting had no runtime reader at all: the only
// consumer read config.Auth.EveryoneIsAReviewer directly, so the
// database row existed, was displayed by the admin API, and did
// absolutely nothing. Editing it at runtime appeared to work and
// silently had no effect.
//
// Returns false on any read error (fail-closed): failing to grant a
// group membership is recoverable, granting one wrongly is not.
IsEveryoneAReviewer(ctx context.Context) bool
}
RuntimeConfigReader supplies operational config values that the auth handlers need at request time. The values are DB-backed via the SettingsService introduced in #415; this interface lets the auth package read them without importing internal/config or api (both of which would create import cycles).
Implementations should be cheap to call per-request (Get() backed by a short TTL cache in SettingsService is fine). A nil reader is interpreted by handlers as "no DB available" and they fall back to the YAML snapshot in h.config — that path exists for unit tests and for the brief window during startup before SetRuntimeConfigReader is wired.
See #419 for the motivation. Once every cfg.* operational read in the auth package has been moved here, the YAML snapshot fallback can be removed and h.config.OAuth.* / h.config.SAML.* trimmed to bootstrap-only fields.
type SAMLConfig ¶
type SAMLConfig struct {
Enabled bool `json:"enabled"`
Providers map[string]SAMLProviderConfig `json:"providers"`
}
SAMLConfig holds SAML configuration
type SAMLManager ¶
type SAMLManager struct {
// contains filtered or unexported fields
}
SAMLManager manages SAML providers
func NewSAMLManager ¶
func NewSAMLManager(service *Service) *SAMLManager
NewSAMLManager creates a new SAML manager
func (*SAMLManager) EnsureProvider ¶
func (m *SAMLManager) EnsureProvider(id string, config SAMLProviderConfig) error
EnsureProvider lazily initializes a SAML provider if not already initialized. Idempotent: if the provider is already initialized, returns immediately. Thread-safe: uses the manager's mutex to prevent concurrent initialization.
func (*SAMLManager) GetProvider ¶
func (m *SAMLManager) GetProvider(id string) (*saml.SAMLProvider, error)
GetProvider returns a SAML provider by ID
func (*SAMLManager) InitializeProviders ¶
func (m *SAMLManager) InitializeProviders(config SAMLConfig, stateStore StateStore) error
InitializeProviders initializes all configured SAML providers
func (*SAMLManager) IsProviderInitialized ¶
func (m *SAMLManager) IsProviderInitialized(id string) bool
IsProviderInitialized checks if a SAML provider was successfully initialized
func (*SAMLManager) ListProviders ¶
func (m *SAMLManager) ListProviders() []string
ListProviders returns a list of configured SAML provider IDs
type SAMLProviderConfig ¶
type SAMLProviderConfig struct {
ID string `json:"id"`
Name string `json:"name"`
Enabled bool `json:"enabled"`
Icon string `json:"icon"`
EntityID string `json:"entity_id"`
MetadataURL string `json:"metadata_url"`
MetadataXML string `json:"metadata_xml"`
ACSURL string `json:"acs_url"`
SLOURL string `json:"slo_url"`
SPPrivateKey string `json:"sp_private_key"`
SPPrivateKeyPath string `json:"sp_private_key_path"`
SPCertificate string `json:"sp_certificate"`
SPCertificatePath string `json:"sp_certificate_path"`
IDPMetadataURL string `json:"idp_metadata_url"`
IDPMetadataB64XML string `json:"idp_metadata_b64xml"`
AllowIDPInitiated bool `json:"allow_idp_initiated"`
ForceAuthn bool `json:"force_authn"`
SignRequests bool `json:"sign_requests"`
NameIDAttribute string `json:"name_id_attribute"`
EmailAttribute string `json:"email_attribute"`
NameAttribute string `json:"name_attribute"`
GivenNameAttribute string `json:"given_name_attribute"`
FamilyNameAttribute string `json:"family_name_attribute"`
GroupsAttribute string `json:"groups_attribute"`
}
SAMLProviderConfig holds configuration for a SAML provider
type SAMLProviderInfo ¶
type SAMLProviderInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Icon string `json:"icon"`
AuthURL string `json:"auth_url"`
MetadataURL string `json:"metadata_url"`
EntityID string `json:"entity_id"`
ACSURL string `json:"acs_url"`
SLOURL string `json:"slo_url,omitempty"`
Initialized bool `json:"initialized"`
}
SAMLProviderInfo contains public information about a SAML provider
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service provides authentication and authorization functionality
func NewService ¶
NewService creates a new authentication service
func (*Service) BlacklistToken ¶
BlacklistToken adds a JWT token to the blacklist so it can no longer be used. This should be called when a user is deleted or logs out to invalidate their tokens.
func (*Service) CacheUserGroups ¶
CacheUserGroups caches user groups in Redis for the session duration
func (*Service) ClearUserGroups ¶
ClearUserGroups clears cached user groups from Redis (used on logout)
func (*Service) CreateClientCredential ¶
func (s *Service) CreateClientCredential(ctx context.Context, params ClientCredentialCreateParams) (*ClientCredential, error)
CreateClientCredential creates a new client credential in the database
func (*Service) CreateUser ¶
CreateUser creates a new user
func (*Service) DeactivateClientCredential ¶
func (s *Service) DeactivateClientCredential(ctx context.Context, id uuid.UUID, ownerUUID uuid.UUID) error
DeactivateClientCredential deactivates a client credential (soft delete) and revokes its outstanding service-account tokens.
func (*Service) DeleteClientCredential ¶
func (s *Service) DeleteClientCredential(ctx context.Context, id uuid.UUID, ownerUUID uuid.UUID) error
DeleteClientCredential permanently deletes a client credential, revoking its outstanding service-account tokens first. Fail closed: if the revocation cannot be stored it returns ErrRevocationStorage with nothing deleted, so the caller can retry. Ownership is checked before revoking so a caller cannot blacklist another user's credential.
func (*Service) DeleteGroupAndData ¶
func (s *Service) DeleteGroupAndData(ctx context.Context, internalUUID string) (*GroupDeletionResult, error)
DeleteGroupAndData deletes a TMI-managed group by internal UUID and handles threat model cleanup Uses internal_uuid for precise identification to avoid issues with duplicate group_names
func (*Service) DeleteUser ¶
DeleteUser deletes a user by internal UUID
func (*Service) DeleteUserAndData ¶
DeleteUserAndData deletes a user by email and handles ownership transfer for threat models. Used by the self-deletion flow (DELETE /me) where identity comes from JWT email.
func (*Service) DeleteUserByInternalUUID ¶
func (s *Service) DeleteUserByInternalUUID(ctx context.Context, internalUUID string) (*DeletionResult, error)
DeleteUserByInternalUUID deletes a user by internal UUID and handles ownership transfer. Used by admin deletion to avoid multi-hop identity resolution that can target the wrong user.
func (*Service) GenerateDeletionChallenge ¶
func (s *Service) GenerateDeletionChallenge(ctx context.Context, userEmail string) (*DeletionChallenge, error)
GenerateDeletionChallenge creates a challenge token for user deletion
func (*Service) GenerateTokens ¶
GenerateTokens generates a new JWT token pair for a user with auth_time = now. Use this for fresh interactive logins; use GenerateTokensWithAuthTime to preserve auth_time across refresh-token rotation.
func (*Service) GenerateTokensWithAuthTime ¶
func (s *Service) GenerateTokensWithAuthTime(ctx context.Context, user User, userInfo *UserInfo, authTime time.Time) (TokenPair, error)
GenerateTokensWithAuthTime is the canonical token-mint entry point. The authTime parameter is the timestamp of the user's last interactive IdP authentication. For fresh logins, pass time.Now(). For refresh-token rotation, pass the preserved auth_time from the previous JWT.
func (*Service) GenerateTokensWithUserInfo ¶
func (s *Service) GenerateTokensWithUserInfo(ctx context.Context, user User, userInfo *UserInfo) (TokenPair, error)
GenerateTokensWithUserInfo generates a new JWT token pair for a user with optional provider UserInfo and auth_time = now.
func (*Service) GetCachedGroups ¶
GetCachedGroups retrieves cached user groups from Redis
func (*Service) GetCachedUserByEmail ¶
GetCachedUserByEmail retrieves a user from cache by email
func (*Service) GetCachedUserByID ¶
GetCachedUserByID retrieves a user from cache by internal UUID
func (*Service) GetCachedUserByProvider ¶
func (s *Service) GetCachedUserByProvider(ctx context.Context, provider, providerUserID string) (*User, error)
GetCachedUserByProvider retrieves a user from cache by provider and provider user ID
func (*Service) GetClientCredentialByClientID ¶
func (s *Service) GetClientCredentialByClientID(ctx context.Context, clientID string) (*ClientCredential, error)
GetClientCredentialByClientID retrieves a client credential by its client_id
func (*Service) GetKeyManager ¶
func (s *Service) GetKeyManager() *JWTKeyManager
GetKeyManager returns the JWT key manager (getter for unexported field)
func (*Service) GetLinkedIdentityByProviderSub ¶
func (s *Service) GetLinkedIdentityByProviderSub(ctx context.Context, provider, providerUserID string) (models.LinkedIdentity, error)
GetLinkedIdentityByProviderSub looks up a linked identity by provider and provider-user-id. Returns ErrLinkedIdentityNotFound if no row matches or the store is not wired.
func (*Service) GetPrimaryProviderID ¶
GetPrimaryProviderID gets the provider user ID for a user Note: In the new architecture, each user has exactly one provider stored directly on the users table
func (*Service) GetSAMLManager ¶
func (s *Service) GetSAMLManager() *SAMLManager
GetSAMLManager returns the SAML manager (getter for unexported field)
func (*Service) GetUserByAnyProviderID ¶
GetUserByAnyProviderID gets a user by provider ID across all providers This allows provider-independent authorization using IdP user IDs NOTE: This can return ambiguous results if the same provider_user_id exists for multiple providers
func (*Service) GetUserByEmail ¶
GetUserByEmail gets a user by email
func (*Service) GetUserByID ¶
GetUserByID gets a user by internal UUID
func (*Service) GetUserByInternalUUID ¶
GetUserByInternalUUID gets a user by their internal UUID.
func (*Service) GetUserByProviderAndEmail ¶
func (s *Service) GetUserByProviderAndEmail(ctx context.Context, provider, email string) (User, error)
GetUserByProviderAndEmail gets a user by provider and email address This is used as a fallback when provider_user_id doesn't match but same provider + email does
func (*Service) GetUserByProviderID ¶
func (s *Service) GetUserByProviderID(ctx context.Context, provider, providerUserID string) (User, error)
GetUserByProviderID gets a user by provider and provider user ID
func (*Service) GetUserProviders ¶
GetUserProviders gets the OAuth provider for a user Note: In the new architecture, each user has exactly one provider
func (*Service) GormDB ¶
GormDB returns the underlying GORM database connection. Used by services that need to wrap operations in retryable transactions.
func (*Service) HandleClientCredentialsGrant ¶
func (s *Service) HandleClientCredentialsGrant(ctx context.Context, clientID, clientSecret string) (*TokenPair, error)
HandleClientCredentialsGrant processes OAuth 2.0 Client Credentials Grant (RFC 6749 Section 4.4) Returns an access token for machine-to-machine authentication
func (*Service) InvalidateUserCache ¶
InvalidateUserCache removes a user from all cache keys
func (*Service) InvalidateUserSessions ¶
InvalidateUserSessions invalidates all sessions for a user
func (*Service) IssueAddonDelegationToken ¶
func (s *Service) IssueAddonDelegationToken( ctx context.Context, invoker *User, addonID, deliveryID, threatModelID uuid.UUID, ) (string, error)
IssueAddonDelegationToken mints a short-lived JWT that impersonates the invoker for one addon-invocation write-back. The token's claims:
- `sub` is the invoker's provider_user_id (matches the invoker's normal login token), so existing JWT middleware and downstream ACL checks resolve to the invoker without modification.
- `email`, `name`, `idp`, `groups`, `tmi_is_security_reviewer` are copied from the invoker. `tmi_is_administrator` is FORCED to false regardless of the invoker's actual administrator membership — a delegation token never grants admin authority, so the addon cannot escape its mandate even if invoked by an admin.
- `delegation` carries the addon/delivery/threat-model scope.
- `aud` is the issuer (self-issued, like normal user tokens) so the existing JWT validator accepts it.
- `exp` is now+DelegationTokenTTL.
Callers (the webhook delivery worker) should call this once per delivery attempt — the previous attempt's token will have expired by the time a retry fires, and minting fresh tokens keeps the invoker's revocation / group-membership state current.
func (*Service) ListClientCredentialsByOwner ¶
func (s *Service) ListClientCredentialsByOwner(ctx context.Context, ownerUUID uuid.UUID) ([]*ClientCredential, error)
ListClientCredentialsByOwner retrieves all client credentials for a given owner
func (*Service) RefreshToken ¶
RefreshToken refreshes an access token using a refresh token. Implements single-use rotation (old token deleted) and absolute session expiration.
func (*Service) RevokeToken ¶
RevokeToken revokes a refresh token
func (*Service) SetClaimsEnricher ¶
func (s *Service) SetClaimsEnricher(enricher ClaimsEnricher)
SetClaimsEnricher sets the claims enricher for JWT token generation
func (*Service) SetLinkedIdentityStore ¶
func (s *Service) SetLinkedIdentityStore(store LinkedIdentityStore)
SetLinkedIdentityStore wires a LinkedIdentityStore into the service, enabling Tier 1b linked-identity resolution during OAuth login.
func (*Service) SetPreUserDeleteHook ¶
func (s *Service) SetPreUserDeleteHook(h UserContentTokenRevoker)
SetPreUserDeleteHook registers a hook that is called before each user deletion to perform best-effort content-token revocations at the provider side. The hook is called with the user's internal UUID before the DB row (and its FK-cascaded child rows) is removed, giving the implementation access to the token data. Pass nil to clear the hook.
func (*Service) SetProviderRegistry ¶
func (s *Service) SetProviderRegistry(registry ProviderRegistry)
SetProviderRegistry sets the provider registry for unified provider lookup.
func (*Service) TouchLinkedIdentityLastUsed ¶
TouchLinkedIdentityLastUsed updates last_used_at for the linked identity with the given id.
func (*Service) TransferOwnership ¶
func (s *Service) TransferOwnership(ctx context.Context, sourceUserUUID, targetUserUUID string) (*TransferResult, error)
TransferOwnership transfers all owned threat models and survey responses from one user to another. The source user retains "writer" access.
func (*Service) UpdateClientCredentialLastUsed ¶
UpdateClientCredentialLastUsed updates the last_used_at timestamp for a client credential
func (*Service) UpdateUser ¶
UpdateUser updates an existing user
type StateStore ¶
type StateStore interface {
// StoreState stores state with associated data and expiration
StoreState(ctx context.Context, state, data string, ttl time.Duration) error
// ValidateState checks if state is valid and returns associated data
ValidateState(ctx context.Context, state string) (string, error)
// GetCallbackURL retrieves the callback URL associated with a state
GetCallbackURL(ctx context.Context, state string) (string, error)
// StoreCallbackURL stores a callback URL with a state
StoreCallbackURL(ctx context.Context, state, callbackURL string, ttl time.Duration) error
// DeleteState removes state from store
DeleteState(ctx context.Context, state string) error
// StorePKCEChallenge stores PKCE code challenge with associated method
StorePKCEChallenge(ctx context.Context, state, codeChallenge, challengeMethod string, ttl time.Duration) error
// GetPKCEChallenge retrieves PKCE code challenge and method for a state
GetPKCEChallenge(ctx context.Context, state string) (challenge, method string, err error)
// DeletePKCEChallenge removes PKCE challenge from store
DeletePKCEChallenge(ctx context.Context, state string) error
}
StateStore is an interface for storing and retrieving state information
type StepUpActor ¶
StepUpActor identifies the user whose step-up event is being recorded. All four fields are denormalized into the audit row (matches the SystemAuditEntry pattern; rows survive user deletion).
type StepUpAuditor ¶
type StepUpAuditor struct {
// contains filtered or unexported fields
}
StepUpAuditor wraps a SystemAuditWriter with the field shapes specific to step-up events. Fail-open: write failures are logged but do not propagate.
func NewStepUpAuditor ¶
func NewStepUpAuditor(writer SystemAuditWriter) *StepUpAuditor
NewStepUpAuditor returns an auditor. writer may be nil (in which case audit calls are no-ops with a debug log; matches the existing fail-open posture).
func (*StepUpAuditor) LogComplete ¶
func (a *StepUpAuditor) LogComplete(ctx context.Context, actor StepUpActor, strength StepUpStrength, providerID, mode string) error
LogComplete records a successful step-up. Strength carries strong|weak; mode carries round_trip|short_circuit.
func (*StepUpAuditor) LogFailed ¶
func (a *StepUpAuditor) LogFailed(ctx context.Context, actor StepUpActor, reason string, extras map[string]string) error
LogFailed records a step-up that did not complete successfully. reason is the short stable code (identity_mismatch, access_denied, state_expired, etc.). extras are inlined into the payload; values are redacted via redactStepUpAttemptedEmail when the key is "attempted_email".
func (*StepUpAuditor) LogRejected ¶
func (a *StepUpAuditor) LogRejected(ctx context.Context, actor StepUpActor, reason string, extras map[string]string) error
LogRejected records a step-up attempt that was rejected before the upstream round-trip began (e.g., CC-grant caller, invalid provider).
type StepUpStrength ¶
type StepUpStrength int
StepUpStrength classifies whether a given provider can guarantee a fresh interactive re-authentication on demand. Strong providers honor OIDC's prompt=login + max_age=0 (or SAML's ForceAuthn=true). Weak providers do not, and step-up against them is short-circuited with an audit marker.
See docs/superpowers/specs/2026-05-10-oauth2-step-up-design.md.
const ( StepUpStrong StepUpStrength = iota StepUpWeak )
func ClassifyStepUpStrength ¶
func ClassifyStepUpStrength(cfg OAuthProviderConfig) StepUpStrength
ClassifyStepUpStrength returns the step-up strength for the given provider config. Rules (first match wins):
- ID in knownStrongProviderIDs → Strong
- ID in knownWeakProviderIDs → Weak
- Has Issuer AND JWKSURL (i.e., OIDC) → Strong (generic OIDC providers honor prompt=login per the OIDC spec)
- Otherwise → Weak (pure-OAuth2 fallback; safest default)
SAML providers are classified Strong by callers via a separate path; this function operates on OAuth provider configs only.
func (StepUpStrength) String ¶
func (s StepUpStrength) String() string
type SystemAuditRecord ¶
type SystemAuditRecord struct {
ActorEmail string
ActorProvider string
ActorProviderID string
ActorDisplayName string
HTTPMethod string
HTTPPath string
FieldPath string
OldValueRedacted *string
NewValueRedacted *string
ChangeSummary *string
CreatedAt time.Time
}
SystemAuditRecord is a transport struct mapping 1:1 to api/models.SystemAuditEntry. Defined here so package auth does not import package api (which would create a cycle).
type SystemAuditWriter ¶
type SystemAuditWriter interface {
WriteSystemAudit(ctx context.Context, entry SystemAuditRecord) error
}
SystemAuditWriter is the minimal write surface required by step-up audit helpers. The concrete implementation in package api wraps GORM/Postgres; tests inject a memory implementation.
type TestHelper ¶
type TestHelper struct {
DB *sql.DB
Mock sqlmock.Sqlmock
Redis *redis.Client
MiniRedis *miniredis.Miniredis
KeyManager *JWTKeyManager
StateStore StateStore
TestContext context.Context
}
TestHelper provides utilities for testing auth package functionality
func NewTestHelper ¶
func NewTestHelper(t *testing.T) *TestHelper
NewTestHelper creates a new test helper with mocked dependencies
func (*TestHelper) Cleanup ¶
func (h *TestHelper) Cleanup()
Cleanup releases all resources held by the test helper
func (*TestHelper) FastForwardRedis ¶
func (h *TestHelper) FastForwardRedis(duration time.Duration)
FastForwardRedis advances time in miniredis for TTL testing
func (*TestHelper) FlushRedis ¶
func (h *TestHelper) FlushRedis()
FlushRedis clears all keys in miniredis
func (*TestHelper) GetRedisKey ¶
func (h *TestHelper) GetRedisKey(key string) (string, error)
GetRedisKey gets a key from miniredis for testing
func (*TestHelper) SetRedisKey ¶
func (h *TestHelper) SetRedisKey(key, value string, expiration time.Duration) error
SetRedisKey sets a key in miniredis for testing
type TestProvider ¶
type TestProvider struct {
*BaseProvider
// contains filtered or unexported fields
}
TestProvider implements the TMI internal OAuth provider In dev/test builds (TMI_BUILD_MODE=dev|test): supports Authorization Code flow with ephemeral user creation In production builds: Only supports Client Credentials Grant for machine-to-machine authentication
func NewTestProvider ¶
func NewTestProvider(config OAuthProviderConfig, callbackURL string) *TestProvider
NewTestProvider creates a new test OAuth provider
func (*TestProvider) ExchangeCode ¶
func (p *TestProvider) ExchangeCode(ctx context.Context, code string) (*TokenResponse, error)
ExchangeCode validates the authorization code and returns tokens only for valid codes
func (*TestProvider) GetAuthorizationURL ¶
func (p *TestProvider) GetAuthorizationURL(state string) string
GetAuthorizationURL returns the test authorization URL For the test provider, we'll create a direct callback URL instead of an external redirect
func (*TestProvider) GetAuthorizationURLWithHint ¶
func (p *TestProvider) GetAuthorizationURLWithHint(state, hint string) string
GetAuthorizationURLWithHint is GetAuthorizationURL with a login_hint encoded into the fake authorization code, the same shape the authorize handler produces, so a flow that goes through this URL (step-up, #817) resolves to the hinted identity instead of a random test user. An invalid hint is dropped, matching validateUserHint.
func (*TestProvider) GetUserInfo ¶
GetUserInfo returns fake user information
func (*TestProvider) ValidateIDToken ¶
func (p *TestProvider) ValidateIDToken(ctx context.Context, idToken string) (*IDTokenClaims, error)
ValidateIDToken validates the test ID token (always succeeds)
type TokenBlacklist ¶
type TokenBlacklist struct {
// contains filtered or unexported fields
}
TokenBlacklist manages blacklisted JWT tokens using Redis
func NewTokenBlacklist ¶
func NewTokenBlacklist(redisClient *redis.Client, keyManager *JWTKeyManager) *TokenBlacklist
NewTokenBlacklist creates a new token blacklist service
func (*TokenBlacklist) BlacklistToken ¶
func (tb *TokenBlacklist) BlacklistToken(ctx context.Context, tokenString string) error
BlacklistToken adds a JWT token to the blacklist
func (*TokenBlacklist) CleanupExpiredTokens ¶
func (tb *TokenBlacklist) CleanupExpiredTokens(ctx context.Context) error
CleanupExpiredTokens removes expired entries from the blacklist This is handled automatically by Redis TTL, but this method can be used for monitoring or manual cleanup if needed
func (*TokenBlacklist) IsCredentialRevoked ¶
func (tb *TokenBlacklist) IsCredentialRevoked(ctx context.Context, credentialID string) (bool, error)
IsCredentialRevoked reports whether service-account tokens minted from a client credential have been revoked via RevokeCredential.
func (*TokenBlacklist) IsTokenBlacklisted ¶
IsTokenBlacklisted checks if a JWT token is blacklisted
func (*TokenBlacklist) IsTokenHashBlacklisted ¶
func (tb *TokenBlacklist) IsTokenHashBlacklisted(ctx context.Context, tokenHash string) (bool, error)
IsTokenHashBlacklisted checks the blacklist by a hash from HashToken, for callers that retain only the hash (WebSocket tickets, #869).
func (*TokenBlacklist) RevokeCredential ¶
func (tb *TokenBlacklist) RevokeCredential(ctx context.Context, credentialID string, ttl time.Duration) error
RevokeCredential marks every service-account token minted from a client credential as revoked (#862). The server does not retain issued SA tokens, so they cannot be blacklisted individually; instead the credential ID is marked for ttl, which must be at least the access-token lifetime so that the last token minted before the revoke has expired by the time the marker does.
type TokenIntrospectionResponse ¶
type TokenIntrospectionResponse struct {
Active bool `json:"active"`
Sub string `json:"sub,omitempty"`
Email string `json:"email,omitempty"`
Name string `json:"name,omitempty"`
Iat int64 `json:"iat,omitempty"`
Exp int64 `json:"exp,omitempty"`
Aud string `json:"aud,omitempty"`
Iss string `json:"iss,omitempty"`
TokenType string `json:"token_type,omitempty"`
Scope string `json:"scope,omitempty"`
}
TokenIntrospectionResponse represents the response from token introspection
type TokenPair ¶
type TokenPair struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
}
TokenPair contains an access token and a refresh token
type TokenResponse ¶
type TokenResponse struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
RefreshToken string `json:"refresh_token,omitempty"`
ExpiresIn int `json:"expires_in"`
IDToken string `json:"id_token,omitempty"`
}
TokenResponse contains the response from the token endpoint
type TokenTestCase ¶
type TokenTestCase struct {
Name string
User User
ExpectedError bool
ExpectedClaims func(*testing.T, *Claims)
}
TokenTestCase represents a test case for token operations
type TransferResult ¶
type TransferResult struct {
ThreatModelIDs []string `json:"threat_model_ids"`
SurveyResponseIDs []string `json:"survey_response_ids"`
}
TransferResult contains statistics about the ownership transfer operation
type User ¶
type User struct {
InternalUUID string `json:"internal_uuid"` // Internal system UUID (cached but excluded from API responses via convertUserToAPIResponse)
Provider string `json:"provider"` // OAuth provider: "tmi", "google", "github", "microsoft", "azure"
ProviderUserID string `json:"provider_user_id"` // Provider's user ID (from JWT sub claim)
Email string `json:"email"`
Name string `json:"name"` // Display name for UI presentation
EmailVerified bool `json:"email_verified"`
AccessToken *string `json:"-"` // OAuth access token (not exposed in JSON) - nullable
RefreshToken *string `json:"-"` // OAuth refresh token (not exposed in JSON) - nullable
TokenExpiry *time.Time `json:"-"` // Token expiration time (not exposed in JSON) - nullable
Groups []string `json:"groups,omitempty"` // Groups from identity provider (not stored in DB)
IsAdmin bool `json:"is_admin"` // Whether user has administrator privileges
IsSecurityReviewer bool `json:"is_security_reviewer"` // Whether user is a security reviewer
Automation *bool `json:"automation,omitempty"` // Whether this is an automation/service account (server-managed, nullable)
CreatedAt time.Time `json:"created_at"`
ModifiedAt time.Time `json:"modified_at"`
LastLogin *time.Time `json:"last_login,omitempty"` // nullable - may be NULL for auto-created admin users
}
User represents a user in the system
func CreateTestUser ¶
CreateTestUser creates a test user with default values
func CreateTestUserWithGroups ¶
CreateTestUserWithGroups creates a test user with specific groups
func CreateTestUserWithRole ¶
CreateTestUserWithRole creates a test user with specific admin status
type UserContentTokenRevoker ¶
type UserContentTokenRevoker interface {
// RevokeUserTokens attempts to revoke all content tokens belonging to
// userID at their respective providers. It never returns an error.
RevokeUserTokens(ctx context.Context, userID string)
}
UserContentTokenRevoker is called before a user is deleted to sweep any per-user OAuth tokens at the provider side. Implementations must be best-effort: revocation failures must be logged but must never block user deletion. The interface lives in the auth package to avoid a circular import with the api package (which provides the concrete implementation).
type UserGroupInfo ¶
type UserGroupInfo struct {
InternalUUID string `json:"internal_uuid"`
GroupName string `json:"group_name"`
Name string `json:"name,omitempty"`
}
UserGroupInfo represents a TMI-managed group that a user belongs to
type UserGroupsFetcher ¶
type UserGroupsFetcher interface {
GetUserGroups(ctx context.Context, userInternalUUID string) ([]UserGroupInfo, error)
}
UserGroupsFetcher retrieves TMI-managed group memberships for a user
type UserInfo ¶
type UserInfo struct {
ID string `json:"id,omitempty"`
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
Name string `json:"name,omitempty"`
GivenName string `json:"given_name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
Picture string `json:"picture,omitempty"`
Locale string `json:"locale,omitempty"`
IdP string `json:"idp,omitempty"` // Identity provider ID
Groups []string `json:"groups,omitempty"` // Groups from identity provider
}
UserInfo contains user information from the provider
func CreateTestUserInfo ¶
CreateTestUserInfo creates UserInfo for testing OAuth responses
type UserInfoEndpoint ¶
UserInfoEndpoint represents a single userinfo endpoint and its claim mappings
type UserProvider ¶
type UserProvider struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Provider string `json:"provider"`
ProviderUserID string `json:"provider_user_id"`
Email string `json:"email"`
IsPrimary bool `json:"is_primary"`
CreatedAt time.Time `json:"created_at"`
LastLogin time.Time `json:"last_login"`
}
UserProvider represents a user's OAuth provider
Source Files
¶
- audit_identity_link.go
- audit_step_up.go
- claim_extractor.go
- client_callback_allowlist.go
- client_credentials.go
- config.go
- config_adapter.go
- cookies.go
- default_provider_prod.go
- delegation_token.go
- group_deletion.go
- handlers.go
- handlers_helpers.go
- handlers_identity_link.go
- handlers_oauth.go
- handlers_oauth_user.go
- handlers_openid.go
- handlers_providers.go
- handlers_revocation.go
- handlers_saml.go
- handlers_step_up.go
- handlers_token.go
- handlers_token_helpers.go
- handlers_user.go
- integration_example.go
- jwt_key_manager.go
- linked_identity_store.go
- main.go
- oauth_token_lockout.go
- oidc_discovery.go
- ownership_transfer.go
- pkce.go
- provider.go
- provider_classifier.go
- provider_prod.go
- provider_registry.go
- provider_step_up.go
- runtime_config_reader.go
- saml_manager.go
- service.go
- service_test_helpers.go
- state_store.go
- test_provider.go
- token_blacklist.go
- url_canonicalize.go
- user_deletion.go
- utils.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package repository provides database repository interfaces and implementations for the auth service.
|
Package repository provides database repository interfaces and implementations for the auth service. |