extauth

package
v0.0.0-...-8acab51 Latest Latest
Warning

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

Go to latest
Published: Apr 26, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package extauth provides external authentication provider integration. This package implements OIDC/OAuth2 client functionality to authenticate users via external identity providers like Google, GitHub, Microsoft, etc.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrProviderNotFound is returned when a provider is not found.
	ErrProviderNotFound = errors.New("provider not found")
	// ErrProviderDisabled is returned when a provider is disabled.
	ErrProviderDisabled = errors.New("provider disabled")
	// ErrInvalidState is returned when the state parameter is invalid.
	ErrInvalidState = errors.New("invalid state parameter")
	// ErrStateExpired is returned when the state has expired.
	ErrStateExpired = errors.New("state expired")
	// ErrInvalidCode is returned when the authorization code is invalid.
	ErrInvalidCode = errors.New("invalid authorization code")
	// ErrTokenExpired is returned when the token has expired.
	ErrTokenExpired = errors.New("token expired")
	// ErrInvalidToken is returned when the token is invalid.
	ErrInvalidToken = errors.New("invalid token")
	// ErrInvalidNonce is returned when the nonce is invalid.
	ErrInvalidNonce = errors.New("invalid nonce")
	// ErrUserNotFound is returned when the user is not found.
	ErrUserNotFound = errors.New("user not found")
	// ErrAccountAlreadyLinked is returned when the account is already linked.
	ErrAccountAlreadyLinked = errors.New("account already linked to another user")
	// ErrEmailNotAllowed is returned when the email domain is not allowed.
	ErrEmailNotAllowed = errors.New("email domain not allowed")
	// ErrDiscoveryFailed is returned when OIDC discovery fails.
	ErrDiscoveryFailed = errors.New("OIDC discovery failed")
)

Functions

This section is empty.

Types

type AccountStore

type AccountStore interface {
	Save(ctx context.Context, account *LinkedAccount) error
	GetByProviderUser(ctx context.Context, providerID, providerUserID string) (*LinkedAccount, error)
	GetByUser(ctx context.Context, userID string) ([]*LinkedAccount, error)
	Delete(ctx context.Context, userID, providerID string) error
}

AccountStore defines the interface for storing linked accounts.

type AuthState

type AuthState struct {
	State        string    `json:"state"`
	Nonce        string    `json:"nonce"`
	ProviderID   string    `json:"provider_id"`
	RedirectURI  string    `json:"redirect_uri"`
	CodeVerifier string    `json:"code_verifier,omitempty"`
	CreatedAt    time.Time `json:"created_at"`
	ExpiresAt    time.Time `json:"expires_at"`
	UserID       string    `json:"user_id,omitempty"` // For account linking
}

AuthState represents the state stored during the auth flow.

type AuthenticatedUser

type AuthenticatedUser struct {
	ID         string   `json:"id"`
	Email      string   `json:"email"`
	Name       string   `json:"name"`
	Picture    string   `json:"picture,omitempty"`
	Roles      []string `json:"roles"`
	ProviderID string   `json:"provider_id"`
}

AuthenticatedUser represents an authenticated user.

type AuthorizeRequest

type AuthorizeRequest struct {
	ProviderID  string `json:"provider_id"`
	RedirectURI string `json:"redirect_uri,omitempty"`
	State       string `json:"state,omitempty"`
	UserID      string `json:"user_id,omitempty"` // For account linking
}

AuthorizeRequest represents a request to start the auth flow.

type AuthorizeResponse

type AuthorizeResponse struct {
	AuthURL string `json:"auth_url"`
	State   string `json:"state"`
}

AuthorizeResponse represents the response to start the auth flow.

type CallbackRequest

type CallbackRequest struct {
	ProviderID       string `json:"provider_id"`
	Code             string `json:"code"`
	State            string `json:"state"`
	Error            string `json:"error,omitempty"`
	ErrorDescription string `json:"error_description,omitempty"`
}

CallbackRequest represents the callback from the provider.

type CallbackResponse

type CallbackResponse struct {
	User         *AuthenticatedUser `json:"user"`
	AccessToken  string             `json:"access_token"`
	RefreshToken string             `json:"refresh_token,omitempty"`
	ExpiresIn    int                `json:"expires_in"`
	IsNewUser    bool               `json:"is_new_user"`
}

