> ## Documentation Index
> Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth2

> OAuth 2.0 authentication middleware for third-party login integration.

## 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

```go theme={null}
import "github.com/go-mizu/mizu/middlewares/oauth2"
```

## Quick Start

```go theme={null}
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

| Field          | Type       | Description               |
| -------------- | ---------- | ------------------------- |
| `ClientID`     | `string`   | OAuth client ID           |
| `ClientSecret` | `string`   | OAuth client secret       |
| `RedirectURL`  | `string`   | Callback URL              |
| `AuthURL`      | `string`   | Provider's auth endpoint  |
| `TokenURL`     | `string`   | Provider's token endpoint |
| `Scopes`       | `[]string` | Requested scopes          |

## Examples

### Google OAuth

```go theme={null}
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

```go theme={null}
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

```go theme={null}
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

```go theme={null}
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

```go theme={null}
// 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

```go theme={null}
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 Case                                         | Description                          | Expected Behavior                       |
| ------------------------------------------------- | ------------------------------------ | --------------------------------------- |
| `TestNew` - valid token                           | Request with valid Bearer token      | Returns 200 OK, token validated         |
| `TestNew` - invalid token                         | Request with invalid Bearer token    | Returns 401 Unauthorized                |
| `TestNew` - missing token                         | Request without Authorization header | Returns 401 Unauthorized                |
| `TestWithOptions_RequiredScopes`                  | Token with insufficient scopes       | Returns 403 Forbidden                   |
| `TestWithOptions_ExpiredToken`                    | Token with past expiration time      | Returns 401 Unauthorized                |
| `TestGet`                                         | Retrieve token from context          | Token object accessible via Get()       |
| `TestSubject`                                     | Extract subject from token           | Subject field returned correctly        |
| `TestHasScope`                                    | Check for specific scope             | Returns true/false based on presence    |
| `TestRequireScopes`                               | Scope enforcement middleware         | Blocks request if scopes missing        |
| `TestWWWAuthenticateHeader`                       | Invalid token response               | WWW-Authenticate header present         |
| `TestExtractToken_Query`                          | Token in query parameter             | Token extracted from URL query          |
| `TestExtractToken_Form`                           | Token in form body                   | Token extracted from form data          |
| `TestExtractToken_InvalidLookup`                  | Malformed TokenLookup config         | Returns 401, no token found             |
| `TestExtractToken_UnknownSource`                  | Unknown token source type            | Returns 401, no token found             |
| `TestExtractToken_HeaderWithoutBearer`            | Plain token without Bearer prefix    | Token extracted and validated           |
| `TestWithOptions_NoValidator`                     | No validator configured              | Returns 401 with ErrNoValidator         |
| `TestWithOptions_CustomErrorHandler`              | Custom error handler set             | Custom handler invoked on error         |
| `TestIntrospectToken` - active                    | Introspection returns active token   | Returns 200 OK                          |
| `TestIntrospectToken` - inactive                  | Introspection returns inactive token | Returns 401 Unauthorized                |
| `TestIntrospectToken_ServerError`                 | Introspection endpoint error         | Returns 401 Unauthorized                |
| `TestIntrospectToken_InvalidJSON`                 | Malformed introspection response     | Returns 401 Unauthorized                |
| `TestIntrospectToken_ConnectionError`             | Cannot connect to endpoint           | Returns 401 Unauthorized                |
| `TestGet_NoToken`                                 | Get() without middleware             | Returns nil                             |
| `TestSubject_NoToken`                             | Subject() without token              | Returns empty string                    |
| `TestScopes`                                      | Retrieve scopes list                 | Returns all token scopes                |
| `TestScopes_NoToken`                              | Scopes() without token               | Returns nil                             |
| `TestRequireScopes_NoToken`                       | RequireScopes without auth           | Returns 401 Unauthorized                |
| `TestRequireScopes_Sufficient`                    | Token has required scopes            | Returns 200 OK, access granted          |
| `TestHasScope_NoToken`                            | HasScope() without token             | Returns false                           |
| `TestErrorTypes`                                  | Error message strings                | All error types return correct messages |
| `TestWithOptions_IntrospectionWithoutClientCreds` | Introspection without client auth    | No Basic auth sent, request succeeds    |

## Related Middlewares

* [oidc](/middlewares/oidc) - OpenID Connect
* [jwt](/middlewares/jwt) - JWT authentication
* [session](/middlewares/session) - Session management
