Skip to main content

Overview

The oauth2 middleware provides OAuth 2.0 authentication flows for integrating with providers like Google, GitHub, Facebook, and custom OAuth servers. Use it when you need:
  • Social login (Google, GitHub, etc.)
  • Third-party authentication
  • OAuth 2.0 authorization code flow

Installation

import "github.com/go-mizu/mizu/middlewares/oauth2"

Quick Start

app := mizu.New()

// Configure OAuth provider
config := oauth2.Config{
    ClientID:     "your-client-id",
    ClientSecret: "your-client-secret",
    RedirectURL:  "http://localhost:3000/callback",
    AuthURL:      "https://provider.com/oauth/authorize",
    TokenURL:     "https://provider.com/oauth/token",
    Scopes:       []string{"email", "profile"},
}

app.Get("/login", oauth2.Login(config))
app.Get("/callback", oauth2.Callback(config, handleUser))

Configuration

Config

FieldTypeDescription
ClientIDstringOAuth client ID
ClientSecretstringOAuth client secret
RedirectURLstringCallback URL
AuthURLstringProvider’s auth endpoint
TokenURLstringProvider’s token endpoint
Scopes[]stringRequested scopes

Examples

Google OAuth

config := oauth2.Google(oauth2.Config{
    ClientID:     os.Getenv("GOOGLE_CLIENT_ID"),
    ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
    RedirectURL:  "http://localhost:3000/auth/google/callback",
    Scopes:       []string{"email", "profile"},
})

app.Get("/auth/google", oauth2.Login(config))
app.Get("/auth/google/callback", oauth2.Callback(config, func(c *mizu.Ctx, token *oauth2.Token, user *oauth2.User) error {
    // User authenticated successfully
    session.Get(c).Set("user_id", user.ID)
    return c.Redirect(302, "/dashboard")
}))

GitHub OAuth

config := oauth2.GitHub(oauth2.Config{
    ClientID:     os.Getenv("GITHUB_CLIENT_ID"),
    ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
    RedirectURL:  "http://localhost:3000/auth/github/callback",
    Scopes:       []string{"user:email"},
})

app.Get("/auth/github", oauth2.Login(config))
app.Get("/auth/github/callback", oauth2.Callback(config, handleGitHubUser))

Custom Provider

config := oauth2.Config{
    ClientID:     "client-id",
    ClientSecret: "client-secret",
    RedirectURL:  "http://localhost:3000/callback",
    AuthURL:      "https://auth.example.com/authorize",
    TokenURL:     "https://auth.example.com/token",
    UserInfoURL:  "https://api.example.com/userinfo",
    Scopes:       []string{"openid", "profile"},
}

State Parameter

app.Get("/login", oauth2.LoginWithState(config, func(c *mizu.Ctx) string {
    // Generate state with return URL
    return base64.URLEncoding.EncodeToString([]byte(c.Query("return_url")))
}))

API Reference

Functions

// Login starts OAuth flow
func Login(config Config) mizu.Handler

// LoginWithState starts flow with custom state
func LoginWithState(config Config, stateFn func(*mizu.Ctx) string) mizu.Handler

// Callback handles OAuth callback
func Callback(config Config, handler CallbackHandler) mizu.Handler

// Provider presets
func Google(config Config) Config
func GitHub(config Config) Config
func Facebook(config Config) Config

Types

type Token struct {
    AccessToken  string
    RefreshToken string
    Expiry       time.Time
}

type User struct {
    ID       string
    Email    string
    Name     string
    Picture  string
    Provider string
}

type CallbackHandler func(*mizu.Ctx, *Token, *User) error

Technical Details

Architecture

The OAuth2 middleware is designed as a resource server implementation that validates OAuth 2.0 access tokens. It follows a flexible architecture that supports multiple validation strategies:
  1. Token Extraction: Supports multiple token sources (header, query, form) via configurable TokenLookup parameter
  2. Token Validation: Two validation strategies:
    • Custom validator function for flexibility
    • Token introspection endpoint (RFC 7662) for standard OAuth servers
  3. Scope Verification: Automatic scope checking against required scopes
  4. Context Storage: Validated token information is stored in request context for downstream handlers