CallbackResponse represents the response after processing the callback.

type ClaimMapping

type ClaimMapping struct {
	Subject  string `json:"subject" yaml:"subject"`
	Email    string `json:"email" yaml:"email"`
	Name     string `json:"name" yaml:"name"`
	Picture  string `json:"picture,omitempty" yaml:"picture,omitempty"`
	Groups   string `json:"groups,omitempty" yaml:"groups,omitempty"`
	Roles    string `json:"roles,omitempty" yaml:"roles,omitempty"`
	Username string `json:"username,omitempty" yaml:"username,omitempty"`
}

ClaimMapping maps OIDC claims to user attributes.

func DefaultClaimMapping

func DefaultClaimMapping() *ClaimMapping

DefaultClaimMapping returns the default claim mapping.

type DefaultService

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

DefaultService implements the Service interface.

func NewService

func NewService(cfg *ServiceConfig) (*DefaultService, error)

NewService creates a new external auth service.

func (*DefaultService) AddProvider

func (s *DefaultService) AddProvider(cfg *ProviderConfig) error

AddProvider adds a new provider at runtime.

func (*DefaultService) Authorize

Authorize starts the authorization flow.

func (*DefaultService) Callback

Callback handles the authorization callback.

func (*DefaultService) GetLinkedAccounts

func (s *DefaultService) GetLinkedAccounts(ctx context.Context, userID string) ([]*LinkedAccount, error)

GetLinkedAccounts returns all linked accounts for a user.

func (*DefaultService) GetProvider

func (s *DefaultService) GetProvider(ctx context.Context, id string) (*ProviderConfig, error)

GetProvider returns a provider by ID.

func (*DefaultService) GetUserInfo

func (s *DefaultService) GetUserInfo(ctx context.Context, providerID, accessToken string) (*UserInfo, error)

GetUserInfo gets user info from the provider.

func (*DefaultService) LinkAccount

func (s *DefaultService) LinkAccount(ctx context.Context, userID string, req *AuthorizeRequest) (*AuthorizeResponse, error)

LinkAccount links an external account to an existing user.

func (*DefaultService) ListProviders

func (s *DefaultService) ListProviders(ctx context.Context) ([]*ProviderInfo, error)

ListProviders returns all enabled providers.

func (*DefaultService) RefreshToken

func (s *DefaultService) RefreshToken(ctx context.Context, providerID, refreshToken string) (*TokenResponse, error)

RefreshToken refreshes an access token.

func (*DefaultService) RemoveProvider

func (s *DefaultService) RemoveProvider(id string)

RemoveProvider removes a provider at runtime.

func (*DefaultService) UnlinkAccount

func (s *DefaultService) UnlinkAccount(ctx context.Context, userID, providerID string) error

UnlinkAccount unlinks an external account from a user.

type DiscoveryDocument

type DiscoveryDocument struct {
	Issuer                           string   `json:"issuer"`
	AuthorizationEndpoint            string   `json:"authorization_endpoint"`
	TokenEndpoint                    string   `json:"token_endpoint"`
	UserInfoEndpoint                 string   `json:"userinfo_endpoint,omitempty"`
	JWKSURI                          string   `json:"jwks_uri,omitempty"`
	ScopesSupported                  []string `json:"scopes_supported,omitempty"`
	ResponseTypesSupported           []string `json:"response_types_supported,omitempty"`
	IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
	ClaimsSupported                  []string `json:"claims_supported,omitempty"`
}

DiscoveryDocument represents the OIDC discovery document.

type Handler

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

Handler handles external auth HTTP requests.

func NewHandler

func NewHandler(service Service) *Handler

NewHandler creates a new external auth handler.

func (*Handler) Authorize

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

Authorize starts the authorization flow.

func (*Handler) Callback

func (h *Handler) Callback(c echo.Context) error

Callback handles the authorization callback.

func (*Handler) ExchangeToken

func (h *Handler) ExchangeToken(c echo.Context) error

ExchangeToken exchanges an authorization code for tokens.

func (*Handler) GetLinkedAccounts

func (h *Handler) GetLinkedAccounts(c echo.Context) error

GetLinkedAccounts returns all linked accounts for the current user.

