oidcprovider

package
v0.0.15 Latest Latest
Warning

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

Go to latest
Published: Feb 11, 2026 License: Apache-2.0 Imports: 45 Imported by: 0

README

OIDC Provider Plugin - Enterprise Edition

Comprehensive enterprise-grade OpenID Connect Provider plugin for AuthSome with multi-tenancy support, org-specific configurations, and RFC-compliant OAuth2/OIDC implementation.

Features

Core OIDC/OAuth2
  • ✅ OAuth2 Authorization Code Flow with PKCE
  • ✅ OpenID Connect ID Tokens (JWT)
  • ✅ Refresh Tokens
  • ✅ Automatic Key Rotation
  • ✅ JWKS Endpoint
  • ✅ UserInfo Endpoint
  • ✅ Discovery Endpoint (.well-known/openid-configuration)
  • RFC 8628: Device Authorization Grant (Device Flow)
Enterprise Features (RFC-Compliant)
  • RFC 7591: Dynamic Client Registration
  • RFC 7662: Token Introspection
  • RFC 7009: Token Revocation (with cascade support)
  • ✅ Client Authentication (client_secret_basic, client_secret_post, none/PKCE)
  • ✅ Persistent Consent Management
  • ✅ Org-specific OAuth Clients with hierarchy fallback
  • ✅ Session-linked Token Lifecycle
  • ✅ Admin Client Management Endpoints
Multi-Tenancy Architecture
  • App-Level Clients: Default OAuth clients available to all organizations
  • Org-Level Clients: Organization-specific OAuth clients with custom configurations
  • Hierarchy Resolution: Org-specific clients override app-level clients
  • Context-Aware: Full integration with App/Environment/Organization contexts

Architecture

Schema Updates
OAuthClient
type OAuthClient struct {
    // Context
    AppID          xid.ID
    EnvironmentID  xid.ID
    OrganizationID *xid.ID  // null = app-level, set = org-specific
    
    // OAuth2/OIDC Config
    RedirectURIs            []string
    PostLogoutRedirectURIs  []string
    GrantTypes              []string
    ResponseTypes           []string
    AllowedScopes           []string
    
    // Security
    TokenEndpointAuthMethod string  // client_secret_basic, client_secret_post, none
    ApplicationType         string  // web, native, spa
    RequirePKCE             bool
    RequireConsent          bool
    TrustedClient           bool
    
    // RFC 7591 Metadata
    LogoURI    string
    PolicyURI  string
    TosURI     string
    Contacts   []string
    Metadata   map[string]interface{}
}
AuthorizationCode
type AuthorizationCode struct {
    AppID          xid.ID
    EnvironmentID  xid.ID
    OrganizationID *xid.ID
    SessionID      *xid.ID     // Links to user session
    
    ConsentGranted bool
    ConsentScopes  string
    AuthTime       time.Time   // For max_age checks
}
OAuthToken
type OAuthToken struct {
    AppID          xid.ID
    EnvironmentID  xid.ID
    OrganizationID *xid.ID
    SessionID      *xid.ID     // For session-based revocation
    
    IDToken    string
    TokenClass string          // access_token, refresh_token, id_token
    
    // JWT Claims
    JTI       string           // For revocation by ID
    Issuer    string
    Audience  []string
    NotBefore *time.Time
    
    // Authentication Context
    AuthTime *time.Time
    ACR      string
    AMR      []string
}
OAuthConsent (New)
type OAuthConsent struct {
    AppID          xid.ID
    EnvironmentID  xid.ID
    OrganizationID *xid.ID
    UserID         xid.ID
    ClientID       string
    Scopes         []string
    ExpiresAt      *time.Time  // Optional consent expiration
}

API Endpoints

Public Endpoints
GET /.well-known/openid-configuration

Returns OIDC discovery document with all supported endpoints and capabilities.

GET /oauth2/jwks

Returns JSON Web Key Set for token verification.

GET /oauth2/authorize

OAuth2/OIDC authorization endpoint. Initiates authorization flow.

Query Parameters:

  • client_id (required)
  • redirect_uri (required)
  • response_type (required): code
  • scope: Space-separated scopes (e.g., openid profile email)
  • state: Opaque value for CSRF protection
  • nonce: For ID token replay protection
  • code_challenge: PKCE challenge
  • code_challenge_method: S256 or plain
POST /oauth2/token

Exchanges authorization code for tokens.

Request:

{
  "grant_type": "authorization_code",
  "code": "...",
  "redirect_uri": "...",
  "client_id": "...",
  "client_secret": "...",  // Not required for public clients
  "code_verifier": "..."   // Required for PKCE
}

Response:

{
  "access_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "...",
  "id_token": "...",        // Only if openid scope requested
  "scope": "openid profile email"
}
GET /oauth2/userinfo

Returns user information. Requires Bearer token.

Response:

{
  "sub": "user_id",
  "email": "user@example.com",
  "email_verified": true,
  "name": "John Doe",
  "preferred_username": "johndoe",
  "picture": "https://..."
}
Enterprise Endpoints
POST /oauth2/introspect (RFC 7662)

Introspects access or refresh token. Requires client authentication.

Request:

{
  "token": "...",
  "token_type_hint": "access_token"  // Optional: access_token or refresh_token
}

Response:

{
  "active": true,
  "scope": "openid profile",
  "client_id": "client_123",
  "username": "johndoe",
  "token_type": "Bearer",
  "exp": 1609459200,
  "iat": 1609455600,
  "sub": "user_id",
  "aud": ["https://api.example.com"],
  "iss": "https://auth.example.com",
  "jti": "token_id"
}
POST /oauth2/revoke (RFC 7009)

Revokes access or refresh token. Requires client authentication.

Request:

{
  "token": "...",
  "token_type_hint": "access_token"  // Optional
}

Response:

{
  "status": "revoked"
}
Admin Endpoints
POST /oauth2/register (RFC 7591)

Registers a new OAuth client (admin only).

Request:

{
  "client_name": "My Application",
  "redirect_uris": ["https://app.example.com/callback"],
  "post_logout_redirect_uris": ["https://app.example.com"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "application_type": "web",  // web, native, spa
  "token_endpoint_auth_method": "client_secret_basic",
  "logo_uri": "https://app.example.com/logo.png",
  "policy_uri": "https://app.example.com/privacy",
  "tos_uri": "https://app.example.com/terms",
  "contacts": ["admin@example.com"],
  "scope": "openid profile email",
  "require_pkce": true,
  "require_consent": true,
  "trusted_client": false
}

Response:

{
  "client_id": "client_01HZ...",
  "client_secret": "secret_...",  // Omitted for public clients
  "client_id_issued_at": 1609459200,
  "client_secret_expires_at": 0,  // 0 = never expires
  "client_name": "My Application",
  "redirect_uris": ["https://app.example.com/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "application_type": "web",
  "token_endpoint_auth_method": "client_secret_basic",
  ...
}
GET /oauth2/clients

Lists all OAuth clients (admin only).

Query Parameters:

  • page: Page number (default: 1)
  • page_size: Results per page (default: 20, max: 100)
GET /oauth2/clients/:clientId

Gets OAuth client details (admin only).

PUT /oauth2/clients/:clientId

Updates OAuth client (admin only).

DELETE /oauth2/clients/:clientId

Deletes OAuth client and revokes all tokens (admin only).

Configuration

auth:
  oidcprovider:
    issuer: "https://auth.example.com"
    
    keys:
      privateKeyPath: "/path/to/private_key.pem"
      publicKeyPath: "/path/to/public_key.pem"
      rotationInterval: "24h"
      keyLifetime: "168h"  # 7 days
    
    tokens:
      accessTokenExpiry: "1h"
      idTokenExpiry: "1h"
      refreshTokenExpiry: "720h"  # 30 days
    
    deviceFlow:
      enabled: true              # Enable device flow (RFC 8628)
      codeExpiry: "10m"         # Device code lifetime
      userCodeLength: 8         # User code characters
      userCodeFormat: "XXXX-XXXX" # User code format
      pollingInterval: 5        # Minimum seconds between polls
      verificationUri: "/device" # Verification page path
      maxPollAttempts: 120      # Maximum poll attempts
      cleanupInterval: "5m"     # Cleanup job interval

Usage

Initialize Plugin
import "github.com/xraph/authsome/plugins/oidcprovider"

plugin := oidcprovider.NewPlugin(
    oidcprovider.WithIssuer("https://auth.example.com"),
)

authsome.RegisterPlugin(plugin)
Register App-Level Client
// Via API (admin authenticated)
POST /oauth2/register
{
  "client_name": "Platform App",
  "redirect_uris": ["https://app.example.com/callback"],
  "application_type": "web"
}
Register Org-Specific Client
// Set organization context, then register
// Client will be scoped to the organization
POST /oauth2/register
{
  "client_name": "Org Custom App",
  "redirect_uris": ["https://org.example.com/callback"]
}
Authorization Flow
  1. Redirect to Authorization Endpoint:
GET /oauth2/authorize?
  client_id=client_123&
  redirect_uri=https://app.example.com/callback&
  response_type=code&
  scope=openid profile email&
  state=random_state&
  code_challenge=challenge&
  code_challenge_method=S256
  1. User Authenticates & Consents

    • If not logged in, redirected to login
    • If consent required, shown consent screen
    • Consent stored for future requests
  2. Redirect Back with Code:

https://app.example.com/callback?code=auth_code&state=random_state
  1. Exchange Code for Tokens:
POST /oauth2/token
{
  "grant_type": "authorization_code",
  "code": "auth_code",
  "redirect_uri": "https://app.example.com/callback",
  "client_id": "client_123",
  "client_secret": "secret",
  "code_verifier": "verifier"
}
  1. Use Access Token:
GET /oauth2/userinfo
Authorization: Bearer access_token

Security Considerations

PKCE (Proof Key for Code Exchange)
  • Mandatory for native and SPA applications
  • Recommended for all authorization code flows
  • Prevents authorization code interception attacks
  • Use S256 method (SHA256) over plain
Client Authentication
  • Confidential Clients (web apps): Use client_secret_basic or client_secret_post
  • Public Clients (native/SPA): Use none with mandatory PKCE
  • Client secrets never expire (rotate manually if compromised)
Token Security
  • Access tokens expire after 1 hour
  • Refresh tokens expire after 30 days
  • Tokens linked to sessions (revoked when session ends)
  • Support revocation by token, session, user, or client
  • JWT IDs (jti) enable revocation by ID
  • Persistent consent storage
  • Optional consent expiration
  • Trusted clients skip consent
  • Consent can be revoked by user

Migration Guide

From Basic OIDC to Enterprise
  1. Update Schema:
# Run migrations to add new fields and tables
authsome migrate
  1. Update Existing Clients:

    • Set application_type based on client type
    • Configure token_endpoint_auth_method
    • Enable require_pkce for public clients
    • Set require_consent based on trust level
  2. Update Client Applications:

    • Implement PKCE for public clients
    • Handle consent screens
    • Use introspection for resource servers
    • Implement token revocation on logout

Advanced Features

Session-Based Token Lifecycle

Tokens are linked to user sessions. When a session is terminated:

  • All associated tokens are automatically revoked
  • Prevents orphaned tokens after logout
Org-Specific Configurations

Organizations can override app-level OAuth clients:

App Level: Default Google OAuth (app client_id)
  ↓
Org Level: Custom Google OAuth (org client_id)

Resolution order:

  1. Try org-specific client first
  2. Fall back to app-level client
  3. Return not found if neither exists
Token Revocation Strategies
  • By Token: Revoke specific access/refresh token
  • By JWT ID: Revoke by jti claim
  • By Session: Cascade revoke all session tokens
  • By User: Revoke all user tokens in org
  • By Client: Revoke all client tokens

Testing

# Run OIDC provider tests
go test -v ./plugins/oidcprovider/...

# Test with coverage
go test -v -cover ./plugins/oidcprovider/...

# Integration tests
go test -v -tags=integration ./plugins/oidcprovider/...

Troubleshooting

Common Issues

"Invalid redirect_uri"

  • Ensure redirect URI is exactly registered (including trailing slashes)
  • Check for HTTPS requirement (except localhost)

"PKCE required for this client"

  • Client configured with require_pkce: true
  • Must provide code_challenge and code_challenge_method in authorize request
  • Must provide code_verifier in token request

"Invalid client credentials"

  • Check client authentication method matches client configuration
  • For Basic auth: Properly encode credentials in Authorization header
  • For POST: Include client_id and client_secret in request body

"Token expired or revoked"

  • Access tokens expire after 1 hour
  • Use refresh token to obtain new access token
  • Check if session was terminated

License

Copyright (c) 2025 AuthSome. All rights reserved.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetTokenScopes

func GetTokenScopes(scope string) []string

GetTokenScopes parses and returns the scopes from a token.

func HasScope

func HasScope(tokenScope, requiredScope string) bool

HasScope checks if a token has a specific scope.

Types

type AccessTokenClaims

type AccessTokenClaims struct {
	jwt.RegisteredClaims

	Scope     string `json:"scope,omitempty"`
	ClientID  string `json:"client_id"`
	TokenType string `json:"token_type"`
	AppID     string `json:"app_id,omitempty"` // Application ID for AuthSome JWT validation
}

AccessTokenClaims represents the claims for an access token.

type AdminHandler

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

AdminHandler handles admin-only OAuth client management endpoints.

func NewAdminHandler

func NewAdminHandler(clientRepo *repo.OAuthClientRepository, registrationSvc *RegistrationService) *AdminHandler

NewAdminHandler creates a new admin handler.

func (*AdminHandler) DeleteClient

func (h *AdminHandler) DeleteClient(c forge.Context) error

DeleteClient deletes an OAuth client.

func (*AdminHandler) GetClient

func (h *AdminHandler) GetClient(c forge.Context) error

GetClient retrieves detailed information about an OAuth client.

func (*AdminHandler) ListClients

func (h *AdminHandler) ListClients(c forge.Context) error

ListClients lists all OAuth clients for the current app/env/org.

func (*AdminHandler) RegisterClient

func (h *AdminHandler) RegisterClient(c forge.Context) error

RegisterClient handles dynamic client registration (admin only).

func (*AdminHandler) UpdateClient

func (h *AdminHandler) UpdateClient(c forge.Context) error

UpdateClient updates an existing OAuth client.

type AuthorizeRequest

type AuthorizeRequest struct {
	ClientID            string `form:"client_id"             json:"client_id"             validate:"required"`
	RedirectURI         string `form:"redirect_uri"          json:"redirect_uri"          validate:"required,url"`
	ResponseType        string `form:"response_type"         json:"response_type"         validate:"required"`
	Scope               string `form:"scope"                 json:"scope"`
	State               string `form:"state"                 json:"state"`
	Nonce               string `form:"nonce"                 json:"nonce"`
	CodeChallenge       string `form:"code_challenge"        json:"code_challenge"`
	CodeChallengeMethod string `form:"code_challenge_method" json:"code_challenge_method"`
	Prompt              string `form:"prompt"                json:"prompt"` // none, login, consent, select_account
	MaxAge              *int   `form:"max_age"               json:"max_age"`
	UILocales           string `form:"ui_locales"            json:"ui_locales"`
	IDTokenHint         string `form:"id_token_hint"         json:"id_token_hint"`
	LoginHint           string `form:"login_hint"            json:"login_hint"`
	ACRValues           string `form:"acr_values"            json:"acr_values"`
}

AuthorizeRequest represents an OAuth2/OIDC authorization request.

type ClientAuthResult

type ClientAuthResult struct {
	ClientID      string
	Authenticated bool
	Method        string // basic, post, none
}

ClientAuthResult represents the result of client authentication.

type ClientAuthenticator

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

ClientAuthenticator handles OAuth2/OIDC client authentication.

func NewClientAuthenticator

func NewClientAuthenticator(clientRepo *repo.OAuthClientRepository) *ClientAuthenticator

NewClientAuthenticator creates a new client authenticator.

func (*ClientAuthenticator) AuthenticateClient

AuthenticateClient authenticates an OAuth2 client using various methods Supports: client_secret_basic, client_secret_post, and none (for public clients with PKCE).

func (*ClientAuthenticator) IsConfidentialClient

func (c *ClientAuthenticator) IsConfidentialClient(client *schema.OAuthClient) bool

IsConfidentialClient checks if a client is a confidential client (has client secret).

func (*ClientAuthenticator) IsPublicClient

func (c *ClientAuthenticator) IsPublicClient(client *schema.OAuthClient) bool

IsPublicClient checks if a client is a public client (no client secret).

func (*ClientAuthenticator) ValidateClientForEndpoint

func (c *ClientAuthenticator) ValidateClientForEndpoint(client *schema.OAuthClient, endpoint string) error

ValidateClientForEndpoint validates that a client can access a specific endpoint.

type ClientDetailsResponse

type ClientDetailsResponse struct {
	ClientID                string   `json:"clientID"`
	Name                    string   `json:"name"`
	ApplicationType         string   `json:"applicationType"`
	RedirectURIs            []string `json:"redirectURIs"`
	PostLogoutRedirectURIs  []string `json:"postLogoutRedirectURIs,omitempty"`
	GrantTypes              []string `json:"grantTypes"`
	ResponseTypes           []string `json:"responseTypes"`
	AllowedScopes           []string `json:"allowedScopes,omitempty"`
	TokenEndpointAuthMethod string   `json:"tokenEndpointAuthMethod"`
	RequirePKCE             bool     `json:"requirePKCE"`
	RequireConsent          bool     `json:"requireConsent"`
	TrustedClient           bool     `json:"trustedClient"`
	LogoURI                 string   `json:"logoURI,omitempty"`
	PolicyURI               string   `json:"policyURI,omitempty"`
	TosURI                  string   `json:"tosURI,omitempty"`
	Contacts                []string `json:"contacts,omitempty"`
	CreatedAt               string   `json:"createdAt"`
	UpdatedAt               string   `json:"updatedAt"`
	IsOrgLevel              bool     `json:"isOrgLevel"`
	OrganizationID          string   `json:"organizationID,omitempty"`
}

ClientDetailsResponse represents detailed information about an OAuth client.

type ClientRegistrationRequest

type ClientRegistrationRequest struct {
	ClientName              string   `json:"client_name"                          validate:"required"`
	RedirectURIs            []string `json:"redirect_uris"                        validate:"required,min=1,dive,url"`
	PostLogoutRedirectURIs  []string `json:"post_logout_redirect_uris,omitempty"  validate:"omitempty,dive,url"`
	GrantTypes              []string `json:"grant_types,omitempty"`
	ResponseTypes           []string `json:"response_types,omitempty"`
	ApplicationType         string   `json:"application_type,omitempty"           validate:"omitempty,oneof=web native spa"`
	TokenEndpointAuthMethod string   `json:"token_endpoint_auth_method,omitempty" validate:"omitempty,oneof=client_secret_basic client_secret_post none"`
	LogoURI                 string   `json:"logo_uri,omitempty"                   validate:"omitempty,url"`
	PolicyURI               string   `json:"policy_uri,omitempty"                 validate:"omitempty,url"`
	TosURI                  string   `json:"tos_uri,omitempty"                    validate:"omitempty,url"`
	Contacts                []string `json:"contacts,omitempty"                   validate:"omitempty,dive,email"`
	Scope                   string   `json:"scope,omitempty"`
	RequirePKCE             bool     `json:"require_pkce,omitempty"`
	RequireConsent          bool     `json:"require_consent,omitempty"`
	TrustedClient           bool     `json:"trusted_client,omitempty"`
}

ClientRegistrationRequest represents a dynamic client registration request (RFC 7591).

type ClientRegistrationResponse

type ClientRegistrationResponse struct {
	ClientID                string   `example:"client_01HZ..."                   json:"client_id"`
	ClientSecret            string   `example:"secret_01HZ..."                   json:"client_secret,omitempty"`
	ClientIDIssuedAt        int64    `example:"1609459200"                       json:"client_id_issued_at"`
	ClientSecretExpiresAt   int64    `example:"0"                                json:"client_secret_expires_at"` // 0 = never expires
	ClientName              string   `example:"My Application"                   json:"client_name"`
	RedirectURIs            []string `example:"https://example.com/callback"     json:"redirect_uris"`
	PostLogoutRedirectURIs  []string `json:"post_logout_redirect_uris,omitempty"`
	GrantTypes              []string `example:"authorization_code,refresh_token" json:"grant_types"`
	ResponseTypes           []string `example:"code"                             json:"response_types"`
	ApplicationType         string   `example:"web"                              json:"application_type"`
	TokenEndpointAuthMethod string   `example:"client_secret_basic"              json:"token_endpoint_auth_method"`
	LogoURI                 string   `json:"logo_uri,omitempty"`
	PolicyURI               string   `json:"policy_uri,omitempty"`
	TosURI                  string   `json:"tos_uri,omitempty"`
	Contacts                []string `json:"contacts,omitempty"`
	Scope                   string   `json:"scope,omitempty"`
}

ClientRegistrationResponse represents a successful client registration response (RFC 7591).

type ClientSummary

type ClientSummary struct {
	ClientID        string `json:"clientID"`
	Name            string `json:"name"`
	ApplicationType string `json:"applicationType"`
	CreatedAt       string `json:"createdAt"`
	IsOrgLevel      bool   `json:"isOrgLevel"`
}

ClientSummary represents a summary of an OAuth client.

type ClientUpdateRequest

type ClientUpdateRequest struct {
	Name                    string   `json:"name,omitempty"`
	RedirectURIs            []string `json:"redirect_uris,omitempty"              validate:"omitempty,dive,url"`
	PostLogoutRedirectURIs  []string `json:"post_logout_redirect_uris,omitempty"  validate:"omitempty,dive,url"`
	GrantTypes              []string `json:"grant_types,omitempty"`
	ResponseTypes           []string `json:"response_types,omitempty"`
	AllowedScopes           []string `json:"allowed_scopes,omitempty"`
	TokenEndpointAuthMethod string   `json:"token_endpoint_auth_method,omitempty" validate:"omitempty,oneof=client_secret_basic client_secret_post none"`
	RequirePKCE             *bool    `json:"require_pkce,omitempty"`
	RequireConsent          *bool    `json:"require_consent,omitempty"`
	TrustedClient           *bool    `json:"trusted_client,omitempty"`
	LogoURI                 string   `json:"logo_uri,omitempty"                   validate:"omitempty,url"`
	PolicyURI               string   `json:"policy_uri,omitempty"                 validate:"omitempty,url"`
	TosURI                  string   `json:"tos_uri,omitempty"                    validate:"omitempty,url"`
	Contacts                []string `json:"contacts,omitempty"                   validate:"omitempty,dive,email"`
}

ClientUpdateRequest represents a client update request.

type ClientsListResponse

type ClientsListResponse struct {
	Clients    []ClientSummary `json:"clients"`
	Total      int             `json:"total"`
	Page       int             `json:"page"`
	PageSize   int             `json:"pageSize"`
	TotalPages int             `json:"totalPages"`
}

ClientsListResponse represents a list of OAuth clients.

type Config

type Config struct {
	// Issuer URL for the OIDC Provider
	Issuer string `json:"issuer"`

	// Audience for JWT validation (optional)
	Audience string `json:"audience"`

	// Enable JWT validation strategy for Bearer tokens
	// When enabled, OAuth/OIDC JWTs can be used to authenticate API requests
	EnableJWTValidation bool `json:"enableJwtValidation"`

	// Key configuration
	Keys struct {
		// Path to RSA private key file (PEM format)
		PrivateKeyPath string `json:"privateKeyPath"`
		// Path to RSA public key file (PEM format)
		PublicKeyPath string `json:"publicKeyPath"`
		// Key rotation settings
		RotationInterval string `json:"rotationInterval"` // e.g., "24h"
		KeyLifetime      string `json:"keyLifetime"`      // e.g., "168h" (7 days)
	} `json:"keys"`

	// Token settings
	Tokens struct {
		AccessTokenExpiry  string `json:"accessTokenExpiry"`  // e.g., "1h"
		IDTokenExpiry      string `json:"idTokenExpiry"`      // e.g., "1h"
		RefreshTokenExpiry string `json:"refreshTokenExpiry"` // e.g., "720h" (30 days)
	} `json:"tokens"`

	// Device Flow configuration (RFC 8628)
	DeviceFlow struct {
		Enabled         bool   `json:"enabled"`
		CodeExpiry      string `json:"codeExpiry"`      // e.g., "10m"
		UserCodeLength  int    `json:"userCodeLength"`  // e.g., 8
		UserCodeFormat  string `json:"userCodeFormat"`  // e.g., "XXXX-XXXX"
		PollingInterval int    `json:"pollingInterval"` // e.g., 5 seconds
		VerificationURI string `json:"verificationUri"` // e.g., "/device"
		MaxPollAttempts int    `json:"maxPollAttempts"` // e.g., 120
		CleanupInterval string `json:"cleanupInterval"` // e.g., "5m"
		LoginURL        string `json:"loginUrl"`        // Custom login URL (e.g., "https://yourapp.com/login" or "/auth/signin" for authsome UI)
		APIMode         bool   `json:"apiMode"`         // If true, returns JSON with loginUrl instead of HTTP redirect (better for SPAs/mobile apps)
	} `json:"deviceFlow"`
}

Config represents the OIDC Provider configuration.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the default OIDC Provider configuration.

type ConsentDecision

type ConsentDecision struct {
	Approved bool
	Scopes   []string
}

ConsentDecision represents a user's consent decision.

type ConsentManager

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

ConsentManager handles OAuth consent with optional integration to enterprise consent plugin.

func NewConsentManager

func NewConsentManager(consentSvc *ConsentService, enterpriseConsent EnterpriseConsentService) *ConsentManager

NewConsentManager creates a consent manager with optional enterprise integration.

func (*ConsentManager) CheckConsent

func (cm *ConsentManager) CheckConsent(ctx context.Context, userID xid.ID, clientID, scope string, appID, envID xid.ID, orgID *xid.ID) (bool, error)

CheckConsent checks if user has granted consent for the client and scopes.

func (*ConsentManager) GenerateConsentHTML

func (cm *ConsentManager) GenerateConsentHTML(clientName, clientLogoURI, scope string, redirectURL string) string

GenerateConsentHTML generates HTML for the OAuth consent screen.

func (*ConsentManager) GetConsentPageData

func (cm *ConsentManager) GetConsentPageData(clientName, clientLogoURI, clientDescription, scope string) map[string]any

GetConsentPageData returns data for rendering custom consent templates.

func (*ConsentManager) RecordConsent

func (cm *ConsentManager) RecordConsent(ctx context.Context, userID xid.ID, clientID, scope string, granted bool, appID, envID xid.ID, orgID *xid.ID, expiresAt *time.Time) error

RecordConsent records user's consent decision.

func (*ConsentManager) RevokeConsent

func (cm *ConsentManager) RevokeConsent(ctx context.Context, userID xid.ID, clientID string) error

RevokeConsent revokes user's consent for a client.

func (*ConsentManager) ValidateConsentRequest

func (cm *ConsentManager) ValidateConsentRequest(consentDecision string) error

ValidateConsentRequest validates the consent decision from user.

type ConsentRequest

type ConsentRequest struct {
	Action              string `form:"action"                json:"action"                validate:"required,oneof=allow deny"`
	ClientID            string `form:"client_id"             json:"client_id"             validate:"required"`
	RedirectURI         string `form:"redirect_uri"          json:"redirect_uri"          validate:"required"`
	ResponseType        string `form:"response_type"         json:"response_type"         validate:"required"`
	Scope               string `form:"scope"                 json:"scope"`
	State               string `form:"state"                 json:"state"`
	CodeChallenge       string `form:"code_challenge"        json:"code_challenge"`
	CodeChallengeMethod string `form:"code_challenge_method" json:"code_challenge_method"`
}

ConsentRequest represents the consent form submission.

type ConsentService

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

ConsentService handles OAuth2/OIDC consent management.

func NewConsentService

func NewConsentService(consentRepo *repo.OAuthConsentRepository, clientRepo *repo.OAuthClientRepository) *ConsentService

NewConsentService creates a new consent service.

func (*ConsentService) CheckConsent

func (s *ConsentService) CheckConsent(ctx context.Context, userID xid.ID, clientID string, requestedScopes []string, appID, envID xid.ID, orgID *xid.ID) (bool, error)

CheckConsent checks if user has already consented to the requested scopes for a client.

func (*ConsentService) FormatScopes

func (s *ConsentService) FormatScopes(scopes []string) string

FormatScopes converts a scope slice to a space-separated string.

func (*ConsentService) GetScopeDescriptions

func (s *ConsentService) GetScopeDescriptions(scopes []string) []ScopeInfo

GetScopeDescriptions returns user-friendly descriptions for scopes.

func (*ConsentService) GrantConsent

func (s *ConsentService) GrantConsent(ctx context.Context, userID xid.ID, clientID string, scopes []string, appID, envID xid.ID, orgID *xid.ID, expiresIn *time.Duration) error

GrantConsent stores user's consent decision.

func (*ConsentService) ListUserConsents

func (s *ConsentService) ListUserConsents(ctx context.Context, userID xid.ID, appID, envID xid.ID, orgID *xid.ID) ([]*schema.OAuthConsent, error)

ListUserConsents retrieves all consents granted by a user.

func (*ConsentService) ParseScopes

func (s *ConsentService) ParseScopes(scopeString string) []string

ParseScopes converts a space-separated scope string to a slice.

func (*ConsentService) RequiresConsent

func (s *ConsentService) RequiresConsent(ctx context.Context, clientID string, scopes []string, appID, envID xid.ID, orgID *xid.ID) (bool, error)

RequiresConsent checks if the requested scopes require user consent.

func (*ConsentService) RevokeConsent

func (s *ConsentService) RevokeConsent(ctx context.Context, userID xid.ID, clientID string) error

RevokeConsent removes a user's consent for a client.

type DashboardExtension added in v0.0.15

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

DashboardExtension implements ui.DashboardExtension for the OIDC provider plugin.

func NewDashboardExtension added in v0.0.15

func NewDashboardExtension(
	clientRepo *repository.OAuthClientRepository,
	tokenRepo *repository.OAuthTokenRepository,
	consentRepo *repository.OAuthConsentRepository,
	deviceCodeRepo *repository.DeviceCodeRepository,
	service *Service,
	logger forge.Logger,
) *DashboardExtension

NewDashboardExtension creates a new dashboard extension.

func (*DashboardExtension) BridgeFunctions added in v0.0.15

func (e *DashboardExtension) BridgeFunctions() []ui.BridgeFunction

BridgeFunctions returns bridge functions for OIDC provider.

func (*DashboardExtension) DashboardWidgets added in v0.0.15

func (e *DashboardExtension) DashboardWidgets() []ui.DashboardWidget

DashboardWidgets returns widgets for the main dashboard.

func (*DashboardExtension) ExtensionID added in v0.0.15

func (e *DashboardExtension) ExtensionID() string

ExtensionID returns the unique identifier for this extension.

func (*DashboardExtension) NavigationItems added in v0.0.15

func (e *DashboardExtension) NavigationItems() []ui.NavigationItem

NavigationItems returns navigation items for OAuth & OIDC section.

func (*DashboardExtension) Routes added in v0.0.15

func (e *DashboardExtension) Routes() []ui.Route

Routes returns dashboard routes for OIDC provider pages.

func (*DashboardExtension) SetBaseUIPath added in v0.0.15

func (e *DashboardExtension) SetBaseUIPath(baseUIPath string)

SetBaseUIPath sets the base UI path for navigation links.

func (*DashboardExtension) SettingsPages added in v0.0.15

func (e *DashboardExtension) SettingsPages() []ui.SettingsPage

SettingsPages returns full settings pages.

func (*DashboardExtension) SettingsSections added in v0.0.15

func (e *DashboardExtension) SettingsSections() []ui.SettingsSection

SettingsSections returns settings sections (deprecated, returning empty).

type DatabaseKeyStore added in v0.0.15

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

DatabaseKeyStore manages keys persisted in the database.

func NewDatabaseKeyStore added in v0.0.15

func NewDatabaseKeyStore(db *bun.DB, appID xid.ID, logger interface{ Printf(string, ...any) }) (*DatabaseKeyStore, error)

NewDatabaseKeyStore creates a key store backed by the database.

func (*DatabaseKeyStore) GetActiveKey added in v0.0.15

func (dks *DatabaseKeyStore) GetActiveKey() *KeyPair

GetActiveKey returns the current active signing key.

func (*DatabaseKeyStore) GetAllValidKeys added in v0.0.15

func (dks *DatabaseKeyStore) GetAllValidKeys() []*KeyPair

GetAllValidKeys returns all valid (non-expired) keys.

func (*DatabaseKeyStore) GetJWKS added in v0.0.15

func (dks *DatabaseKeyStore) GetJWKS() (*JWKS, error)

GetJWKS returns the public JWKS for all active keys.

func (*DatabaseKeyStore) GetKey added in v0.0.15

func (dks *DatabaseKeyStore) GetKey(kid string) *KeyPair

GetKey retrieves a specific key by ID (used for verification).

func (*DatabaseKeyStore) GetLastRotation added in v0.0.15

func (dks *DatabaseKeyStore) GetLastRotation() time.Time

GetLastRotation returns when keys were last rotated.

func (*DatabaseKeyStore) RotateKeys added in v0.0.15

func (dks *DatabaseKeyStore) RotateKeys() error

RotateKeys generates a new key and deactivates old ones.

func (*DatabaseKeyStore) ShouldRotate added in v0.0.15

func (dks *DatabaseKeyStore) ShouldRotate() bool

ShouldRotate checks if keys should be rotated.

type DeviceAuthorizationDecisionRequest added in v0.0.15

type DeviceAuthorizationDecisionRequest struct {
	UserCode string `form:"user_code" json:"user_code" validate:"required"`
	Action   string `form:"action"    json:"action"    validate:"required,oneof=approve deny"`
}

DeviceAuthorizationDecisionRequest represents the user's authorization decision.

type DeviceAuthorizationRequest added in v0.0.15

type DeviceAuthorizationRequest struct {
	ClientID string `form:"client_id" json:"client_id" validate:"required"`
	Scope    string `form:"scope"     json:"scope"`
}

DeviceAuthorizationRequest represents a device authorization request.

type DeviceAuthorizationResponse added in v0.0.15

type DeviceAuthorizationResponse struct {
	DeviceCode              string `example:"GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS"      json:"device_code"`
	UserCode                string `example:"WDJB-MJHT"                                      json:"user_code"`
	VerificationURI         string `example:"https://example.com/device"                     json:"verification_uri"`
	VerificationURIComplete string `example:"https://example.com/device?user_code=WDJB-MJHT" json:"verification_uri_complete,omitempty"`
	ExpiresIn               int    `example:"600"                                            json:"expires_in"` // seconds
	Interval                int    `example:"5"                                              json:"interval"`   // polling interval in seconds
}

DeviceAuthorizationResponse represents the device authorization response.

type DeviceCodeEntryResponse added in v0.0.15

type DeviceCodeEntryResponse struct {
	FormAction  string `example:"/oauth2/device/verify" json:"formAction"`
	Placeholder string `example:"XXXX-XXXX"             json:"placeholder"`
	BasePath    string `example:"/api/identity/oauth2"  json:"basePath"`
}

DeviceCodeEntryResponse is returned for the device code entry endpoint (API mode).

type DeviceDecisionResponse added in v0.0.15

type DeviceDecisionResponse struct {
	Success  bool   `example:"true"                           json:"success"`
	Approved bool   `example:"true"                           json:"approved"`
	Message  string `example:"Device authorized successfully" json:"message"`
}

DeviceDecisionResponse is returned after the authorization decision (API mode).

type DeviceVerificationInfo added in v0.0.15

type DeviceVerificationInfo struct {
	UserCode   string
	ClientName string
	Scopes     []ScopeInfo
}

DeviceVerificationInfo contains info to display during verification.

type DeviceVerificationRequest added in v0.0.15

type DeviceVerificationRequest struct {
	UserCode string `form:"user_code" json:"user_code" validate:"required"`
}

DeviceVerificationRequest represents the user code verification request.

type DeviceVerifyResponse added in v0.0.15

type DeviceVerifyResponse struct {
	UserCode          string      `example:"WDJB-MJHT"                    json:"userCode"`
	UserCodeFormatted string      `example:"WDJB-MJHT"                    json:"userCodeFormatted"`
	ClientName        string      `example:"My Application"               json:"clientName"`
	ClientID          string      `example:"client_01HZ..."               json:"clientId"`
	LogoURI           string      `example:"https://example.com/logo.png" json:"logoUri,omitempty"`
	Scopes            []ScopeInfo `json:"scopes"`
	AuthorizeURL      string      `example:"/oauth2/device/authorize"     json:"authorizeUrl"`
}

DeviceVerifyResponse is returned after verifying a device code (API mode).

type DiscoveryResponse

type DiscoveryResponse struct {
	Issuer                                    string   `example:"https://auth.example.com"                             json:"issuer"`
	AuthorizationEndpoint                     string   `example:"https://auth.example.com/oauth2/authorize"            json:"authorization_endpoint"`
	TokenEndpoint                             string   `example:"https://auth.example.com/oauth2/token"                json:"token_endpoint"`
	UserInfoEndpoint                          string   `example:"https://auth.example.com/oauth2/userinfo"             json:"userinfo_endpoint"`
	JwksURI                                   string   `example:"https://auth.example.com/oauth2/jwks"                 json:"jwks_uri"`
	RegistrationEndpoint                      string   `example:"https://auth.example.com/oauth2/register"             json:"registration_endpoint,omitempty"`
	IntrospectionEndpoint                     string   `example:"https://auth.example.com/oauth2/introspect"           json:"introspection_endpoint,omitempty"`
	RevocationEndpoint                        string   `example:"https://auth.example.com/oauth2/revoke"               json:"revocation_endpoint,omitempty"`
	DeviceAuthorizationEndpoint               string   `example:"https://auth.example.com/oauth2/device/authorize"     json:"device_authorization_endpoint,omitempty"`
	ResponseTypesSupported                    []string `example:"code,token,id_token"                                  json:"response_types_supported"`
	ResponseModesSupported                    []string `example:"query,fragment,form_post"                             json:"response_modes_supported,omitempty"`
	GrantTypesSupported                       []string `example:"authorization_code,refresh_token,client_credentials"  json:"grant_types_supported"`
	SubjectTypesSupported                     []string `example:"public"                                               json:"subject_types_supported"`
	IDTokenSigningAlgValuesSupported          []string `example:"RS256"                                                json:"id_token_signing_alg_values_supported"`
	ScopesSupported                           []string `example:"openid,profile,email"                                 json:"scopes_supported"`
	TokenEndpointAuthMethodsSupported         []string `example:"client_secret_basic,client_secret_post"               json:"token_endpoint_auth_methods_supported"`
	ClaimsSupported                           []string `example:"sub,name,email,picture"                               json:"claims_supported"`
	CodeChallengeMethodsSupported             []string `example:"S256,plain"                                           json:"code_challenge_methods_supported,omitempty"`
	IntrospectionEndpointAuthMethodsSupported []string `json:"introspection_endpoint_auth_methods_supported,omitempty"`
	RevocationEndpointAuthMethodsSupported    []string `json:"revocation_endpoint_auth_methods_supported,omitempty"`
	RequestParameterSupported                 bool     `json:"request_parameter_supported,omitempty"`
	RequestURIParameterSupported              bool     `json:"request_uri_parameter_supported,omitempty"`
	RequireRequestURIRegistration             bool     `json:"require_request_uri_registration,omitempty"`
	ClaimsParameterSupported                  bool     `json:"claims_parameter_supported,omitempty"`
}

DiscoveryResponse represents the OIDC discovery document.

type DiscoveryService

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

DiscoveryService handles OIDC discovery document generation.

func NewDiscoveryService

func NewDiscoveryService(config Config) *DiscoveryService

NewDiscoveryService creates a new discovery service.

func (*DiscoveryService) GetDiscoveryDocument

func (s *DiscoveryService) GetDiscoveryDocument(ctx context.Context, baseURL, basePath string) *DiscoveryResponse

GetDiscoveryDocument generates the OIDC discovery document (.well-known/openid-configuration).

func (*DiscoveryService) GetIssuer

func (s *DiscoveryService) GetIssuer() string

GetIssuer returns the configured issuer URL.

func (*DiscoveryService) SupportsGrantType

func (s *DiscoveryService) SupportsGrantType(grantType string) bool

SupportsGrantType checks if a grant type is supported.

func (*DiscoveryService) SupportsResponseType

func (s *DiscoveryService) SupportsResponseType(responseType string) bool

SupportsResponseType checks if a response type is supported.

func (*DiscoveryService) SupportsScope

func (s *DiscoveryService) SupportsScope(scope string) bool

SupportsScope checks if a scope is supported.

type EnterpriseConsentService

type EnterpriseConsentService interface {
	// CreateConsent records user consent
	CreateConsent(ctx context.Context, orgID, userID string, req any) (any, error)

	// GetConsent retrieves consent status
	GetConsent(ctx context.Context, id string) (any, error)

	// RevokeConsent revokes a consent
	RevokeConsent(ctx context.Context, id string) error
}

EnterpriseConsentService interface for enterprise consent plugin This allows optional integration without hard dependency.

type ErrorResponse

type ErrorResponse = responses.ErrorResponse

ErrorResponse is the standard OAuth2/OIDC error response.

type Handler

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

Handler handles OIDC provider HTTP endpoints.

func NewHandler

func NewHandler(svc *Service, basePath, loginURL string, apiMode bool) *Handler

NewHandler creates a new OIDC handler.

func (*Handler) Authorize

func (h *Handler) Authorize(c forge.Context) error

Authorize handles OAuth2/OIDC authorization requests.

func (*Handler) DeviceAuthorize added in v0.0.15

func (h *Handler) DeviceAuthorize(c forge.Context) error

DeviceAuthorize initiates the device authorization flow.

func (*Handler) DeviceAuthorizeDecision added in v0.0.15

func (h *Handler) DeviceAuthorizeDecision(c forge.Context) error

DeviceAuthorizeDecision handles the user's authorization decision.

func (*Handler) DeviceCodeEntry added in v0.0.15

func (h *Handler) DeviceCodeEntry(c forge.Context) error

DeviceCodeEntry shows the device code entry form.

func (*Handler) DeviceVerify added in v0.0.15

func (h *Handler) DeviceVerify(c forge.Context) error

DeviceVerify verifies the user code and shows the consent screen.

func (*Handler) Discovery

func (h *Handler) Discovery(c forge.Context) error

Discovery handles the OIDC discovery endpoint (.well-known/openid-configuration).

func (*Handler) HandleConsent

func (h *Handler) HandleConsent(c forge.Context) error

HandleConsent processes the consent form submission.

func (*Handler) IntrospectToken

func (h *Handler) IntrospectToken(c forge.Context) error

IntrospectToken handles token introspection requests.

func (*Handler) JWKS

func (h *Handler) JWKS(c forge.Context) error

JWKS returns the JSON Web Key Set.

func (*Handler) RevokeToken

func (h *Handler) RevokeToken(c forge.Context) error

RevokeToken handles token revocation requests.

func (*Handler) Token

func (h *Handler) Token(c forge.Context) error

Token handles the token endpoint.

func (*Handler) UserInfo

func (h *Handler) UserInfo(c forge.Context) error

UserInfo returns user information based on the access token.

type IDTokenClaims

type IDTokenClaims struct {
	jwt.RegisteredClaims

	Nonce             string `json:"nonce,omitempty"`
	AuthTime          int64  `json:"auth_time"`
	SessionState      string `json:"session_state,omitempty"`
	PreferredUsername string `json:"preferred_username,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"`
}

IDTokenClaims represents the claims for an OIDC ID token.

type IntrospectionService

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

IntrospectionService handles RFC 7662 token introspection operations.

func NewIntrospectionService

func NewIntrospectionService(tokenRepo *repo.OAuthTokenRepository, clientRepo *repo.OAuthClientRepository, userSvc UserService) *IntrospectionService

NewIntrospectionService creates a new token introspection service.

func (*IntrospectionService) IntrospectByJTI

func (s *IntrospectionService) IntrospectByJTI(ctx context.Context, jti string, requestingClientID string) (*TokenIntrospectionResponse, error)

IntrospectByJTI introspects a token by its JWT ID.

func (*IntrospectionService) IntrospectToken

func (s *IntrospectionService) IntrospectToken(ctx context.Context, req *TokenIntrospectionRequest, requestingClientID string) (*TokenIntrospectionResponse, error)

IntrospectToken implements RFC 7662 token introspection Returns token metadata if active, or {active: false} if inactive/invalid.

func (*IntrospectionService) ValidateIntrospectionRequest

func (s *IntrospectionService) ValidateIntrospectionRequest(req *TokenIntrospectionRequest) error

ValidateIntrospectionRequest validates the introspection request.

type JWK

type JWK struct {
	KeyType   string `json:"kty"`
	Use       string `json:"use"`
	KeyID     string `json:"kid"`
	Algorithm string `json:"alg"`
	N         string `json:"n"` // RSA modulus
	E         string `json:"e"` // RSA exponent
}

JWK represents a JSON Web Key.

type JWKResponse

type JWKResponse = JWK

JWKResponse is an alias for the JWKS key structure defined in jwks.go.

type JWKS

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

JWKS represents a JSON Web Key Set.

type JWKSResponse

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

JWKSResponse represents the JSON Web Key Set response.

type JWKSService

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

JWKSService manages JSON Web Key Sets for the OIDC Provider.

func NewDatabaseJWKSService added in v0.0.15

func NewDatabaseJWKSService(db *bun.DB, appID xid.ID, logger interface{ Printf(string, ...any) }) (*JWKSService, error)

NewDatabaseJWKSService creates a JWKS service backed by the database.

func NewJWKSService

func NewJWKSService() (*JWKSService, error)

NewJWKSService creates a new JWKS service.

func NewJWKSServiceFromFiles

func NewJWKSServiceFromFiles(privateKeyPath, publicKeyPath, rotationInterval, keyLifetime string) (*JWKSService, error)

NewJWKSServiceFromFiles creates a JWKS service with keys loaded from files.

func (*JWKSService) GetActiveKeyPair

func (j *JWKSService) GetActiveKeyPair() *KeyPair

GetActiveKeyPair returns the current active key pair for signing.

func (*JWKSService) GetCurrentKeyID

func (j *JWKSService) GetCurrentKeyID() string

GetCurrentKeyID returns the ID of the current active key.

func (*JWKSService) GetCurrentPrivateKey

func (j *JWKSService) GetCurrentPrivateKey() *rsa.PrivateKey

GetCurrentPrivateKey returns the private key of the current active key.

func (*JWKSService) GetJWKS

func (j *JWKSService) GetJWKS() (*JWKS, error)

GetJWKS returns the current JSON Web Key Set.

func (*JWKSService) GetKeyByID

func (j *JWKSService) GetKeyByID(keyID string) (*JWK, error)

GetKeyByID returns a specific key by its ID.

func (*JWKSService) GetLastRotation added in v0.0.15

func (j *JWKSService) GetLastRotation() time.Time

GetLastRotation returns the last key rotation time.

func (*JWKSService) GetPublicKey

func (j *JWKSService) GetPublicKey(keyID string) (*rsa.PublicKey, error)

GetPublicKey returns the public key for a given key ID.

func (*JWKSService) RotateKeys

func (j *JWKSService) RotateKeys() error

RotateKeys triggers key rotation.

func (*JWKSService) ShouldRotate

func (j *JWKSService) ShouldRotate() bool

ShouldRotate checks if keys should be rotated.

type JWTAuthError added in v0.0.15

type JWTAuthError struct {
	Message string
	Err     error
}

JWTAuthError represents a JWT authentication error.

func (*JWTAuthError) Error added in v0.0.15

func (e *JWTAuthError) Error() string

func (*JWTAuthError) Unwrap added in v0.0.15

func (e *JWTAuthError) Unwrap() error

type JWTService

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

JWTService handles JWT token generation and signing for OIDC Provider.

func NewJWTService

func NewJWTService(issuer string, jwksService *JWKSService) (*JWTService, error)

NewJWTService creates a new JWT service with JWKS service for key management.

func (*JWTService) GenerateAccessToken

func (j *JWTService) GenerateAccessToken(userID, clientID, scope string) (string, error)

GenerateAccessToken creates a signed access token.

func (*JWTService) GenerateAccessTokenWithAppID added in v0.0.15

func (j *JWTService) GenerateAccessTokenWithAppID(userID, clientID, scope, appID string) (string, error)

GenerateAccessTokenWithAppID creates a signed access token with optional app_id.

func (*JWTService) GenerateIDToken

func (j *JWTService) GenerateIDToken(userID, clientID, nonce string, authTime time.Time, userInfo map[string]any) (string, error)

GenerateIDToken creates a signed OIDC ID token.

func (*JWTService) VerifyToken

func (j *JWTService) VerifyToken(tokenString string) (*jwt.Token, error)

VerifyToken verifies and parses a JWT token.

type JWTValidationStrategy added in v0.0.15

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

JWTValidationStrategy validates OAuth/OIDC JWT access tokens This allows any AuthSome endpoint to accept JWT tokens from the OIDC provider.

func NewJWTValidationStrategy added in v0.0.15

func NewJWTValidationStrategy(
	oidcJWTSvc *JWTService,
	userSvc user.ServiceInterface,
	issuer string,
	audience string,
	logger forge.Logger,
) *JWTValidationStrategy

NewJWTValidationStrategy creates a new JWT validation strategy.

func (*JWTValidationStrategy) Authenticate added in v0.0.15

func (s *JWTValidationStrategy) Authenticate(ctx context.Context, credentials any) (*contexts.AuthContext, error)

Authenticate validates the JWT and builds auth context.

func (*JWTValidationStrategy) Extract added in v0.0.15

func (s *JWTValidationStrategy) Extract(c forge.Context) (any, bool)

Extract attempts to extract a JWT from Authorization header.

func (*JWTValidationStrategy) ID added in v0.0.15

func (s *JWTValidationStrategy) ID() string

ID returns the strategy identifier.

func (*JWTValidationStrategy) Priority added in v0.0.15

func (s *JWTValidationStrategy) Priority() int

Priority returns 15 (after API keys 10, before bearer session tokens 20).

type KeyPair

type KeyPair struct {
	ID         string
	PrivateKey *rsa.PrivateKey
	PublicKey  *rsa.PublicKey
	CreatedAt  time.Time
	ExpiresAt  time.Time
	Active     bool // Whether this key is used for signing new tokens
}

KeyPair represents an RSA key pair with metadata.

type KeyStore

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

KeyStore manages multiple key pairs for rotation (in-memory implementation).

func NewKeyStore

func NewKeyStore() (*KeyStore, error)

NewKeyStore creates a new key store with initial key pair.

func NewKeyStoreFromFiles

func NewKeyStoreFromFiles(privateKeyPath, publicKeyPath, rotationInterval, keyLifetime string) (*KeyStore, error)

NewKeyStoreFromFiles creates a new key store with keys loaded from files.

func (*KeyStore) GetActiveKey

func (ks *KeyStore) GetActiveKey() *KeyPair

GetActiveKey returns the current active key pair for signing.

func (*KeyStore) GetAllValidKeys

func (ks *KeyStore) GetAllValidKeys() []*KeyPair

GetAllValidKeys returns all keys that haven't expired.

func (*KeyStore) GetKey added in v0.0.15

func (ks *KeyStore) GetKey(kid string) *KeyPair

GetKey is an alias for GetKeyByID (implements KeyStoreInterface).

func (*KeyStore) GetKeyByID

func (ks *KeyStore) GetKeyByID(keyID string) *KeyPair

GetKeyByID returns a key pair by its ID.

func (*KeyStore) GetLastRotation added in v0.0.15

func (ks *KeyStore) GetLastRotation() time.Time

GetLastRotation returns the last rotation time.

func (*KeyStore) RotateKeys

func (ks *KeyStore) RotateKeys() error

RotateKeys generates a new key and cleans up expired keys.

func (*KeyStore) ShouldRotate

func (ks *KeyStore) ShouldRotate() bool

ShouldRotate checks if keys should be rotated based on the rotation interval.

type KeyStoreInterface added in v0.0.15

type KeyStoreInterface interface {
	GetActiveKey() *KeyPair
	GetKey(kid string) *KeyPair
	GetAllValidKeys() []*KeyPair
	RotateKeys() error
	ShouldRotate() bool
	GetLastRotation() time.Time
}

KeyStoreInterface defines the interface for key storage backends.

type OAuthErrorResponse

type OAuthErrorResponse struct {
	Error            string `example:"invalid_request"                                 json:"error"`
	ErrorDescription string `example:"The request is missing a required parameter"     json:"error_description,omitempty"`
	ErrorURI         string `example:"https://docs.example.com/errors/invalid_request" json:"error_uri,omitempty"`
	State            string `json:"state,omitempty"`
}

OAuthErrorResponse represents an OAuth2-specific error response.

type Plugin

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

Plugin wires the OIDC Provider service and registers routes.

func NewPlugin

func NewPlugin(opts ...PluginOption) *Plugin

NewPlugin creates a new OIDC provider plugin instance with optional configuration.

func (*Plugin) DashboardExtension added in v0.0.15

func (p *Plugin) DashboardExtension() ui.DashboardExtension

DashboardExtension implements the PluginWithDashboardExtension interface This allows the dashboard plugin to discover and register our UI components.

func (*Plugin) ID

func (p *Plugin) ID() string

func (*Plugin) Init

func (p *Plugin) Init(authInst core.Authsome) error

Init accepts auth instance with GetDB method.

func (*Plugin) Migrate

func (p *Plugin) Migrate() error

Migrate runs database migrations.

func (*Plugin) RegisterExtensions

func (p *Plugin) RegisterExtensions(reg any) error

RegisterExtensions registers the plugin with the extension registry (deprecated - use DashboardExtension).

func (*Plugin) RegisterHooks

func (p *Plugin) RegisterHooks(hooksRegistry *hooks.HookRegistry) error

RegisterHooks registers plugin hooks.

func (*Plugin) RegisterRoutes

func (p *Plugin) RegisterRoutes(router forge.Router) error

RegisterRoutes mounts OIDC Provider endpoints.

func (*Plugin) RegisterServiceDecorators

func (p *Plugin) RegisterServiceDecorators(services *registry.ServiceRegistry) error

RegisterServiceDecorators registers service decorators.

func (*Plugin) Shutdown

func (p *Plugin) Shutdown() error

Shutdown performs cleanup when the plugin is shutting down.

type PluginOption

type PluginOption func(*Plugin)

PluginOption is a functional option for configuring the OIDC provider plugin.

func WithAccessTokenExpiry added in v0.0.15

func WithAccessTokenExpiry(expiry string) PluginOption

WithAccessTokenExpiry sets the access token expiry duration.

func WithAudience added in v0.0.15

func WithAudience(audience string) PluginOption

WithAudience sets the expected JWT audience for validation.

func WithDefaultConfig

func WithDefaultConfig(cfg Config) PluginOption

WithDefaultConfig sets the default configuration for the plugin.

func WithDeviceFlowAPIMode added in v0.0.15

func WithDeviceFlowAPIMode(enabled bool) PluginOption

WithDeviceFlowAPIMode enables API mode for device flow authentication When enabled, returns JSON with loginUrl instead of HTTP redirect (better for SPAs/mobile apps) Default is false (HTML redirect mode for backward compatibility).

func WithDeviceFlowCleanupInterval added in v0.0.15

func WithDeviceFlowCleanupInterval(interval string) PluginOption

WithDeviceFlowCleanupInterval sets the cleanup interval for expired device codes.

func WithDeviceFlowCodeExpiry added in v0.0.15

func WithDeviceFlowCodeExpiry(expiry string) PluginOption

WithDeviceFlowCodeExpiry sets the device code expiry duration.

func WithDeviceFlowConfig added in v0.0.15

func WithDeviceFlowConfig(enabled bool, codeExpiry string, userCodeLength int, userCodeFormat string, pollingInterval int, verificationURI string, maxPollAttempts int, cleanupInterval string, loginURL string, apiMode bool) PluginOption

WithDeviceFlowConfig sets all device flow configuration at once.

func WithDeviceFlowEnabled added in v0.0.15

func WithDeviceFlowEnabled(enabled bool) PluginOption

WithDeviceFlowEnabled enables or disables the device flow.

func WithDeviceFlowLoginURL added in v0.0.15

func WithDeviceFlowLoginURL(loginURL string) PluginOption

WithDeviceFlowLoginURL sets the custom login URL for device flow authentication If not set, defaults to "/auth/signin". Use this to redirect to your frontend's login page. Example: "https://yourapp.com/login" or "https://app.example.com/auth/login"

func WithDeviceFlowMaxPollAttempts added in v0.0.15

func WithDeviceFlowMaxPollAttempts(max int) PluginOption

WithDeviceFlowMaxPollAttempts sets the maximum number of polling attempts.

func WithDeviceFlowPollingInterval added in v0.0.15

func WithDeviceFlowPollingInterval(interval int) PluginOption

WithDeviceFlowPollingInterval sets the polling interval in seconds.

func WithDeviceFlowUserCodeFormat added in v0.0.15

func WithDeviceFlowUserCodeFormat(format string) PluginOption

WithDeviceFlowUserCodeFormat sets the format of the user code (e.g., "XXXX-XXXX").

func WithDeviceFlowUserCodeLength added in v0.0.15

func WithDeviceFlowUserCodeLength(length int) PluginOption

WithDeviceFlowUserCodeLength sets the length of the user code.

func WithDeviceFlowVerificationURI added in v0.0.15

func WithDeviceFlowVerificationURI(uri string) PluginOption

WithDeviceFlowVerificationURI sets the verification URI for device flow.

func WithIDTokenExpiry added in v0.0.15

func WithIDTokenExpiry(expiry string) PluginOption

WithIDTokenExpiry sets the ID token expiry duration.

func WithIssuer

func WithIssuer(issuer string) PluginOption

WithIssuer sets the OIDC issuer URL.

func WithJWTValidationEnabled added in v0.0.15

func WithJWTValidationEnabled(enabled bool) PluginOption

WithJWTValidationEnabled enables JWT validation strategy When enabled, OAuth/OIDC JWTs can be used to authenticate API requests globally.

func WithKeyLifetime added in v0.0.15

func WithKeyLifetime(lifetime string) PluginOption

WithKeyLifetime sets the key lifetime.

func WithKeyRotationInterval added in v0.0.15

func WithKeyRotationInterval(interval string) PluginOption

WithKeyRotationInterval sets the key rotation interval.

func WithPrivateKeyPath added in v0.0.15

func WithPrivateKeyPath(path string) PluginOption

WithPrivateKeyPath sets the path to the RSA private key file.

func WithPublicKeyPath added in v0.0.15

func WithPublicKeyPath(path string) PluginOption

WithPublicKeyPath sets the path to the RSA public key file.

func WithRefreshTokenExpiry added in v0.0.15

func WithRefreshTokenExpiry(expiry string) PluginOption

WithRefreshTokenExpiry sets the refresh token expiry duration.

type RegistrationService

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

RegistrationService handles RFC 7591 dynamic client registration operations.

func NewRegistrationService

func NewRegistrationService(clientRepo *repo.OAuthClientRepository, config Config) *RegistrationService

NewRegistrationService creates a new client registration service.

func (*RegistrationService) RegisterClient

func (s *RegistrationService) RegisterClient(ctx context.Context, req *ClientRegistrationRequest, appID, envID xid.ID, orgID *xid.ID) (*ClientRegistrationResponse, error)

RegisterClient implements RFC 7591 dynamic client registration.

func (*RegistrationService) ValidateRegistrationRequest

func (s *RegistrationService) ValidateRegistrationRequest(req *ClientRegistrationRequest) error

ValidateRegistrationRequest validates a client registration request per RFC 7591.

type RevokeTokenService

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

RevokeTokenService handles RFC 7009 token revocation operations.

func NewRevokeTokenService

func NewRevokeTokenService(tokenRepo *repo.OAuthTokenRepository) *RevokeTokenService

NewRevokeTokenService creates a new token revocation service.

func (*RevokeTokenService) AuthenticateClient

func (s *RevokeTokenService) AuthenticateClient(r *http.Request, clientRepo *repo.OAuthClientRepository) (*ClientAuthResult, error)

AuthenticateClient performs client authentication for the revocation endpoint Supports client_secret_basic and client_secret_post methods.

func (*RevokeTokenService) RevokeByJTI

func (s *RevokeTokenService) RevokeByJTI(ctx context.Context, jti string) error

RevokeByJTI revokes a token by its JWT ID.

func (*RevokeTokenService) RevokeToken

RevokeToken implements RFC 7009 token revocation Returns nil even if token doesn't exist (per RFC 7009 spec).

type ScopeInfo

type ScopeInfo struct {
	Name        string
	Description string
}

ScopeInfo represents a scope with its description for consent screens.

type Service

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

Service provides enterprise OIDC Provider operations with org-aware support.

func NewService

func NewService(config Config) *Service

NewService creates a new OIDC Provider service with default config.

func NewServiceWithRepos

func NewServiceWithRepos(clientRepo *repo.OAuthClientRepository, config Config, db *bun.DB, appID xid.ID, logger interface{ Printf(string, ...interface{}) }) *Service

NewServiceWithRepos creates a new OIDC Provider service with repositories.

func (*Service) CreateAuthorizationCode

func (s *Service) CreateAuthorizationCode(ctx context.Context, req *AuthorizeRequest, userID xid.ID, sessionID xid.ID) (*schema.AuthorizationCode, error)

CreateAuthorizationCode creates and stores an authorization code with full context.

func (*Service) ExchangeCodeForTokens

func (s *Service) ExchangeCodeForTokens(ctx context.Context, authCode *schema.AuthorizationCode, userInfo map[string]interface{}) (*TokenResponse, error)

ExchangeCodeForTokens exchanges an authorization code for JWT tokens.

func (*Service) ExtractContext

func (s *Service) ExtractContext(ctx context.Context) (appID, envID xid.ID, orgID *xid.ID, err error)

ExtractContext extracts app, env, and org context from request context.

func (*Service) GenerateAuthorizationCode

func (s *Service) GenerateAuthorizationCode() (string, error)

GenerateAuthorizationCode generates a secure authorization code.

func (*Service) GenerateClientCredentialsToken

func (s *Service) GenerateClientCredentialsToken(ctx context.Context, client *schema.OAuthClient, scope string) (*TokenResponse, error)

GenerateClientCredentialsToken generates a token for client credentials grant (M2M) Implements OAuth2 Client Credentials Grant (RFC 6749 Section 4.4).

func (*Service) GenerateTokensForDeviceCode added in v0.0.15

func (s *Service) GenerateTokensForDeviceCode(ctx context.Context, deviceCode *schema.DeviceCode, client *schema.OAuthClient) (*TokenResponse, error)

GenerateTokensForDeviceCode generates tokens for a device code grant.

func (*Service) GetConfig added in v0.0.15

func (s *Service) GetConfig() interface{}

GetConfig returns the service configuration as interface{} to avoid import cycles.

func (*Service) GetConfigTyped added in v0.0.15

func (s *Service) GetConfigTyped() Config

GetConfigTyped returns the typed configuration (for internal use).

func (*Service) GetCurrentKeyID added in v0.0.15

func (s *Service) GetCurrentKeyID() (string, error)

GetCurrentKeyID returns the current JWT signing key ID.

func (*Service) GetDeviceFlowService added in v0.0.15

func (s *Service) GetDeviceFlowService() interface{}

GetDeviceFlowService returns the device flow service if enabled as interface{}.

func (*Service) GetJWKS

func (s *Service) GetJWKS() (*JWKS, error)

GetJWKS returns the JSON Web Key Set for token verification.

func (*Service) GetLastKeyRotation added in v0.0.15

func (s *Service) GetLastKeyRotation() time.Time

GetLastKeyRotation returns the last key rotation time.

func (*Service) GetUserInfoFromToken

func (s *Service) GetUserInfoFromToken(ctx context.Context, accessToken string) (map[string]interface{}, error)

GetUserInfoFromToken retrieves user information based on an access token.

func (*Service) MarkCodeAsUsed

func (s *Service) MarkCodeAsUsed(ctx context.Context, code string) error

MarkCodeAsUsed marks an authorization code as used.

func (*Service) RefreshAccessToken

func (s *Service) RefreshAccessToken(ctx context.Context, refreshToken, clientID, requestedScope string) (*TokenResponse, error)

RefreshAccessToken refreshes an access token using a refresh token Implements OAuth2 Refresh Token Grant (RFC 6749 Section 6) Optionally rotates the refresh token for improved security.

func (*Service) RotateKeys added in v0.0.15

func (s *Service) RotateKeys() error

RotateKeys manually triggers a JWT key rotation.

func (*Service) SetDeviceFlowService added in v0.0.15

func (s *Service) SetDeviceFlowService(deviceFlowSvc *deviceflow.Service)

SetDeviceFlowService sets the device flow service.

func (*Service) SetRepositories

func (s *Service) SetRepositories(
	clientRepo *repo.OAuthClientRepository,
	codeRepo *repo.AuthorizationCodeRepository,
	tokenRepo *repo.OAuthTokenRepository,
	consentRepo *repo.OAuthConsentRepository,
)

SetRepositories configures all required repositories.

func (*Service) SetSessionService

func (s *Service) SetSessionService(sessionSvc *session.Service)

SetSessionService configures the session service.

func (*Service) SetUserService

func (s *Service) SetUserService(userSvc *user.Service)

SetUserService sets the user service.

func (*Service) StartKeyRotation

func (s *Service) StartKeyRotation()

StartKeyRotation begins automatic key rotation in the background.

func (*Service) StopKeyRotation

func (s *Service) StopKeyRotation()

StopKeyRotation stops the automatic key rotation.

func (*Service) ValidateAuthorizationCode

func (s *Service) ValidateAuthorizationCode(ctx context.Context, code, clientID, redirectURI, codeVerifier string) (*schema.AuthorizationCode, error)

ValidateAuthorizationCode validates and retrieves an authorization code.

func (*Service) ValidateAuthorizeRequest

func (s *Service) ValidateAuthorizeRequest(ctx context.Context, req *AuthorizeRequest) error

ValidateAuthorizeRequest validates an OAuth2/OIDC authorization request.

type TokenIntrospectionRequest

type TokenIntrospectionRequest struct {
	Token         string `form:"token"           json:"token"           validate:"required"`
	TokenTypeHint string `form:"token_type_hint" json:"token_type_hint"` // access_token, refresh_token
	ClientID      string `form:"client_id"       json:"client_id"`
	ClientSecret  string `form:"client_secret"   json:"client_secret"`
}

TokenIntrospectionRequest represents a token introspection request (RFC 7662).

type TokenIntrospectionResponse

type TokenIntrospectionResponse struct {
	Active    bool     `example:"true"                     json:"active"`
	Scope     string   `example:"openid profile email"     json:"scope,omitempty"`
	ClientID  string   `example:"client_123"               json:"client_id,omitempty"`
	Username  string   `example:"johndoe"                  json:"username,omitempty"`
	TokenType string   `example:"Bearer"                   json:"token_type,omitempty"`
	Exp       int64    `example:"1609459200"               json:"exp,omitempty"`
	Iat       int64    `example:"1609455600"               json:"iat,omitempty"`
	Nbf       int64    `example:"1609455600"               json:"nbf,omitempty"`
	Sub       string   `example:"01HZ..."                  json:"sub,omitempty"`
	Aud       []string `example:"https://api.example.com"  json:"aud,omitempty"`
	Iss       string   `example:"https://auth.example.com" json:"iss,omitempty"`
	Jti       string   `example:"token_01HZ..."            json:"jti,omitempty"`
}

TokenIntrospectionResponse represents a token introspection response (RFC 7662).

type TokenRequest

type TokenRequest struct {
	GrantType    string `form:"grant_type"    json:"grant_type"    validate:"required"`
	Code         string `form:"code"          json:"code"`
	RedirectURI  string `form:"redirect_uri"  json:"redirect_uri"`
	ClientID     string `form:"client_id"     json:"client_id"`
	ClientSecret string `form:"client_secret" json:"client_secret"`
	CodeVerifier string `form:"code_verifier" json:"code_verifier"`
	RefreshToken string `form:"refresh_token" json:"refresh_token"`
	Scope        string `form:"scope"         json:"scope"`
	// Client Credentials grant
	Audience string `form:"audience" json:"audience"`
	// Device Code grant (RFC 8628)
	DeviceCode string `form:"device_code" json:"device_code"`
}

TokenRequest represents the token endpoint request.

type TokenResponse

type TokenResponse struct {
	AccessToken  string `example:"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." json:"access_token"`
	TokenType    string `example:"Bearer"                                  json:"token_type"`
	ExpiresIn    int    `example:"3600"                                    json:"expires_in"`
	RefreshToken string `example:"def50200..."                             json:"refresh_token,omitempty"`
	IDToken      string `example:"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." json:"id_token,omitempty"`
	Scope        string `example:"openid profile email"                    json:"scope,omitempty"`
}

TokenResponse represents the OAuth2/OIDC token response.

type TokenRevocationRequest

type TokenRevocationRequest struct {
	Token         string `form:"token"           json:"token"           validate:"required"`
	TokenTypeHint string `form:"token_type_hint" json:"token_type_hint"` // access_token, refresh_token
	ClientID      string `form:"client_id"       json:"client_id"`
	ClientSecret  string `form:"client_secret"   json:"client_secret"`
}

TokenRevocationRequest represents a token revocation request (RFC 7009).

type UserInfoResponse

type UserInfoResponse struct {
	Sub               string `example:"01HZ..."                        json:"sub"`
	Email             string `example:"user@example.com"               json:"email,omitempty"`
	EmailVerified     bool   `example:"true"                           json:"email_verified,omitempty"`
	Name              string `example:"John Doe"                       json:"name,omitempty"`
	GivenName         string `example:"John"                           json:"given_name,omitempty"`
	FamilyName        string `example:"Doe"                            json:"family_name,omitempty"`
	MiddleName        string `json:"middle_name,omitempty"`
	Nickname          string `json:"nickname,omitempty"`
	PreferredUsername string `example:"johndoe"                        json:"preferred_username,omitempty"`
	Profile           string `json:"profile,omitempty"`
	Picture           string `example:"https://example.com/avatar.jpg" json:"picture,omitempty"`
	Website           string `json:"website,omitempty"`
	Gender            string `json:"gender,omitempty"`
	Birthdate         string `json:"birthdate,omitempty"`
	Zoneinfo          string `json:"zoneinfo,omitempty"`
	Locale            string `json:"locale,omitempty"`
	UpdatedAt         int64  `json:"updated_at,omitempty"`
	PhoneNumber       string `json:"phone_number,omitempty"`
	PhoneVerified     bool   `json:"phone_number_verified,omitempty"`
}

UserInfoResponse represents the OIDC userinfo endpoint response.

type UserService

type UserService interface {
	FindByID(ctx context.Context, userID xid.ID) (any, error)
}

UserService interface for getting user information during introspection Note: This uses interface{} for userID to allow xid.ID or string.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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