Documentation
¶
Overview ¶
Package oauth provides database migration management for the OAuth plugin.
This file handles parsing and loading migration files from the embedded filesystem, supporting multiple SQL dialects (PostgreSQL, MySQL, SQLite).
Migration Versioning:
- Version 001: Initial schema (oauth_connections table)
- Version 002+: Additional migrations (indexes, columns, etc.)
File Naming Convention:
- Up migrations: 002_add_index.up.sql
- Down migrations: 002_add_index.down.sql
Package oauth provides OAuth 2.0 / OpenID Connect authentication for Aegis.
This plugin enables "Login with Google", "Login with GitHub", and other OAuth-based authentication flows. It uses the Goth library as the provider implementation while maintaining abstraction for potential alternatives.
OAuth Flow:
- User clicks "Login with Google" → GET /auth/oauth/google
- Plugin generates CSRF state token and stores it in signed cookie
- Plugin redirects to Google's authorization page
- User approves → Google redirects to /auth/oauth/google/callback?code=xxx&state=xxx
- Plugin validates state token (CSRF protection)
- Plugin exchanges authorization code for access token
- Plugin fetches user profile from Google
- Plugin creates/links Aegis user account and session
- User is authenticated with session cookie
Supported Providers (via Goth):
- google: Google OAuth 2.0
- github: GitHub OAuth 2.0
- line: LINE OAuth 2.0 (Japan, Taiwan, Thailand)
- microsoft: Microsoft Azure AD / Office 365
- apple: Apple Sign In
- discord, slack, gitlab, bitbucket, twitter, linkedin, spotify, twitch, amazon
- generic: Custom OAuth 2.0 / OIDC providers (requires manual configuration)
Multi-Provider Setup: Configure multiple providers to offer users a choice:
cfg := &oauth.Config{
CallbackURL: "https://example.com/auth",
Providers: []oauth.ProviderConfig{
{ProviderID: "google", ProviderType: "google", ClientID: "...", ClientSecret: "..."},
{ProviderID: "github", ProviderType: "github", ClientID: "...", ClientSecret: "..."},
},
}
oauthPlugin := oauth.New(cfg, nil)
Security Features:
- CSRF Protection: State tokens with HMAC signing prevent cross-site request forgery
- Secure Cookies: HTTPOnly, Secure, SameSite settings via CookieManager
- State Expiration: OAuth states expire after 15 minutes
- Token Storage: Access/refresh tokens stored in database (not cookies)
Account Linking: Users can link multiple OAuth providers to a single Aegis account:
- Same email: Automatically linked on first sign-in
- Different emails: Manual linking via LinkAccount API
- Multiple providers: One user can have Google + GitHub + Apple linked
Database Schema:
- oauth_connections table: Stores provider links (user_id, provider, provider_user_id, tokens)
- Foreign key to auth.users: Ensures referential integrity
Index ¶
- Constants
- func CreateGothProvider(cfg ProviderConfig, callbackURL string) (goth.Provider, error)
- func GetMigrations(dialect plugins.Dialect) ([]plugins.Migration, error)
- func GetSchema(dialect plugins.Dialect) (*plugins.Schema, error)
- func UserToGothUser(oauthUser *User, provider string) goth.User
- type Config
- type Connection
- type DefaultOAuthStore
- func (s *DefaultOAuthStore) CreateConnection(ctx context.Context, conn Connection) (*Connection, error)
- func (s *DefaultOAuthStore) DeleteConnection(ctx context.Context, provider, userID string) error
- func (s *DefaultOAuthStore) GetConnectionByProviderUserID(ctx context.Context, provider, providerUserID string) (*Connection, error)
- func (s *DefaultOAuthStore) GetConnectionsByUserID(ctx context.Context, userID string) ([]Connection, error)
- func (s *DefaultOAuthStore) UpdateConnection(ctx context.Context, conn Connection) error
- type GothAdapter
- type Handlers
- type LinkAccountRequest
- type Plugin
- func (p *Plugin) BeginAuth(w http.ResponseWriter, r *http.Request, providerName string) error
- func (p *Plugin) CompleteAuth(ctx context.Context, w http.ResponseWriter, r *http.Request) (*User, *auth.Session, error)
- func (p *Plugin) Dependencies() []plugins.Dependency
- func (p *Plugin) Description() string
- func (p *Plugin) GetMigrations() []plugins.Migration
- func (p *Plugin) GetSchemas() []plugins.Schema
- func (p *Plugin) GetStateStore() *StateStore
- func (p *Plugin) GetUserConnections(ctx context.Context, userID string) ([]*Connection, error)
- func (p *Plugin) Init(_ context.Context, a plugins.Aegis) error
- func (p *Plugin) LinkAccount(ctx context.Context, userID string, oauthUser *User, provider string) error
- func (p *Plugin) MountRoutes(router router.Router, prefix string)
- func (p *Plugin) Name() string
- func (p *Plugin) ProvidesAuthMethods() []string
- func (p *Plugin) RequiresTables() []string
- func (p *Plugin) UnlinkAccount(ctx context.Context, userID, provider string) error
- func (p *Plugin) Version() string
- type ProviderConfig
- func Apple(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func Bitbucket(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func Discord(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func Generic(providerID, clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func GitHub(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func GitLab(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func Google(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func LINE(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func LinkedIn(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func Microsoft(clientID, clientSecret, tenantID string, opts ...ProviderOption) ProviderConfig
- func Slack(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func Spotify(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func Twitch(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- func Twitter(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
- type ProviderOption
- func WithAccessType(accessType string) ProviderOption
- func WithDisableImplicitSignUp() ProviderOption
- func WithDisableSignUp() ProviderOption
- func WithDiscoveryURL(url string) ProviderOption
- func WithOverrideUserInfo() ProviderOption
- func WithPKCE() ProviderOption
- func WithProfileMapper(fn func(map[string]interface{}) (*User, error)) ProviderOption
- func WithPrompt(prompt string) ProviderOption
- func WithProviderID(id string) ProviderOption
- func WithRedirectURI(uri string) ProviderOption
- func WithScopes(scopes ...string) ProviderOption
- func WithUserInfoFetcher(fn func(*Tokens) (*User, error)) ProviderOption
- type StateData
- type StateStore
- func (s *StateStore) ClearState(w http.ResponseWriter)
- func (s *StateStore) GenerateState() (string, error)
- func (s *StateStore) GetMaxAge() time.Duration
- func (s *StateStore) GetState(r *http.Request) (*StateData, error)
- func (s *StateStore) SetMaxAge(age time.Duration)
- func (s *StateStore) StoreState(w http.ResponseWriter, data *StateData) error
- func (s *StateStore) ValidateState(r *http.Request, callbackState string) (*StateData, error)
- type StateStoreConfig
- type Store
- type Tokens
- type User
Constants ¶
const ( // SchemaLinkAccountRequest is the OpenAPI schema name for account linking. // Request Body: // { // "provider": "google" // } SchemaLinkAccountRequest = "LinkAccountRequest" )
Schema names for OpenAPI specification generation.
These constants define the OpenAPI schema names for OAuth request types. They are used in route metadata to generate accurate API documentation.
const SecretPurposeOAuthState = "aegis:oauth-state"
SecretPurposeOAuthState is the purpose string for deriving OAuth state secrets. Aegis's secret derivation system uses this to generate a unique secret for signing OAuth state cookies, // SecretPurposeOAuthState is the purpose for OAuth state tokens #nosec G101
const StateCookieName = "_aegis_oauth_state"
StateCookieName is the cookie name used for OAuth state storage. This cookie is separate from the session cookie and is only used during the OAuth flow (created on BeginAuth, validated on callback, then deleted).
Variables ¶
This section is empty.
Functions ¶
func CreateGothProvider ¶
func CreateGothProvider(cfg ProviderConfig, callbackURL string) (goth.Provider, error)
CreateGothProvider creates a goth.Provider from ProviderConfig.
This factory function instantiates the correct Goth provider based on the ProviderType, applying scopes and callback URL. It handles provider-specific initialization quirks (e.g., Apple's JWT client secret).
Supported Providers:
- google, github, line, microsoft, apple
- discord, slack, gitlab, bitbucket, twitter
- linkedin, spotify, twitch, amazon
- generic (manual endpoint configuration)
Parameters:
- cfg: Provider configuration with type, credentials, and options
- callbackURL: OAuth callback URL for this provider
Returns:
- goth.Provider: Initialized Goth provider
- error: Unsupported provider type or missing configuration
Example:
cfg := oauth.ProviderConfig{
ProviderType: "google",
ClientID: "...",
ClientSecret: "...",
Scopes: []string{"email", "profile"},
}
provider, _ := oauth.CreateGothProvider(cfg, "https://example.com/auth/oauth/google/callback")
func GetMigrations ¶
GetMigrations returns all database migrations for the specified SQL dialect.
This function combines the initial schema (version 001) with any additional migrations (version 002+) to produce a complete, ordered list of migrations.
Parameters:
- dialect: SQL dialect (DialectPostgres, DialectMySQL, DialectSQLite)
Returns:
- []plugins.Migration: Ordered list of migrations (version ASC)
- error: File read error or unsupported dialect
func GetSchema ¶
GetSchema returns the initial database schema for the OAuth plugin.
This function provides the CREATE TABLE statement for oauth_connections, formatted for the specified SQL dialect. For PostgreSQL, it includes both the auth schema prerequisite and the OAuth schema.
Parameters:
- dialect: SQL dialect (DialectPostgres or DialectMySQL)
Returns:
- *plugins.Schema: Schema metadata with SQL and version info
- error: Unsupported dialect error
func UserToGothUser ¶
UserToGothUser converts Aegis User to goth.User.
This helper function transforms Aegis's User struct back into Goth's representation, useful for interacting with Goth's provider APIs.
Parameters:
- oauthUser: Aegis OAuth user
- provider: Provider name ("google", "github", etc.)
Returns:
- goth.User: Goth user model
Types ¶
type Config ¶
type Config struct {
// Providers configures which OAuth providers to enable.
// Each provider needs a client ID, client secret from the provider's developer console.
Providers []ProviderConfig
// CallbackURL is the base URL for OAuth callbacks (e.g., "https://example.com/auth").
// The plugin appends "/oauth/:provider/callback" to this base.
// Example: CallbackURL="https://example.com/auth" → callback at "https://example.com/auth/oauth/google/callback"
CallbackURL string
// StateSecret is deprecated - Aegis now derives state secrets from master secret.
// This field is kept for backward compatibility but is ignored.
StateSecret []byte
}
Config holds OAuth plugin configuration.
This structure defines all OAuth providers to enable and their settings. Multiple providers can be configured to offer users authentication choices.
Example:
cfg := &oauth.Config{
CallbackURL: "https://example.com/auth",
Providers: []oauth.ProviderConfig{
{
ProviderID: "google",
ProviderType: "google",
ClientID: os.Getenv("GOOGLE_CLIENT_ID"),
ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
Scopes: []string{"email", "profile"},
},
},
}
type Connection ¶
type Connection struct {
// Aegis fields
ID string // Unique connection ID (generated by Aegis)
UserID string // Aegis user ID (foreign key to auth.users)
CreatedAt time.Time // When the connection was first created
UpdatedAt time.Time // Last updated (token refresh, profile update)
// OAuth provider fields
Provider string // Provider name ("google", "github", etc.)
ProviderUserID string // Provider's user ID (Google: "108...", GitHub: "12345")
// User profile from provider
Email string // User's email from provider
Name string // User's name from provider
AvatarURL string // User's avatar URL from provider
// OAuth tokens
AccessToken string // OAuth access token for API calls
RefreshToken string // OAuth refresh token for renewing access
ExpiresAt time.Time // When the access token expires
// Provider-specific data (JSON)
ProviderData map[string]interface{} // Additional fields from provider
}
Connection represents an OAuth connection stored in the database.
This is the persistent storage model linking an Aegis user to an OAuth provider. Each connection stores the provider's user ID, tokens, and metadata for account linking and token refresh.
Database Table: oauth_connections Primary Key: (provider, provider_user_id) Foreign Key: user_id → auth.users.id
Example Row:
- ID: "conn_abc123"
- UserID: "user_xyz789"
- Provider: "google"
- ProviderUserID: "108234567890123456789" (Google's user ID)
- Email: "user@gmail.com"
- Name: "John Doe"
- AccessToken: "ya29.a0AfH6SMB..." (Google access token)
- RefreshToken: "1//0gZ..." (Google refresh token)
- ExpiresAt: 2024-01-01T13:00:00Z
type DefaultOAuthStore ¶
type DefaultOAuthStore struct {
// contains filtered or unexported fields
}
DefaultOAuthStore implements Store using a SQL database backend.
This implementation uses sqlc-generated type-safe queries to manage OAuth connections in PostgreSQL, MySQL, or SQLite. It stores connections in the oauth_connections table with foreign key constraints to auth.users.
Database Table: oauth_connections Columns:
- id (TEXT PRIMARY KEY): Connection identifier
- user_id (TEXT NOT NULL): Foreign key to auth.users.id
- provider (TEXT NOT NULL): Provider name ("google", "github", etc.)
- provider_user_id (TEXT NOT NULL): Provider's user ID
- email, name, avatar_url: User profile from provider
- access_token, refresh_token: OAuth tokens
- expires_at: Token expiration timestamp
- provider_data (TEXT): JSON-encoded provider-specific fields
- created_at, updated_at: Timestamps
Unique Constraint: (provider, provider_user_id) Foreign Key: user_id → auth.users.id ON DELETE CASCADE
func NewDefaultOAuthStore ¶
func NewDefaultOAuthStore(db *sql.DB) *DefaultOAuthStore
NewDefaultOAuthStore creates a new DefaultOAuthStore backed by a SQL database.
The provided database connection must be configured for the correct dialect and have the oauth_connections table schema applied.
Parameters:
- db: Active SQL database connection
Returns:
- *DefaultOAuthStore: Configured store ready for use
func (*DefaultOAuthStore) CreateConnection ¶
func (s *DefaultOAuthStore) CreateConnection(ctx context.Context, conn Connection) (*Connection, error)
CreateConnection creates a new OAuth connection
func (*DefaultOAuthStore) DeleteConnection ¶
func (s *DefaultOAuthStore) DeleteConnection(ctx context.Context, provider, userID string) error
DeleteConnection deletes an OAuth connection by provider and user ID
func (*DefaultOAuthStore) GetConnectionByProviderUserID ¶
func (s *DefaultOAuthStore) GetConnectionByProviderUserID(ctx context.Context, provider, providerUserID string) (*Connection, error)
GetConnectionByProviderUserID retrieves a connection by provider and provider user ID
func (*DefaultOAuthStore) GetConnectionsByUserID ¶
func (s *DefaultOAuthStore) GetConnectionsByUserID(ctx context.Context, userID string) ([]Connection, error)
GetConnectionsByUserID retrieves all connections for a user
func (*DefaultOAuthStore) UpdateConnection ¶
func (s *DefaultOAuthStore) UpdateConnection(ctx context.Context, conn Connection) error
UpdateConnection updates an OAuth connection
type GothAdapter ¶
type GothAdapter struct {
// contains filtered or unexported fields
}
GothAdapter adapts goth.Provider to Aegis's Provider interface.
This adapter makes Goth the default/recommended OAuth provider implementation while keeping it technically optional through abstraction. If you want to use a different OAuth library, you can implement the Provider interface without Goth.
Goth Benefits:
- 50+ pre-configured OAuth providers (Google, GitHub, Apple, etc.)
- Battle-tested OAuth 2.0 / OIDC implementation
- Active maintenance and security updates
- Provider-specific quirks handled (Apple's JWT client secret, etc.)
Abstraction Benefits:
- Aegis core doesn't depend on Goth directly
- Easier testing with mock providers
- Future flexibility if Goth is discontinued
func NewGothAdapter ¶
func NewGothAdapter(provider goth.Provider) *GothAdapter
NewGothAdapter creates a new Goth adapter wrapping a Goth provider.
This function wraps any Goth provider to work with Aegis's OAuth plugin. Most users won't call this directly - the plugin creates adapters automatically from ProviderConfig using CreateGothProvider.
Parameters:
- provider: Goth provider instance (google.New, github.New, etc.)
Returns:
- *GothAdapter: Adapter implementing Provider interface
Example:
googleProvider := google.New(clientID, clientSecret, callbackURL) adapter := oauth.NewGothAdapter(googleProvider)
func (*GothAdapter) Exchange ¶
func (g *GothAdapter) Exchange(_ string) (*User, error)
Exchange exchanges authorization code for user information.
Note: This is a simplified interface. In practice, Aegis uses the full gothic.CompleteUserAuth flow which handles the complete OAuth callback processing (code exchange, token retrieval, user info fetch).
This method is currently not used - instead, the plugin uses Goth's session-based flow directly for more flexibility.
func (*GothAdapter) GetAuthURL ¶
func (g *GothAdapter) GetAuthURL(state string) (string, error)
GetAuthURL returns the provider's authorization URL with CSRF state.
This method starts the OAuth session and returns the URL to redirect the user to for authorization (e.g., https://accounts.google.com/o/oauth2/auth).
Parameters:
- state: CSRF state token (random string for security)
Returns:
- string: Authorization URL to redirect user to
- error: OAuth session creation error
func (*GothAdapter) Name ¶
func (g *GothAdapter) Name() string
Name returns the provider identifier (e.g., "google", "github").
type Handlers ¶
type Handlers struct {
// contains filtered or unexported fields
}
Handlers provides HTTP endpoint handlers for OAuth authentication.
All handlers have been made private (lowercase) to encourage programmatic use of the underlying Plugin methods. This struct serves as a mounting point for the router.
func NewHandlers ¶
NewHandlers creates OAuth plugin HTTP handlers.
type LinkAccountRequest ¶
type LinkAccountRequest struct {
Provider string `json:"provider"`
}
LinkAccountRequest represents a request to link an OAuth account.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin provides OAuth 2.0 authentication integration for Aegis.
This plugin manages multiple OAuth providers simultaneously, allowing users to authenticate with Google, GitHub, Apple, and other services. It integrates with Aegis's user and session management to create unified accounts.
Components:
- providerConfigs: Plugin configuration for each provider (client ID, secret, scopes)
- gothProviders: Goth provider instances for OAuth protocol handling
- stateStore: CSRF protection via signed cookies (state parameter)
- store: Database persistence for OAuth connections
- sessionService: Creates Aegis sessions after successful OAuth
Thread Safety: Plugin is safe for concurrent use after initialization (Init called).
func New ¶
New creates a new OAuth plugin with configured providers.
This function initializes the plugin with provider configurations and creates Goth provider instances for each configured provider. Providers that fail to initialize are logged but don't prevent other providers from working.
Provider Configuration: Each provider needs:
- ProviderID: Unique identifier (used in URLs like /oauth/google)
- ProviderType: Provider implementation type ("google", "github", etc.)
- ClientID: OAuth client ID from provider's developer console
- ClientSecret: OAuth client secret from provider's developer console
Parameters:
- cfg: Plugin configuration with providers and callback URL
- store: OAuth connection storage (nil = use DefaultOAuthStore)
- dialect: Database dialect (optional, defaults to PostgreSQL)
Returns:
- *Plugin: Initialized plugin ready for Init() call
Example:
cfg := &oauth.Config{
CallbackURL: "https://example.com/auth",
Providers: []oauth.ProviderConfig{
{ProviderID: "google", ProviderType: "google", ClientID: "...", ClientSecret: "..."},
{ProviderID: "github", ProviderType: "github", ClientID: "...", ClientSecret: "..."},
},
}
plugin := oauth.New(cfg, nil, plugins.DialectPostgres)
func (*Plugin) BeginAuth ¶
BeginAuth starts the OAuth authentication flow with CSRF protection.
This method initiates the OAuth flow by:
- Generating a cryptographically secure CSRF state token
- Starting the OAuth session with the provider
- Obtaining the provider's authorization URL
- Storing state and session data in a signed cookie
- Redirecting the user to the provider's authorization page
OAuth Flow (Step 1-3):
- User → GET /auth/oauth/google
- Plugin → Generate state="abc123", store in cookie
- Plugin → Redirect to https://accounts.google.com/authorize?state=abc123&...
State Cookie: The state cookie contains:
- CSRF state token (random 32 bytes, base64-encoded)
- Provider name ("google", "github", etc.)
- Marshaled OAuth session data (for completing the flow)
- HMAC signature (prevents tampering)
- Expiration: 15 minutes (short-lived for security)
Parameters:
- w: HTTP response writer for setting cookies and redirecting
- r: HTTP request (currently unused)
- providerName: Provider identifier ("google", "github", etc.)
Returns:
- error: Provider not found, state generation failed, or redirect failed
Security:
- State token is cryptographically random (32 bytes from crypto/rand)
- State cookie is HMAC-signed to prevent tampering
- Cookie uses Secure, HTTPOnly, SameSite settings from SessionConfig
func (*Plugin) CompleteAuth ¶
func (p *Plugin) CompleteAuth(ctx context.Context, w http.ResponseWriter, r *http.Request) (*User, *auth.Session, error)
CompleteAuth completes the OAuth authentication flow after provider callback.
This method handles the OAuth callback by:
- Validating the CSRF state token from the callback
- Exchanging the authorization code for an access token
- Fetching the user's profile from the provider
- Creating or linking an Aegis user account
- Creating an Aegis session for the authenticated user
OAuth Flow (Step 4-9):
- Provider → Redirect to /auth/oauth/google/callback?code=xyz&state=abc123
- Plugin → Validate state=abc123 matches cookie
- Plugin → Exchange code=xyz for access token
- Plugin → Fetch user profile from provider
- Plugin → Create/link user account in Aegis
- Plugin → Create session and set cookie
User Account Matching:
- Existing OAuth connection: Retrieve linked user
- New OAuth connection with known email: Link to existing user
- New OAuth connection with unknown email: Create new user
- OAuth without email: Create user without email (uses provider name)
Parameters:
- ctx: Request context for database operations
- w: HTTP response writer for clearing state cookie
- r: HTTP request with OAuth callback parameters (code, state)
Returns:
- *User: Authenticated user with OAuth data
- *auth.Session: New Aegis session for the user
- error: State validation, token exchange, or user creation error
Security:
- State validation prevents CSRF attacks
- State cookie is cleared after validation (one-time use)
- Access tokens stored in database (not cookies)
func (*Plugin) Dependencies ¶
func (p *Plugin) Dependencies() []plugins.Dependency
Dependencies returns external package dependencies
func (*Plugin) Description ¶
Description returns a human-readable description
func (*Plugin) GetMigrations ¶
GetMigrations returns the plugin migrations
func (*Plugin) GetSchemas ¶
GetSchemas returns all schemas for all supported dialects
func (*Plugin) GetStateStore ¶
func (p *Plugin) GetStateStore() *StateStore
GetStateStore returns the OAuth state store
func (*Plugin) GetUserConnections ¶
GetUserConnections retrieves all OAuth provider connections for a user.
This method returns all linked OAuth providers, including access tokens, refresh tokens, and provider-specific data. Useful for displaying linked accounts in user settings or managing provider connections.
Parameters:
- ctx: Request context
- userID: Aegis user ID
Returns:
- []*Connection: List of OAuth connections (may be empty)
- error: Database query error
Example:
connections, _ := plugin.GetUserConnections(ctx, user.ID)
for _, conn := range connections {
fmt.Printf("Linked: %s (%s)\n", conn.Provider, conn.Email)
}
func (*Plugin) Init ¶
Init initializes the OAuth plugin with Aegis services.
This method is called during Aegis startup to inject dependencies and set up the state store. It retrieves services from the Aegis interface and derives the OAuth state secret from the master secret.
Initialization Steps:
- Get user, session, and account services from Aegis
- Initialize OAuth store if not provided
- Derive state secret from master secret ("aegis:oauth-state" purpose)
- Create StateStore with derived secret for CSRF protection
Parameters:
- ctx: Initialization context (currently unused)
- a: Aegis interface providing services and configuration
Returns:
- error: Initialization error (currently always nil)
func (*Plugin) LinkAccount ¶
func (p *Plugin) LinkAccount(ctx context.Context, userID string, oauthUser *User, provider string) error
LinkAccount links an OAuth provider to an existing authenticated user account.
This method allows users to add additional OAuth providers to their account. For example, a user who signed up with email/password can later link their Google account for easier login.
Use Cases:
- Link Google to existing email/password account
- Link multiple providers to one account (Google + GitHub + Apple)
- Re-link provider after unlinking
Parameters:
- ctx: Request context
- userID: Aegis user ID to link provider to
- oauthUser: OAuth user data from provider
- provider: Provider name ("google", "github", etc.)
Returns:
- error: Database error or duplicate connection error
Example:
// User already authenticated via session user, _ := core.GetUser(r.Context()) err := plugin.LinkAccount(ctx, user.ID, oauthUser, "google")
func (*Plugin) MountRoutes ¶
MountRoutes registers HTTP routes for the OAuth plugin
func (*Plugin) ProvidesAuthMethods ¶
ProvidesAuthMethods returns authentication methods provided
func (*Plugin) RequiresTables ¶
RequiresTables returns core tables this plugin depends on
func (*Plugin) UnlinkAccount ¶
UnlinkAccount removes an OAuth provider link from a user account.
This method allows users to disconnect OAuth providers from their account. The user's Aegis account remains active, but they can no longer sign in using the unlinked provider.
Safety: This method does NOT check if the user has other authentication methods. You should verify the user has email/password or other OAuth providers before allowing unlinking to prevent account lockout.
Parameters:
- ctx: Request context
- userID: Aegis user ID
- provider: Provider name to unlink ("google", "github", etc.)
Returns:
- error: Database error or connection not found
Example:
// Unlink Google from user's account err := plugin.UnlinkAccount(ctx, user.ID, "google")
type ProviderConfig ¶
type ProviderConfig struct {
// Identity
ProviderID string // Unique ID (e.g., "google", "github", "line-jp")
ProviderType string // Provider type (e.g., "google", "github", "generic")
// OAuth Credentials
ClientID string // OAuth client ID from provider's developer console
ClientSecret string // OAuth client secret from provider's developer console
// OAuth Endpoints (for generic providers)
AuthURL string // Authorization endpoint
TokenURL string // Token endpoint
UserInfoURL string // User info endpoint
// Discovery (alternative to manual endpoints)
DiscoveryURL string // OIDC discovery URL
// OAuth Options
Scopes []string // OAuth scopes (e.g., ["email", "profile"])
RedirectURI string // Custom redirect URI (optional)
// Advanced Options
PKCE bool // Enable PKCE (recommended for mobile apps)
ResponseType string // OAuth response type (default: "code")
ResponseMode string // Response mode (query/form_post)
Prompt string // Auth prompt (login/consent/etc)
AccessType string // Access type (offline for refresh tokens)
// User Sign Up Control
DisableImplicitSignUp bool // Require explicit sign-up
DisableSignUp bool // Disable sign-up entirely
OverrideUserInfo bool // Update user info on each sign-in
// Custom Functions (optional)
GetUserInfo func(*Tokens) (*User, error) // Custom user info fetcher
MapProfile func(map[string]interface{}) (*User, error) // Profile mapper
}
ProviderConfig defines configuration for a single OAuth provider.
This struct configures how Aegis integrates with an OAuth 2.0 or OpenID Connect provider. It supports both pre-built providers (Google, GitHub) and generic OAuth 2.0 providers with manual endpoint configuration.
Pre-Built Providers: For well-known providers, only set ProviderType, ClientID, and ClientSecret:
{ProviderID: "google", ProviderType: "google", ClientID: "...", ClientSecret: "..."}
Generic Providers: For custom OAuth providers, set endpoints manually or use DiscoveryURL:
{ProviderID: "custom", ProviderType: "generic", ClientID: "...", ClientSecret: "...",
AuthURL: "https://provider.com/oauth/authorize",
TokenURL: "https://provider.com/oauth/token",
UserInfoURL: "https://provider.com/oauth/userinfo"}
func Apple ¶
func Apple(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
Apple creates an Apple Sign In provider configuration.
Apple requires special setup:
- Client Secret: Not a simple string, but a JWT signed with your private key
- Team ID: Your Apple Developer Team ID
Default Scopes: ["name", "email"] Discovery: https://appleid.apple.com/.well-known/openid-configuration
Note: Apple's "client secret" is actually a JWT that you must generate and sign with your private key. See Apple's documentation for details.
Parameters:
- clientID: Apple Service ID
- clientSecret: JWT signed with your private key
- teamID: Apple Developer Team ID (currently unused)
- opts: Optional customization
Returns:
- ProviderConfig: Apple provider configuration
func Bitbucket ¶
func Bitbucket(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
Bitbucket creates a Bitbucket OAuth provider configuration.
Default Scopes: ["account", "email"]
func Discord ¶
func Discord(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
Discord creates a Discord OAuth provider configuration.
Default Scopes: ["identify", "email"]
Parameters:
- clientID: Discord Application client ID
- clientSecret: Discord Application client secret
- opts: Optional customization
func Generic ¶
func Generic(providerID, clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
Generic creates a custom OAuth provider configuration.
Use this for OAuth providers not included in the pre-configured helpers. You must provide either:
- Discovery URL (OIDC providers): Automatic endpoint discovery
- Manual endpoints: AuthURL, TokenURL, UserInfoURL
Parameters:
- providerID: Unique provider identifier (used in URLs)
- clientID: OAuth client ID from provider
- clientSecret: OAuth client secret from provider
- opts: Required configuration (scopes, endpoints, etc.)
Returns:
- ProviderConfig: Generic provider configuration
Example (OIDC Discovery):
custom := oauth.Generic("keycloak", clientID, clientSecret,
oauth.WithDiscoveryURL("https://auth.example.com/realms/master/.well-known/openid-configuration"),
oauth.WithScopes("openid", "email", "profile"),
)
Example (Manual Endpoints):
custom := oauth.Generic("custom", clientID, clientSecret,
oauth.WithScopes("email"),
)
custom.AuthURL = "https://provider.com/oauth/authorize"
custom.TokenURL = "https://provider.com/oauth/token"
custom.UserInfoURL = "https://provider.com/oauth/userinfo"
func GitHub ¶
func GitHub(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
GitHub creates a GitHub OAuth provider configuration.
Default Scopes: ["user:email"] No discovery URL (GitHub uses fixed endpoints)
Parameters:
- clientID: GitHub OAuth App client ID
- clientSecret: GitHub OAuth App client secret
- opts: Optional customization
Returns:
- ProviderConfig: GitHub provider configuration
func GitLab ¶
func GitLab(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
GitLab creates a GitLab OAuth provider configuration.
Default Scopes: ["read_user", "openid", "profile", "email"]
Supports both GitLab.com and self-hosted instances.
func Google ¶
func Google(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
Google creates a Google OAuth provider configuration.
Default Scopes: ["openid", "email", "profile"] Discovery: https://accounts.google.com/.well-known/openid-configuration
Parameters:
- clientID: Google OAuth client ID (from Google Cloud Console)
- clientSecret: Google OAuth client secret
- opts: Optional customization (scopes, prompt, etc.)
Returns:
- ProviderConfig: Google provider configuration
Example:
googleCfg := oauth.Google("123456.apps.googleusercontent.com", "secret",
oauth.WithScopes("email", "profile", "calendar.readonly"),
oauth.WithAccessType("offline"), // Request refresh token
oauth.WithPrompt("consent"), // Force consent to get refresh token
)
func LINE ¶
func LINE(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
LINE creates a LINE OAuth provider configuration.
LINE supports multiple channels for different countries:
- Japan: Regular LINE Login
- Thailand, Taiwan, Indonesia: Country-specific configurations
Default Scopes: ["profile", "openid", "email"] Discovery: https://access.line.me/.well-known/openid-configuration
For multiple LINE channels:
lineJP := oauth.LINE(clientID_JP, clientSecret_JP,
oauth.WithProviderID("line-jp"),
)
lineTW := oauth.LINE(clientID_TW, clientSecret_TW,
oauth.WithProviderID("line-tw"),
)
Parameters:
- clientID: LINE Channel ID
- clientSecret: LINE Channel Secret
- opts: Optional customization
Returns:
- ProviderConfig: LINE provider configuration
func LinkedIn ¶
func LinkedIn(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
LinkedIn creates a LinkedIn OAuth provider configuration.
Default Scopes: ["r_liteprofile", "r_emailaddress"]
Note: LinkedIn's API and scopes change frequently. Verify current scopes in LinkedIn's docs.
func Microsoft ¶
func Microsoft(clientID, clientSecret, tenantID string, opts ...ProviderOption) ProviderConfig
Microsoft creates a Microsoft/Azure AD OAuth provider configuration.
Default Scopes: ["openid", "email", "profile"] Discovery: Uses tenant-specific discovery URL
Parameters:
- clientID: Azure AD App client ID
- clientSecret: Azure AD App client secret
- tenantID: Azure AD tenant ID (or "common" for multi-tenant)
- opts: Optional customization
Returns:
- ProviderConfig: Microsoft provider configuration
func Slack ¶
func Slack(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
Slack creates a Slack OAuth provider configuration.
Default Scopes: ["openid", "profile", "email"] Discovery: https://slack.com/.well-known/openid-configuration
func Spotify ¶
func Spotify(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
Spotify creates a Spotify OAuth provider configuration.
Default Scopes: ["user-read-email", "user-read-private"]
func Twitch ¶
func Twitch(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
Twitch creates a Twitch OAuth provider configuration.
Default Scopes: ["user:read:email"] Discovery: https://id.twitch.tv/oauth2/.well-known/openid-configuration
func Twitter ¶
func Twitter(clientID, clientSecret string, opts ...ProviderOption) ProviderConfig
Twitter (X) creates a Twitter/X OAuth provider configuration.
Default Scopes: ["tweet.read", "users.read"]
Note: Twitter's OAuth implementation has changed over time. This uses OAuth 2.0.
type ProviderOption ¶
type ProviderOption func(*ProviderConfig)
ProviderOption is a functional option for customizing provider configuration.
This pattern allows flexible provider configuration with sensible defaults. Options can be chained to customize scopes, PKCE, sign-up behavior, etc.
Example:
cfg := oauth.Google(clientID, clientSecret,
oauth.WithScopes("email", "profile", "calendar"),
oauth.WithPKCE(),
oauth.WithDisableImplicitSignUp(),
)
func WithAccessType ¶
func WithAccessType(accessType string) ProviderOption
WithAccessType sets the access type (e.g., "offline" for refresh tokens).
Setting access_type="offline" requests a refresh token from the provider, allowing token renewal without re-authentication. This is provider-specific:
- Google: access_type=offline
- Microsoft: access_type=offline (or prompt=consent)
Example:
oauth.Google(clientID, clientSecret,
oauth.WithAccessType("offline"), // Request refresh token
oauth.WithPrompt("consent"), // Force consent to get refresh token
)
func WithDisableImplicitSignUp ¶
func WithDisableImplicitSignUp() ProviderOption
WithDisableImplicitSignUp disables automatic sign-up for new OAuth users.
When enabled, users must be explicitly invited or pre-created before they can sign in via OAuth. Useful for enterprise applications with controlled user provisioning.
Behavior:
- Existing user + OAuth: Link OAuth to existing account (allowed)
- New OAuth user: Return error "Sign-up not allowed" (blocked)
Example:
oauth.Google(clientID, clientSecret,
oauth.WithDisableImplicitSignUp(), // Require pre-existing accounts
)
func WithDisableSignUp ¶
func WithDisableSignUp() ProviderOption
WithDisableSignUp disables sign-up entirely (only existing users can sign in).
This is stricter than WithDisableImplicitSignUp - it prevents both new user creation and OAuth linking to existing accounts.
Behavior:
- Existing user with OAuth already linked: Sign-in allowed
- Existing user without OAuth: Linking blocked
- New user: Sign-up blocked
Use Case: Read-only OAuth for authentication of pre-provisioned users only.
func WithDiscoveryURL ¶
func WithDiscoveryURL(url string) ProviderOption
WithDiscoveryURL sets the OIDC discovery URL for automatic endpoint configuration.
For OpenID Connect providers, the discovery URL provides all OAuth endpoints (authorization, token, userinfo, JWKS) automatically via a JSON document.
Example:
oauth.Generic("custom", clientID, clientSecret,
oauth.WithDiscoveryURL("https://auth.example.com/.well-known/openid-configuration"),
)
func WithOverrideUserInfo ¶
func WithOverrideUserInfo() ProviderOption
WithOverrideUserInfo enables updating user info on each sign-in.
By default, user profile data (name, email, avatar) is only saved on first sign-up. Enabling this option updates the user profile every time they sign in via OAuth, keeping data synchronized with the provider.
Use Cases:
- Keep user names/avatars up-to-date from provider
- Sync email changes from provider
- Corporate directory synchronization
Caution:
- May overwrite user-edited profile data
- Consider allowing users to opt out of sync
func WithPKCE ¶
func WithPKCE() ProviderOption
WithPKCE enables PKCE (Proof Key for Code Exchange) for enhanced security.
PKCE protects against authorization code interception attacks, especially important for mobile apps and public clients that can't securely store secrets.
Recommended for:
- Mobile apps (iOS, Android)
- Single-page applications (SPAs)
- Any public OAuth client
func WithProfileMapper ¶
func WithProfileMapper(fn func(map[string]interface{}) (*User, error)) ProviderOption
WithProfileMapper sets a custom profile mapper function.
Use this to transform provider-specific profile data into Aegis User format. Useful for extracting custom fields or handling non-standard profile structures.
Example:
mapProfile := func(profile map[string]interface{}) (*oauth.User, error) {
return &oauth.User{
User: auth.User{
ID: profile["sub"].(string),
Email: profile["email"].(string),
Name: profile["display_name"].(string),
Avatar: profile["picture_url"].(string),
},
ProviderData: profile,
}, nil
}
oauth.Generic("custom", clientID, clientSecret,
oauth.WithProfileMapper(mapProfile),
)
func WithPrompt ¶
func WithPrompt(prompt string) ProviderOption
WithPrompt sets the OAuth prompt parameter.
The prompt parameter controls how the provider asks for user consent:
- "none": No UI shown (silent auth, may fail if interaction required)
- "login": Always show login screen (even if logged in)
- "consent": Always show consent screen
- "select_account": Show account picker
Example:
oauth.Google(clientID, clientSecret,
oauth.WithPrompt("select_account"), // Always show account picker
)
func WithProviderID ¶
func WithProviderID(id string) ProviderOption
WithProviderID sets a custom provider ID.
Useful for having multiple instances of the same provider with different configurations (e.g., LINE for Japan vs Taiwan, Google for different tenants).
Example:
// LINE for Japan
lineJP := oauth.LINE(clientID_JP, clientSecret_JP,
oauth.WithProviderID("line-jp"),
)
// LINE for Taiwan
lineTW := oauth.LINE(clientID_TW, clientSecret_TW,
oauth.WithProviderID("line-tw"),
)
func WithRedirectURI ¶
func WithRedirectURI(uri string) ProviderOption
WithRedirectURI sets a custom redirect URI for the provider.
By default, the plugin constructs redirect URIs as:
{CallbackURL}/oauth/{provider}/callback
Use this to override with a provider-specific redirect URI, for example if you've registered a different callback URL in the provider's console.
Example:
oauth.Google(clientID, clientSecret,
oauth.WithRedirectURI("https://example.com/custom/google/callback"),
)
func WithScopes ¶
func WithScopes(scopes ...string) ProviderOption
WithScopes sets custom OAuth scopes for the provider.
Scopes determine what user data the provider will share. Each provider has different scope names and defaults.
Example:
oauth.Google(clientID, clientSecret,
oauth.WithScopes("email", "profile", "calendar.readonly"),
)
func WithUserInfoFetcher ¶
func WithUserInfoFetcher(fn func(*Tokens) (*User, error)) ProviderOption
WithUserInfoFetcher sets a custom user info fetcher function.
Use this to customize how user data is retrieved from the provider, for example to call additional API endpoints or parse non-standard responses.
Example:
fetchUser := func(tokens *oauth.Tokens) (*oauth.User, error) {
// Call custom API with access token
resp, _ := http.Get("https://api.example.com/user?access_token=" + tokens.AccessToken)
// Parse custom response format
var data map[string]interface{}
json.NewDecoder(resp.Body).Decode(&data)
return &oauth.User{...}, nil
}
oauth.Generic("custom", clientID, clientSecret,
oauth.WithUserInfoFetcher(fetchUser),
)
type StateData ¶
type StateData struct {
State string // CSRF state token (32 random bytes, base64)
Provider string // OAuth provider name ("google", "github", etc.)
SessionData string // Marshaled goth.Session (provider-specific OAuth state)
}
StateData holds the OAuth state data stored during the OAuth flow.
This data is serialized, compressed, signed, and stored in a cookie during BeginAuth, then retrieved and validated during the callback.
Cookie Format (with signature):
<hmac-signature>.<base64(state|provider|base64(gzip(sessionData)))>
Example:
"a8f3d..." (HMAC) + "." + "YWJjMTIzfGdvb2dsZXxINHNJQUFBLi4u" (payload)
type StateStore ¶
type StateStore struct {
// contains filtered or unexported fields
}
StateStore manages OAuth state cookies for CSRF protection during OAuth flows.
OAuth CSRF Attack Without State:
- Attacker initiates OAuth flow → gets callback URL with auth code
- Attacker tricks victim into visiting callback URL
- Victim's browser exchanges code → victim's account linked to attacker's provider
- Attacker can now access victim's account via OAuth
CSRF Protection With State:
- Plugin generates random state token before redirect
- State stored in signed cookie (attacker can't forge)
- Provider includes state in callback URL
- Plugin validates callback state matches cookie
- If mismatch → reject (CSRF attempt)
State Cookie Contents:
- CSRF state token (32 random bytes, base64-encoded)
- Provider name ("google", "github", etc.)
- Marshaled OAuth session (for resuming flow)
- HMAC signature (prevents tampering)
Security Features:
- HMAC-SHA256 signing with derived secret (prevents cookie tampering)
- Gzip compression (session data can be large)
- Short expiration (15 minutes default)
- HTTPOnly, Secure, SameSite settings from CookieManager
Integration: This store integrates with Aegis's core CookieManager for consistent cookie settings (domain, secure flag, SameSite policy) across all cookies.
func NewStateStore ¶
func NewStateStore(cfg *StateStoreConfig) *StateStore
NewStateStore creates a new StateStore for managing OAuth state with CSRF protection.
The secret should be at least 32 bytes for cryptographic security. Aegis derives this from the master secret using the purpose "aegis:oauth-state".
Parameters:
- cfg: Configuration with session settings, secret, and max age
Returns:
- *StateStore: Initialized store ready for use
Example:
secret := aegis.DeriveSecret("aegis:oauth-state")
store := oauth.NewStateStore(&oauth.StateStoreConfig{
SessionConfig: aegis.GetSessionConfig(),
Secret: secret,
MaxAge: 15 * 60, // 15 minutes
})
func (*StateStore) ClearState ¶
func (s *StateStore) ClearState(w http.ResponseWriter)
ClearState clears the OAuth state cookie.
This method is called after successful callback validation to delete the one-time-use state cookie. It sets MaxAge=-1 to instruct the browser to delete the cookie immediately.
Parameters:
- w: HTTP response writer for clearing cookie
func (*StateStore) GenerateState ¶
func (s *StateStore) GenerateState() (string, error)
GenerateState generates a cryptographically secure random state string.
The state is a 32-byte random value base64-encoded for use as a CSRF token. This is the value included in the OAuth authorization URL and validated in the callback.
Returns:
- string: Base64-encoded random state (44 characters)
- error: Crypto random generation error (extremely rare)
func (*StateStore) GetMaxAge ¶
func (s *StateStore) GetMaxAge() time.Duration
GetMaxAge returns the max age for state cookies
func (*StateStore) GetState ¶
func (s *StateStore) GetState(r *http.Request) (*StateData, error)
GetState retrieves and validates the OAuth state data from the cookie.
This method is called during the OAuth callback to retrieve the stored state data for validation. It verifies the HMAC signature to ensure the cookie wasn't tampered with.
Processing:
- Read cookie value
- Base64-decode payload
- Verify HMAC signature (if secret is set)
- Parse: state|provider|compressedSession
- Decompress SessionData with gzip
Parameters:
- r: HTTP request with state cookie
Returns:
- *StateData: Decoded state data
- error: Cookie not found, invalid signature, or decoding error
func (*StateStore) SetMaxAge ¶
func (s *StateStore) SetMaxAge(age time.Duration)
SetMaxAge sets the max age for state cookies
func (*StateStore) StoreState ¶
func (s *StateStore) StoreState(w http.ResponseWriter, data *StateData) error
StoreState stores OAuth state data in a signed, compressed cookie.
This method is called at the start of the OAuth flow (BeginAuth) to save the state data for validation during the callback. The cookie is HTTPOnly and Secure (if configured) to prevent JavaScript access and MITM attacks.
Processing:
- Compress SessionData with gzip (can be large ~1-2KB)
- Concatenate: state|provider|compressedSession
- Sign with HMAC-SHA256 if secret is set
- Base64-encode payload
- Store in cookie with configured expiration (15 minutes default)
Parameters:
- w: HTTP response writer for setting cookie
- data: State data to store
Returns:
- error: Compression or encoding error
func (*StateStore) ValidateState ¶
ValidateState checks if the callback state matches the stored state.
This is the critical CSRF protection check. It ensures the OAuth callback originated from a legitimate authorization flow initiated by this server.
Validation:
- Retrieve state data from cookie
- Compare stored.State with callbackState from query parameter
- If mismatch → return error (possible CSRF attack)
Parameters:
- r: HTTP request with state cookie
- callbackState: State parameter from OAuth callback URL
Returns:
- *StateData: Validated state data (if state matches)
- error: State mismatch (CSRF) or cookie retrieval error
type StateStoreConfig ¶
type StateStoreConfig struct {
// SessionConfig contains cookie settings (Domain, Secure, HTTPOnly, SameSite)
SessionConfig *core.SessionConfig
// Secret is the key used for signing cookies (should be at least 32 bytes)
Secret []byte
// MaxAge overrides the default OAuth state cookie max age in seconds (default: 15 minutes)
MaxAge int
}
StateStoreConfig holds configuration for creating a StateStore.
Example:
cfg := &oauth.StateStoreConfig{
SessionConfig: aegis.GetSessionConfig(), // Domain, Secure, HTTPOnly, SameSite
Secret: aegis.DeriveSecret("aegis:oauth-state"), // 32+ bytes
MaxAge: 15 * 60, // 15 minutes
}
store := oauth.NewStateStore(cfg)
type Store ¶
type Store interface {
// CreateConnection creates a new OAuth provider connection.
//
// This method links an OAuth provider to an Aegis user account. It stores
// the provider's user ID, access tokens, and user profile data.
//
// Constraints:
// - (provider, provider_user_id) must be unique
// - user_id must reference a valid auth.users.id
//
// Parameters:
// - ctx: Request context
// - conn: Connection data (ID, UserID, Provider, tokens, etc.)
//
// Returns:
// - *Connection: Created connection with generated ID
// - error: Duplicate connection or foreign key violation
CreateConnection(ctx context.Context, conn Connection) (*Connection, error)
// GetConnectionByProviderUserID retrieves a connection by provider and provider user ID.
//
// This method is used during OAuth login to check if a provider account is
// already linked to an Aegis user. The provider user ID is the unique identifier
// from the OAuth provider (e.g., Google's "108...", GitHub's "12345").
//
// Parameters:
// - ctx: Request context
// - provider: Provider name ("google", "github", etc.)
// - providerUserID: Provider's unique user ID
//
// Returns:
// - *Connection: Existing connection if found
// - error: ErrNotFound if no connection exists
GetConnectionByProviderUserID(ctx context.Context, provider, providerUserID string) (*Connection, error)
// GetConnectionsByUserID retrieves all OAuth connections for a user.
//
// This method returns all linked provider accounts for a user, useful for
// displaying connected accounts in user settings.
//
// Parameters:
// - ctx: Request context
// - userID: Aegis user ID
//
// Returns:
// - []Connection: List of connections (may be empty)
// - error: Database query error
GetConnectionsByUserID(ctx context.Context, userID string) ([]Connection, error)
// UpdateConnection updates an existing OAuth connection.
//
// This method refreshes tokens or updates user profile data from the provider.
// It updates the updated_at timestamp automatically.
//
// Parameters:
// - ctx: Request context
// - conn: Updated connection data (must have matching ID)
//
// Returns:
// - error: Connection not found or database error
UpdateConnection(ctx context.Context, conn Connection) error
// DeleteConnection removes an OAuth provider link from a user account.
//
// This method unlinks the specified provider from the user's account. The
// user's Aegis account remains active.
//
// Parameters:
// - ctx: Request context
// - provider: Provider name ("google", "github", etc.)
// - userID: Aegis user ID
//
// Returns:
// - error: Connection not found or database error
DeleteConnection(ctx context.Context, provider, userID string) error
}
Store defines the interface for OAuth connection storage operations.
This interface abstracts database operations for managing OAuth provider connections. Implementations should use transactions for consistency and handle duplicate connection errors appropriately.
Thread Safety: Implementations must be safe for concurrent use from multiple goroutines.
type Tokens ¶
type Tokens struct {
AccessToken string // OAuth access token
RefreshToken string // OAuth refresh token
ExpiresAt time.Time // Token expiration time
Scopes []string // Granted scopes
Raw map[string]interface{} // Provider-specific fields
}
Tokens represents OAuth token response.
This struct holds the tokens returned by the provider after successful authorization code exchange. It's used for custom user info fetching.
type User ¶
type User struct {
auth.User
AccessToken string
RefreshToken string
ExpiresAt time.Time
ProviderData map[string]interface{}
}
User represents an OAuth-authenticated user with provider tokens.
This struct extends auth.User with OAuth-specific fields like access tokens and provider-specific data. It's used during the OAuth flow to transfer user information from the provider to Aegis.
Fields:
- User: Base user fields (ID, Email, Name, Avatar from auth.User)
- AccessToken: OAuth access token for API calls to the provider
- RefreshToken: OAuth refresh token for renewing access tokens
- ExpiresAt: When the access token expires
- ProviderData: Provider-specific fields (e.g., Google's "locale", GitHub's "company")
func GothUserToUser ¶
GothUserToUser converts goth.User to Aegis's User model.
This helper function transforms Goth's OAuth user representation into Aegis's User struct, preserving OAuth tokens and provider-specific data.
Field Mapping:
- goth.UserID → User.ID (provider's user ID)
- goth.Email → User.Email
- goth.Name → User.Name
- goth.AvatarURL → User.Avatar
- goth.AccessToken, RefreshToken, ExpiresAt preserved
Parameters:
- gothUser: User data from Goth provider
Returns:
- *User: Aegis user model