func (*Handler) GetUserInfo

func (h *Handler) GetUserInfo(c echo.Context) error

GetUserInfo gets user info from the provider.

func (*Handler) LinkAccount

func (h *Handler) LinkAccount(c echo.Context) error

LinkAccount links an external account to the current user.

func (*Handler) ListProviders

func (h *Handler) ListProviders(c echo.Context) error

ListProviders returns all enabled providers.

func (*Handler) RefreshToken

func (h *Handler) RefreshToken(c echo.Context) error

RefreshToken refreshes an access token.

func (*Handler) RegisterProtectedRoutes

func (h *Handler) RegisterProtectedRoutes(g *echo.Group)

RegisterProtectedRoutes registers routes that require authentication.

func (*Handler) RegisterRoutes

func (h *Handler) RegisterRoutes(g *echo.Group)

RegisterRoutes registers the external auth routes. Note: Some routes require authentication - use RegisterProtectedRoutes for those.

func (*Handler) UnlinkAccount

func (h *Handler) UnlinkAccount(c echo.Context) error

UnlinkAccount unlinks an external account from the current user.

type LinkedAccount

type LinkedAccount struct {
	ID             string    `json:"id"`
	UserID         string    `json:"user_id"`
	ProviderID     string    `json:"provider_id"`
	ProviderUserID string    `json:"provider_user_id"`
	Email          string    `json:"email,omitempty"`
	Name           string    `json:"name,omitempty"`
	Picture        string    `json:"picture,omitempty"`
	AccessToken    string    `json:"-"`
	RefreshToken   string    `json:"-"`
	TokenExpiry    time.Time `json:"token_expiry,omitempty"`
	CreatedAt      time.Time `json:"created_at"`
	UpdatedAt      time.Time `json:"updated_at"`
}

LinkedAccount represents an external account linked to a user.

type MemoryAccountStore

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

MemoryAccountStore is an in-memory implementation of AccountStore.

func NewMemoryAccountStore

func NewMemoryAccountStore() *MemoryAccountStore

NewMemoryAccountStore creates a new in-memory account store.

func (*MemoryAccountStore) Delete

func (s *MemoryAccountStore) Delete(ctx context.Context, userID, providerID string) error

Delete deletes a linked account.

func (*MemoryAccountStore) GetByProviderUser

func (s *MemoryAccountStore) GetByProviderUser(ctx context.Context, providerID, providerUserID string) (*LinkedAccount, error)

GetByProviderUser gets a linked account by provider and provider user ID.

func (*MemoryAccountStore) GetByUser

func (s *MemoryAccountStore) GetByUser(ctx context.Context, userID string) ([]*LinkedAccount, error)

GetByUser gets all linked accounts for a user.

func (*MemoryAccountStore) Save

func (s *MemoryAccountStore) Save(ctx context.Context, account *LinkedAccount) error

Save saves a linked account.

type MemoryStateStore

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

MemoryStateStore is an in-memory implementation of StateStore.

func NewMemoryStateStore

func NewMemoryStateStore() *MemoryStateStore

NewMemoryStateStore creates a new in-memory state store.

func (*MemoryStateStore) Cleanup

func (s *MemoryStateStore) Cleanup(ctx context.Context) error

Cleanup removes expired states.

func (*MemoryStateStore) Delete

func (s *MemoryStateStore) Delete(ctx context.Context, state string) error

Delete deletes an auth state.

func (*MemoryStateStore) Get

func (s *MemoryStateStore) Get(ctx context.Context, state string) (*AuthState, error)

Get retrieves an auth state by state parameter.

func (*MemoryStateStore) Save

func (s *MemoryStateStore) Save(ctx context.Context, state *AuthState) error

Save saves an auth state.

type ProviderClient

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

ProviderClient is a client for an external auth provider.

func NewProviderClient

func NewProviderClient(config *ProviderConfig) (*ProviderClient, error)

NewProviderClient creates a new provider client.

func (*ProviderClient) AuthorizationURL

func (c *ProviderClient) AuthorizationURL(state, nonce, codeChallenge string) (string, error)

AuthorizationURL returns the authorization URL for the auth flow.

func (*ProviderClient) Config

func (c *ProviderClient) Config() *ProviderConfig

