Documentation
¶
Overview ¶
Package oauth provides OAuth2 authorization code flow implementations for common providers.
This package includes a Provider interface and concrete implementations for Google and GitHub. Each provider handles the full OAuth2 flow: generating authorization URLs, exchanging codes for tokens, and fetching verified user information.
Features ¶
- Provider interface for pluggable OAuth2 implementations
- Google OAuth2 with email verification
- GitHub OAuth2 with primary verified email resolution
- Functional options for custom HTTP clients (testing, custom transports)
- Configuration structs with env tags for environment-based setup
- Sentinel errors with "oauth:" prefix for consistent error handling
Usage ¶
Google provider setup:
provider, err := oauth.NewGoogleProvider(oauth.GoogleConfig{
ClientID: os.Getenv("GOOGLE_OAUTH_CLIENT_ID"),
ClientSecret: os.Getenv("GOOGLE_OAUTH_CLIENT_SECRET"),
RedirectURL: "https://example.com/auth/google/callback",
})
if err != nil {
log.Fatal(err)
}
// Generate authorization URL
url := provider.AuthCodeURL("random-state-string")
// Exchange code for token (in callback handler)
token, err := provider.Exchange(ctx, code, "")
if err != nil {
// handle error
}
// Fetch user info
user, err := provider.FetchUserInfo(ctx, token)
if err != nil {
// handle error
}
GitHub provider setup:
provider, err := oauth.NewGitHubProvider(oauth.GitHubConfig{
ClientID: os.Getenv("GITHUB_OAUTH_CLIENT_ID"),
ClientSecret: os.Getenv("GITHUB_OAUTH_CLIENT_SECRET"),
RedirectURL: "https://example.com/auth/github/callback",
})
if err != nil {
log.Fatal(err)
}
Custom Providers ¶
Implement the Provider interface to add support for other OAuth2 providers:
type MyProvider struct { /* ... */ }
func (p *MyProvider) Name() string { return "my-provider" }
func (p *MyProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string { /* ... */ }
func (p *MyProvider) Exchange(ctx context.Context, code, redirectURI string) (*oauth2.Token, error) { /* ... */ }
func (p *MyProvider) FetchUserInfo(ctx context.Context, token *oauth2.Token) (*oauth.UserInfo, error) { /* ... */ }
Testing ¶
Use WithHTTPClient to inject a test server for unit testing:
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// mock responses
}))
defer ts.Close()
provider, err := oauth.NewGoogleProvider(cfg, oauth.WithHTTPClient(ts.Client()))
Error Handling ¶
The package provides sentinel errors for specific failure modes:
- ErrMissingClientID: Constructor called without client ID
- ErrMissingClientSecret: Constructor called without client secret
- ErrEmailNotVerified: Provider reports unverified email
- ErrFetchFailed: HTTP request to provider failed
- ErrNilResponse: Provider returned nil HTTP response
- ErrRequestFailed: Provider returned non-OK HTTP status
- ErrDecodeFailed: Failed to decode provider JSON response
Use errors.Is for checking:
if errors.Is(err, oauth.ErrEmailNotVerified) {
// ask user to verify email
}
Security ¶
- Always validate the state parameter to prevent CSRF attacks
- Use HTTPS redirect URIs in production
- Store tokens securely (encrypted at rest, never in URLs)
- Both providers enforce email verification before returning user info
- Keep client secrets out of source control (use environment variables)
Index ¶
- Constants
- Variables
- func GitHubDefaultScopes() []string
- func GoogleDefaultScopes() []string
- type GitHubConfig
- type GitHubProvider
- func (p *GitHubProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
- func (p *GitHubProvider) Exchange(ctx context.Context, code, redirectURI string) (*oauth2.Token, error)
- func (p *GitHubProvider) FetchUserInfo(ctx context.Context, token *oauth2.Token) (*UserInfo, error)
- func (p *GitHubProvider) Name() string
- type GoogleConfig
- type GoogleProvider
- func (p *GoogleProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
- func (p *GoogleProvider) Exchange(ctx context.Context, code, redirectURI string) (*oauth2.Token, error)
- func (p *GoogleProvider) FetchUserInfo(ctx context.Context, token *oauth2.Token) (*UserInfo, error)
- func (p *GoogleProvider) Name() string
- type Option
- type Provider
- type UserInfo
Constants ¶
const (
// GitHubProviderName is the identifier for GitHub OAuth provider.
GitHubProviderName = "github"
)
const (
// GoogleProviderName is the identifier for Google OAuth provider.
GoogleProviderName = "google"
)
Variables ¶
var ( // ErrMissingClientID is returned when the OAuth client ID is not provided. ErrMissingClientID = errors.New("oauth: missing client ID") // ErrMissingClientSecret is returned when the OAuth client secret is not provided. ErrMissingClientSecret = errors.New("oauth: missing client secret") // ErrEmailNotVerified is returned when the OAuth provider reports // that the user's email is not verified. ErrEmailNotVerified = errors.New("oauth: email not verified") // ErrNilResponse is returned when the OAuth provider returns a nil response. ErrNilResponse = errors.New("oauth: nil response from provider") // ErrFetchFailed is returned when fetching data from the OAuth provider fails. ErrFetchFailed = errors.New("oauth: failed to fetch from provider") // ErrRequestFailed is returned when the OAuth provider returns a non-OK status. ErrRequestFailed = errors.New("oauth: request returned non-OK status") // ErrDecodeFailed is returned when decoding the OAuth provider response fails. ErrDecodeFailed = errors.New("oauth: failed to decode response") )
Functions ¶
func GitHubDefaultScopes ¶
func GitHubDefaultScopes() []string
GitHubDefaultScopes returns the default scopes for GitHub OAuth.
func GoogleDefaultScopes ¶
func GoogleDefaultScopes() []string
GoogleDefaultScopes returns the default scopes for Google OAuth.
Types ¶
type GitHubConfig ¶
type GitHubConfig struct {
ClientID string `env:"CLIENT_ID,required"`
ClientSecret string `env:"CLIENT_SECRET,required"`
RedirectURL string `env:"REDIRECT_URL"`
Scopes []string `env:"SCOPES" envSeparator:","`
}
GitHubConfig holds GitHub OAuth configuration.
type GitHubProvider ¶
type GitHubProvider struct {
// contains filtered or unexported fields
}
GitHubProvider implements Provider for GitHub OAuth.
func NewGitHubProvider ¶
func NewGitHubProvider(cfg GitHubConfig, opts ...Option) (*GitHubProvider, error)
NewGitHubProvider creates a new GitHub OAuth provider. Returns an error if ClientID or ClientSecret is empty.
func (*GitHubProvider) AuthCodeURL ¶
func (p *GitHubProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
AuthCodeURL generates the authorization URL.
func (*GitHubProvider) Exchange ¶
func (p *GitHubProvider) Exchange(ctx context.Context, code, redirectURI string) (*oauth2.Token, error)
Exchange trades an authorization code for tokens.
func (*GitHubProvider) FetchUserInfo ¶
FetchUserInfo retrieves user information from GitHub. Returns ErrEmailNotVerified if no verified primary email is found.
func (*GitHubProvider) Name ¶
func (p *GitHubProvider) Name() string
Name returns the provider identifier.
type GoogleConfig ¶
type GoogleConfig struct {
ClientID string `env:"CLIENT_ID,required"`
ClientSecret string `env:"CLIENT_SECRET,required"`
RedirectURL string `env:"REDIRECT_URL"`
Scopes []string `env:"SCOPES" envSeparator:","`
}
GoogleConfig holds Google OAuth configuration.
type GoogleProvider ¶
type GoogleProvider struct {
// contains filtered or unexported fields
}
GoogleProvider implements Provider for Google OAuth.
func NewGoogleProvider ¶
func NewGoogleProvider(cfg GoogleConfig, opts ...Option) (*GoogleProvider, error)
NewGoogleProvider creates a new Google OAuth provider. Returns an error if ClientID or ClientSecret is empty.
func (*GoogleProvider) AuthCodeURL ¶
func (p *GoogleProvider) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
AuthCodeURL generates the authorization URL.
func (*GoogleProvider) Exchange ¶
func (p *GoogleProvider) Exchange(ctx context.Context, code, redirectURI string) (*oauth2.Token, error)
Exchange trades an authorization code for tokens.
func (*GoogleProvider) FetchUserInfo ¶
FetchUserInfo retrieves user information from Google. Returns ErrEmailNotVerified if the user's email is not verified.
func (*GoogleProvider) Name ¶
func (p *GoogleProvider) Name() string
Name returns the provider identifier.
type Option ¶
type Option func(*options)
Option configures an OAuth provider.
func WithHTTPClient ¶
WithHTTPClient sets a custom HTTP client for OAuth requests. This is useful for testing with httptest servers or injecting custom transports (e.g., logging, retries).
type Provider ¶
type Provider interface {
// Name returns the provider identifier (e.g., "google", "github").
Name() string
// AuthCodeURL generates the authorization URL for the OAuth flow.
AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string
// Exchange trades an authorization code for tokens.
Exchange(ctx context.Context, code, redirectURI string) (*oauth2.Token, error)
// FetchUserInfo retrieves user information using the access token.
// Implementations must verify the user's email and return ErrEmailNotVerified
// if the email is not verified.
FetchUserInfo(ctx context.Context, token *oauth2.Token) (*UserInfo, error)
}
Provider abstracts provider-specific OAuth operations. Each provider (Google, GitHub, etc.) implements this interface. Provider implementations handle all provider-specific details internally, including email verification checks.