Files
gh-christianlouis-docuelevate/tests/README_OAUTH_TESTING.md
T
2026-02-13 09:12:11 +00:00

186 lines
5.8 KiB
Markdown

# OAuth Testing with Mock OAuth2 Server
This directory contains infrastructure for testing OAuth/OIDC authentication flows using a mock OAuth2 server.
## Overview
The test setup supports two modes:
1. **Mock Mode (Default)**: Uses `mock-oauth2-server` via testcontainers for fast, deterministic tests
2. **Real Mode**: Uses actual OAuth credentials from GitHub Actions secrets for integration testing
## Quick Start
### Running Tests with Mock OAuth
```bash
# Run all OAuth integration tests (uses mock by default)
pytest tests/test_oauth_integration_flows.py -v
# Run with coverage
pytest tests/test_oauth_integration_flows.py --cov=app.auth --cov-report=term-missing
```
### Running Tests with Real OAuth (CI/GitHub Actions)
When running in GitHub Actions with secrets configured:
```bash
# Tests automatically detect real credentials and use them
pytest tests/test_oauth_integration_flows.py -v -m requires_external
# Force mock mode even with real credentials available
USE_MOCK_OAUTH=true pytest tests/test_oauth_integration_flows.py -v
# Force real mode (will skip if credentials not available)
USE_REAL_OAUTH=true pytest tests/test_oauth_integration_flows.py -v
```
## Architecture
### Components
1. **mock_oauth_server.py**: Testcontainers wrapper for mock-oauth2-server
- Provides complete OIDC endpoints (.well-known, token, userinfo, JWKS)
- Generates valid JWTs for testing
- Fast startup (<1s), no persistence needed
2. **conftest_oauth.py**: Pytest fixtures for OAuth testing
- `mock_oauth_server`: Session-scoped mock server fixture
- `oauth_config`: OAuth configuration (mock or real)
- `oauth_enabled_app`: Test client with OAuth enabled
- `oauth_test_token`: Generate test JWT tokens
- `test_user_info`: Test user claims
3. **test_oauth_integration_flows.py**: Integration tests
- OAuth login flow
- Token exchange and callback
- Session management
- Error handling
- Real OAuth provider tests (when credentials available)
### Mock OAuth Server
The mock server (https://github.com/navikt/mock-oauth2-server) provides:
- **Authorization endpoint**: `/default/authorize`
- **Token endpoint**: `/default/token`
- **Userinfo endpoint**: `/default/userinfo`
- **JWKS endpoint**: `/default/jwks`
- **Discovery**: `/default/.well-known/openid-configuration`
- **Debug token creation**: `/debugger/token`
## Usage Examples
### Basic OAuth Test
```python
import pytest
@pytest.mark.integration
def test_oauth_login(oauth_enabled_app):
"""Test OAuth login redirects to provider."""
response = oauth_enabled_app.get("/oauth-login", follow_redirects=False)
assert response.status_code == 302
assert "authorize" in response.headers["location"]
```
### Testing with Mock User
```python
from unittest.mock import patch
@pytest.mark.integration
@patch("app.auth.oauth.authentik.authorize_access_token")
async def test_oauth_callback(mock_authorize, oauth_enabled_app, test_user_info):
"""Test OAuth callback with test user."""
mock_authorize.return_value = {
"access_token": "test-token",
"userinfo": test_user_info,
}
response = oauth_enabled_app.get("/oauth-callback?code=test-code")
assert response.status_code == 302 # Redirects after login
```
### Testing with Generated Token
```python
@pytest.mark.integration
def test_with_jwt_token(oauth_test_token, test_user_info):
"""Test with a valid JWT from mock server."""
# oauth_test_token is a valid JWT signed by the mock server
# It can be verified using the mock server's JWKS endpoint
assert oauth_test_token is not None
print(f"Token for user: {test_user_info['email']}")
```
## Configuration
### Environment Variables
- `USE_MOCK_OAUTH=true`: Force mock mode
- `USE_REAL_OAUTH=true`: Force real mode (fails if credentials not available)
- `AUTHENTIK_CLIENT_ID`: OAuth client ID (for real mode)
- `AUTHENTIK_CLIENT_SECRET`: OAuth client secret (for real mode)
- `AUTHENTIK_CONFIG_URL`: OIDC discovery URL (for real mode)
### GitHub Actions Secrets
When these secrets are set in GitHub Actions, tests automatically use real OAuth:
```yaml
# .github/workflows/test.yml
env:
AUTHENTIK_CLIENT_ID: ${{ secrets.AUTHENTIK_CLIENT_ID }}
AUTHENTIK_CLIENT_SECRET: ${{ secrets.AUTHENTIK_CLIENT_SECRET }}
AUTHENTIK_CONFIG_URL: ${{ secrets.AUTHENTIK_CONFIG_URL }}
```
## Troubleshooting
### Mock Server Won't Start
```bash
# Check Docker is running
docker ps
# Pull the image manually
docker pull ghcr.io/navikt/mock-oauth2-server:2.1.1
# Check logs
pytest tests/test_oauth_integration_flows.py -v -s
```
### Tests Hang on Container Startup
The mock server fixture waits up to 30 seconds for the server to be ready. If tests hang:
1. Check Docker resources (CPU, memory)
2. Check if port 8080 is available
3. Try running with `-s` flag to see container logs
### Token Validation Fails
The mock server generates valid JWTs that can be verified using its JWKS endpoint. If validation fails:
1. Ensure the token was created from the correct mock server instance
2. Check the `aud` (audience) claim matches your client ID
3. Verify the `iss` (issuer) claim matches the mock server URL
## Benefits of This Approach
1. **Fast**: Mock server starts in <1s, tests run quickly
2. **Deterministic**: No external dependencies, same results every time
3. **Realistic**: Tests actual OAuth flows with real OIDC endpoints
4. **Flexible**: Can switch to real OAuth for integration tests
5. **CI-Friendly**: Works in ephemeral CI environments
6. **Complete**: All OIDC endpoints available for testing
## Further Reading
- [Mock OAuth2 Server Documentation](https://github.com/navikt/mock-oauth2-server)
- [Testcontainers Python](https://testcontainers-python.readthedocs.io/)
- [OAuth 2.0 RFC 6749](https://tools.ietf.org/html/rfc6749)
- [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html)