Config returns the provider configuration.

func (*ProviderClient) Discover

func (c *ProviderClient) Discover(ctx context.Context) (*DiscoveryDocument, error)

Discover fetches the OIDC discovery document.

func (*ProviderClient) ExchangeCode

func (c *ProviderClient) ExchangeCode(ctx context.Context, code, codeVerifier string) (*TokenResponse, error)

ExchangeCode exchanges an authorization code for tokens.

func (*ProviderClient) GetUserInfo

func (c *ProviderClient) GetUserInfo(ctx context.Context, accessToken string) (*UserInfo, error)

GetUserInfo fetches user info from the userinfo endpoint.

func (*ProviderClient) RefreshToken

func (c *ProviderClient) RefreshToken(ctx context.Context, refreshToken string) (*TokenResponse, error)

RefreshToken refreshes an access token using a refresh token.

type ProviderConfig

type ProviderConfig struct {
	// ID is the unique identifier for this provider.
	ID string `json:"id" yaml:"id"`
	// Name is the display name for this provider.
	Name string `json:"name" yaml:"name"`
	// Type is the provider type.
	Type ProviderType `json:"type" yaml:"type"`
	// Enabled indicates if this provider is enabled.
	Enabled bool `json:"enabled" yaml:"enabled"`
	// ClientID is the OAuth client ID.
	ClientID string `json:"client_id" yaml:"client_id"`
	// ClientSecret is the OAuth client secret.
	ClientSecret string `json:"client_secret" yaml:"client_secret"`
	// IssuerURL is the OIDC issuer URL (for discovery).
	IssuerURL string `json:"issuer_url,omitempty" yaml:"issuer_url,omitempty"`
	// AuthURL is the authorization endpoint URL.
	AuthURL string `json:"auth_url,omitempty" yaml:"auth_url,omitempty"`
	// TokenURL is the token endpoint URL.
	TokenURL string `json:"token_url,omitempty" yaml:"token_url,omitempty"`
	// UserInfoURL is the userinfo endpoint URL.
	UserInfoURL string `json:"userinfo_url,omitempty" yaml:"userinfo_url,omitempty"`
	// JWKSURL is the JWKS endpoint URL.
	JWKSURL string `json:"jwks_url,omitempty" yaml:"jwks_url,omitempty"`
	// Scopes is the list of scopes to request.
	Scopes []string `json:"scopes,omitempty" yaml:"scopes,omitempty"`
	// RedirectURL is the callback URL.
	RedirectURL string `json:"redirect_url" yaml:"redirect_url"`
	// ClaimMapping maps provider claims to user attributes.
	ClaimMapping *ClaimMapping `json:"claim_mapping,omitempty" yaml:"claim_mapping,omitempty"`
	// RoleMappings maps provider claims to user roles.
	RoleMappings []RoleMapping `json:"role_mappings,omitempty" yaml:"role_mappings,omitempty"`
	// AutoCreateUser indicates if users should be auto-created.
	AutoCreateUser bool `json:"auto_create_user" yaml:"auto_create_user"`
	// DefaultRole is the default role for auto-created users.
	DefaultRole string `json:"default_role" yaml:"default_role"`
	// AllowedDomains restricts login to specific email domains.
	AllowedDomains []string `json:"allowed_domains,omitempty" yaml:"allowed_domains,omitempty"`
	// IconURL is the URL for the provider's icon.
	IconURL string `json:"icon_url,omitempty" yaml:"icon_url,omitempty"`
	// Order is the display order for this provider.
	Order int `json:"order" yaml:"order"`
}

ProviderConfig holds the configuration for an external auth provider.

type ProviderInfo

type ProviderInfo struct {
	ID      string       `json:"id"`
	Name    string       `json:"name"`
	Type    ProviderType `json:"type"`
	IconURL string       `json:"icon_url,omitempty"`
	Order   int          `json:"order"`
}

ProviderInfo represents public information about a provider.

type ProviderType

type ProviderType string

ProviderType represents the type of external auth provider.