Token Validation Flow

  1. Extract token from request based on TokenLookup configuration
  2. Validate token using:
    • Custom Validator function if provided, or
    • OAuth 2.0 token introspection endpoint
  3. Check token expiration if ExpiresAt is set
  4. Verify required scopes match token scopes
  5. Store validated token in context
  6. Pass request to next handler

Token Introspection

When using the introspection endpoint, the middleware:
  • Sends POST request to IntrospectionURL with token
  • Uses Basic authentication with ClientID and ClientSecret if provided
  • Parses introspection response according to RFC 7662
  • Validates the active field and extracts token metadata (subject, scopes, expiry)

Error Handling

The middleware returns appropriate HTTP status codes:
  • 401 Unauthorized: Missing, invalid, or expired tokens
  • 403 Forbidden: Insufficient scopes
  • Sets WWW-Authenticate: Bearer header on errors
Custom error handling can be implemented via the ErrorHandler option.

Context Access

Helper functions provide convenient access to token information:
  • Get(c): Retrieves full token object
  • Subject(c): Gets token subject
  • Scopes(c): Returns token scopes
  • HasScope(c, scope): Checks for specific scope

Scope Middleware

The RequireScopes middleware can be chained after the main OAuth2 middleware to enforce scope requirements at specific routes or route groups.

Security Considerations

  1. Store secrets securely - Use environment variables
  2. Validate state parameter - Prevents CSRF attacks
  3. Use HTTPS - Required for production OAuth
  4. Verify tokens - Check expiration and validity
  5. Limit scopes - Request only needed permissions

Best Practices

  • Store tokens securely (encrypted if stored)
  • Implement token refresh for long sessions
  • Handle callback errors gracefully
  • Log OAuth events for security monitoring

Testing

The OAuth2 middleware includes comprehensive test coverage for all functionality:
Test CaseDescriptionExpected Behavior
TestNew - valid tokenRequest with valid Bearer tokenReturns 200 OK, token validated
TestNew - invalid tokenRequest with invalid Bearer tokenReturns 401 Unauthorized
TestNew - missing tokenRequest without Authorization headerReturns 401 Unauthorized
TestWithOptions_RequiredScopesToken with insufficient scopesReturns 403 Forbidden
TestWithOptions_ExpiredTokenToken with past expiration timeReturns 401 Unauthorized
TestGetRetrieve token from contextToken object accessible via Get()
TestSubjectExtract subject from tokenSubject field returned correctly
TestHasScopeCheck for specific scopeReturns true/false based on presence
TestRequireScopesScope enforcement middlewareBlocks request if scopes missing
TestWWWAuthenticateHeaderInvalid token responseWWW-Authenticate header present
TestExtractToken_QueryToken in query parameterToken extracted from URL query
TestExtractToken_FormToken in form bodyToken extracted from form data
TestExtractToken_InvalidLookupMalformed TokenLookup configReturns 401, no token found
TestExtractToken_UnknownSourceUnknown token source typeReturns 401, no token found
TestExtractToken_HeaderWithoutBearerPlain token without Bearer prefixToken extracted and validated
TestWithOptions_NoValidatorNo validator configuredReturns 401 with ErrNoValidator
TestWithOptions_CustomErrorHandlerCustom error handler setCustom handler invoked on error
TestIntrospectToken - activeIntrospection returns active tokenReturns 200 OK
TestIntrospectToken - inactiveIntrospection returns inactive tokenReturns 401 Unauthorized
TestIntrospectToken_ServerErrorIntrospection endpoint errorReturns 401 Unauthorized
TestIntrospectToken_InvalidJSONMalformed introspection responseReturns 401 Unauthorized
TestIntrospectToken_ConnectionErrorCannot connect to endpointReturns 401 Unauthorized
TestGet_NoTokenGet() without middlewareReturns nil
TestSubject_NoTokenSubject() without tokenReturns empty string
TestScopesRetrieve scopes listReturns all token scopes
TestScopes_NoTokenScopes() without tokenReturns nil
TestRequireScopes_NoTokenRequireScopes without authReturns 401 Unauthorized
TestRequireScopes_SufficientToken has required scopesReturns 200 OK, access granted
TestHasScope_NoTokenHasScope() without tokenReturns false
TestErrorTypesError message stringsAll error types return correct messages
TestWithOptions_IntrospectionWithoutClientCredsIntrospection without client authNo Basic auth sent, request succeeds
  • oidc - OpenID Connect
  • jwt - JWT authentication
  • session - Session management