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

# ADR-005: OIDC Authentication Architecture

> OIDC-based authentication with FastAPI Security integration

# ADR-005: OIDC Authentication Architecture

## Status

Accepted

## Context

Tasteful applications need secure authentication that supports enterprise identity providers while integrating seamlessly with the async FastAPI architecture and flavor system.

## Decision

We selected **OpenID Connect (OIDC) with FastAPI Security dependencies** for authentication.

### Architecture

```python theme={null}
from tasteful.authn.oidc import OIDCAuthenticationBackend

# OIDC Backend with automatic discovery
oidc_backend = OIDCAuthenticationBackend(
    name="app_auth",
    metadata_url="https://auth.company.com/.well-known/openid_configuration",
    client_id="your-client-id",
    client_secret="your-client-secret",
    scopes="openid profile email roles"
)

# Integration with TastefulApp
app = TastefulApp(
    title="Secure Application",
    authentication_backends=[oidc_backend],  # FastAPI dependencies
    flavors=[UserFlavor]
)
```

## Rationale

### Why OIDC?

* **Industry Standard**: Built on OAuth 2.0, widely supported
* **Enterprise Ready**: Works with Azure AD, Zitadel, Keycloak, Auth0
* **Rich Claims**: Access to user roles, permissions, and attributes
* **Automatic Discovery**: Metadata endpoints for easy configuration

### Why FastAPI Security Dependencies?

* **Native Integration**: Leverages FastAPI's dependency injection
* **Automatic Protection**: All endpoints protected without boilerplate
* **Async Performance**: Non-blocking token validation
* **Easy Testing**: Simple to mock for unit tests

### Authentication Flow

1. Client sends `Authorization: Bearer <token>` header
2. Backend extracts and validates token via OIDC introspection
3. User object created from token claims
4. User available in `request.state.user` for controllers

## Implementation

### Controller Integration

```python theme={null}
class UserController(BaseController):
    @Get("/profile")
    async def get_profile(self, request: Request):
        user: OIDCUser = request.state.user  # Automatically available
        return {"name": user.name, "email": user.user_info.get("email")}
    
    @Get("/admin")
    async def admin_only(self, request: Request):
        user: OIDCUser = request.state.user
        
        # Manual authorization (formal system planned)
        roles = user.user_info.get("roles", [])
        if "admin" not in roles:
            raise HTTPException(status_code=403, detail="Admin access required")
        
        return {"message": "Admin access granted"}
```

### Custom Authentication Backend

```python theme={null}
from tasteful.authn.base import AsyncAuthenticationBackend

class APIKeyBackend(AsyncAuthenticationBackend):
    async def authenticate(self, request: Request) -> BaseUser | None:
        api_key = request.headers.get("X-API-Key")
        if api_key and await self.validate_api_key(api_key):
            return BaseUser(name="api_user")
        return None
```

## Consequences

### Positive

* **Standards Compliance**: OIDC industry standard
* **Enterprise Integration**: Works with existing identity providers
* **Developer Experience**: Minimal boilerplate, automatic user injection
* **Performance**: Async token validation
* **Security**: Real-time token validation and revocation

### Negative

* **External Dependency**: Requires OIDC provider availability
* **Network Latency**: Token introspection adds round-trip
* **Configuration Complexity**: OIDC setup requires OAuth knowledge

## Testing

```python theme={null}
class MockAuthBackend:
    def __init__(self, mock_user: BaseUser):
        self.mock_user = mock_user
    
    async def __call__(self, request):
        request.state.user = self.mock_user

@pytest.fixture
def authenticated_client():
    mock_auth = MockAuthBackend(BaseUser(name="test_user"))
    app = create_app(authentication_backends=[mock_auth])
    return TestClient(app.app)
```

## References

* [OpenID Connect Specification](https://openid.net/connect/)
* [FastAPI Security Documentation](https://fastapi.tiangolo.com/tutorial/security/)
* [OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662)