const (
	// ProviderGeneric is a generic OIDC provider.
	ProviderGeneric ProviderType = "generic"
	// ProviderGoogle is Google's OIDC provider.
	ProviderGoogle ProviderType = "google"
	// ProviderGitHub is GitHub's OAuth provider.
	ProviderGitHub ProviderType = "github"
	// ProviderMicrosoft is Microsoft/Azure AD's OIDC provider.
	ProviderMicrosoft ProviderType = "microsoft"
	// ProviderKeycloak is Keycloak's OIDC provider.
	ProviderKeycloak ProviderType = "keycloak"
	// ProviderAuthentik is Authentik's OIDC provider.
	ProviderAuthentik ProviderType = "authentik"
	// ProviderAuth0 is Auth0's OIDC provider.
	ProviderAuth0 ProviderType = "auth0"
)

type RoleMapping

type RoleMapping struct {
	Claim string `json:"claim" yaml:"claim"`
	Value string `json:"value" yaml:"value"`
	Role  string `json:"role" yaml:"role"`
}

RoleMapping maps a claim value to a role.

type Service

type Service interface {
	// ListProviders returns all enabled providers.
	ListProviders(ctx context.Context) ([]*ProviderInfo, error)

	// GetProvider returns a provider by ID.
	GetProvider(ctx context.Context, id string) (*ProviderConfig, error)

	// Authorize starts the authorization flow.
	Authorize(ctx context.Context, req *AuthorizeRequest) (*AuthorizeResponse, error)

	// Callback handles the authorization callback.
	Callback(ctx context.Context, req *CallbackRequest) (*CallbackResponse, error)

	// RefreshToken refreshes an access token.
	RefreshToken(ctx context.Context, providerID, refreshToken string) (*TokenResponse, error)

	// GetUserInfo gets user info from the provider.
	GetUserInfo(ctx context.Context, providerID, accessToken string) (*UserInfo, error)

	// LinkAccount links an external account to an existing user.
	LinkAccount(ctx context.Context, userID string, req *AuthorizeRequest) (*AuthorizeResponse, error)

	// UnlinkAccount unlinks an external account from a user.
	UnlinkAccount(ctx context.Context, userID, providerID string) error

	// GetLinkedAccounts returns all linked accounts for a user.
	GetLinkedAccounts(ctx context.Context, userID string) ([]*LinkedAccount, error)
}

Service defines the external auth service interface.

type ServiceConfig

type ServiceConfig struct {
	Providers      []*ProviderConfig
	StateStore     StateStore
	AccountStore   AccountStore
	UserStore      UserStore
	TokenGenerator TokenGenerator
	StateTTL       time.Duration
}

ServiceConfig holds the service configuration.

type StateStore

type StateStore interface {
	Save(ctx context.Context, state *AuthState) error
	Get(ctx context.Context, state string) (*AuthState, error)
	Delete(ctx context.Context, state string) error
	Cleanup(ctx context.Context) error
}

StateStore defines the interface for storing auth state.

type TokenGenerator

type TokenGenerator interface {
	GenerateAccessToken(userID, username, role string) (string, error)
}

TokenGenerator generates access tokens for authenticated users.

type TokenResponse

type TokenResponse struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`
	IDToken      string `json:"id_token,omitempty"`
	Scope        string `json:"scope,omitempty"`
}

TokenResponse represents the token endpoint response.

type UserInfo

type UserInfo struct {
	Subject       string   `json:"sub"`
	Email         string   `json:"email,omitempty"`
	EmailVerified bool     `json:"email_verified,omitempty"`
	Name          string   `json:"name,omitempty"`
	GivenName     string   `json:"given_name,omitempty"`
	FamilyName    string   `json:"family_name,omitempty"`
	Picture       string   `json:"picture,omitempty"`
	Locale        string   `json:"locale,omitempty"`
	Username      string   `json:"preferred_username,omitempty"`
	Groups        []string `json:"groups,omitempty"`
	Roles         []string `json:"roles,omitempty"`
}

UserInfo represents user information from the provider.

type UserStore

type UserStore interface {
	GetByID(ctx context.Context, id string) (*AuthenticatedUser, error)
	GetByEmail(ctx context.Context, email string) (*AuthenticatedUser, error)
	Create(ctx context.Context, user *AuthenticatedUser) error
	Update(ctx context.Context, user *AuthenticatedUser) error
}

UserStore defines the interface for user operations.

Jump to

Keyboard shortcuts

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