docs: add comprehensive OAuth testing CI/CD guide
- Document GitHub Actions configuration for mock and real OAuth - Add security best practices for OAuth secrets - Include troubleshooting guide - Provide complete workflow examples Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,360 @@
|
||||
# Using Mock OAuth2 Server in CI/CD
|
||||
|
||||
This guide explains how to use the mock OAuth2 server infrastructure in continuous integration pipelines.
|
||||
|
||||
## GitHub Actions Configuration
|
||||
|
||||
### Running with Mock OAuth (Default)
|
||||
|
||||
The tests automatically use mock OAuth by default. No special configuration needed:
|
||||
|
||||
```yaml
|
||||
name: Tests with Mock OAuth
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
- name: Run OAuth tests (mock)
|
||||
run: |
|
||||
pytest tests/test_oauth_integration_flows.py -v
|
||||
```
|
||||
|
||||
### Running with Real OAuth (Using Secrets)
|
||||
|
||||
To test with real OAuth credentials (e.g., Authentik, Auth0):
|
||||
|
||||
```yaml
|
||||
name: Tests with Real OAuth
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test-real-oauth:
|
||||
runs-on: ubuntu-latest
|
||||
# Only run if secrets are available (not on external PRs)
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
- name: Run OAuth tests (real)
|
||||
env:
|
||||
AUTHENTIK_CLIENT_ID: ${{ secrets.AUTHENTIK_CLIENT_ID }}
|
||||
AUTHENTIK_CLIENT_SECRET: ${{ secrets.AUTHENTIK_CLIENT_SECRET }}
|
||||
AUTHENTIK_CONFIG_URL: ${{ secrets.AUTHENTIK_CONFIG_URL }}
|
||||
run: |
|
||||
# Tests auto-detect real credentials and use them
|
||||
pytest tests/test_oauth_integration_flows.py -v -m requires_external
|
||||
```
|
||||
|
||||
### Hybrid Approach (Best Practice)
|
||||
|
||||
Run both mock and real tests in separate jobs:
|
||||
|
||||
```yaml
|
||||
name: OAuth Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test-mock-oauth:
|
||||
name: OAuth Tests (Mock)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
- name: Run mock OAuth tests
|
||||
run: |
|
||||
pytest tests/test_oauth_integration_flows.py \
|
||||
-v \
|
||||
-m "not requires_external"
|
||||
|
||||
test-real-oauth:
|
||||
name: OAuth Tests (Real - Internal Only)
|
||||
runs-on: ubuntu-latest
|
||||
# Only run on internal commits where secrets are available
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
- name: Run real OAuth tests
|
||||
env:
|
||||
AUTHENTIK_CLIENT_ID: ${{ secrets.AUTHENTIK_CLIENT_ID }}
|
||||
AUTHENTIK_CLIENT_SECRET: ${{ secrets.AUTHENTIK_CLIENT_SECRET }}
|
||||
AUTHENTIK_CONFIG_URL: ${{ secrets.AUTHENTIK_CONFIG_URL }}
|
||||
run: |
|
||||
pytest tests/test_oauth_integration_flows.py \
|
||||
-v \
|
||||
-m requires_external
|
||||
```
|
||||
|
||||
## Required GitHub Secrets
|
||||
|
||||
To enable real OAuth testing, configure these secrets in your repository:
|
||||
|
||||
1. Go to **Settings → Secrets and variables → Actions**
|
||||
2. Add the following secrets:
|
||||
|
||||
| Secret Name | Description | Example Value |
|
||||
|-------------|-------------|---------------|
|
||||
| `AUTHENTIK_CLIENT_ID` | OAuth client ID | `docuelevate-app` |
|
||||
| `AUTHENTIK_CLIENT_SECRET` | OAuth client secret | `super-secret-value` |
|
||||
| `AUTHENTIK_CONFIG_URL` | OIDC discovery URL | `https://auth.example.com/application/o/docuelevate/.well-known/openid-configuration` |
|
||||
|
||||
## Docker Service (Alternative to Testcontainers)
|
||||
|
||||
If you prefer not to use testcontainers in CI, you can run mock-oauth2-server as a service:
|
||||
|
||||
```yaml
|
||||
name: Tests with OAuth Service
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
mock-oauth:
|
||||
image: ghcr.io/navikt/mock-oauth2-server:2.1.1
|
||||
ports:
|
||||
- 8080:8080
|
||||
options: >-
|
||||
--health-cmd "wget -q -O /dev/null http://localhost:8080/default/.well-known/openid-configuration || exit 1"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
- name: Configure OAuth to use service
|
||||
run: |
|
||||
export OAUTH_MOCK_URL=http://localhost:8080
|
||||
export USE_MOCK_OAUTH=true
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
pytest tests/test_oauth_integration_flows.py -v
|
||||
```
|
||||
|
||||
## Forcing Mock or Real Mode
|
||||
|
||||
You can override the automatic detection with environment variables:
|
||||
|
||||
```bash
|
||||
# Force mock mode (even if real credentials available)
|
||||
export USE_MOCK_OAUTH=true
|
||||
pytest tests/test_oauth_integration_flows.py -v
|
||||
|
||||
# Force real mode (will skip if credentials not available)
|
||||
export USE_REAL_OAUTH=true
|
||||
pytest tests/test_oauth_integration_flows.py -v
|
||||
```
|
||||
|
||||
## Debugging OAuth Tests in CI
|
||||
|
||||
### View Container Logs
|
||||
|
||||
Add this step to debug mock OAuth server issues:
|
||||
|
||||
```yaml
|
||||
- name: Show mock OAuth logs (on failure)
|
||||
if: failure()
|
||||
run: |
|
||||
docker ps -a
|
||||
docker logs $(docker ps -aq --filter ancestor=ghcr.io/navikt/mock-oauth2-server:2.1.1)
|
||||
```
|
||||
|
||||
### Enable Verbose Logging
|
||||
|
||||
```yaml
|
||||
- name: Run tests with verbose logging
|
||||
run: |
|
||||
pytest tests/test_oauth_integration_flows.py -vvs --log-cli-level=DEBUG
|
||||
```
|
||||
|
||||
### Check Well-Known Endpoint
|
||||
|
||||
```yaml
|
||||
- name: Verify mock OAuth server
|
||||
run: |
|
||||
curl -f http://localhost:8080/default/.well-known/openid-configuration || exit 1
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- **Mock OAuth**: ~1s startup time, tests run in <10s
|
||||
- **Real OAuth**: Depends on network latency, typically <30s
|
||||
- **Docker Service**: Fastest for CI (pre-started), ~0.5s overhead
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. ✅ **Never commit real OAuth credentials** to the repository
|
||||
2. ✅ **Use GitHub secrets** for real credentials
|
||||
3. ✅ **Restrict real OAuth tests** to internal PRs only
|
||||
4. ✅ **Use mock OAuth** for external/fork PRs
|
||||
5. ✅ **Rotate secrets** regularly if compromised
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Tests Skip with "OAuth credentials not available"
|
||||
|
||||
**Cause**: Real OAuth credentials not configured or not accessible.
|
||||
|
||||
**Solution**:
|
||||
- For local dev: Use mock mode (default)
|
||||
- For CI: Add secrets to GitHub repository settings
|
||||
- Check secret availability: `if github.event_name == 'push'`
|
||||
|
||||
### Mock OAuth Server Won't Start
|
||||
|
||||
**Cause**: Docker not available or testcontainers can't start container.
|
||||
|
||||
**Solution**:
|
||||
```yaml
|
||||
- name: Start Docker
|
||||
run: |
|
||||
sudo systemctl start docker
|
||||
docker ps
|
||||
```
|
||||
|
||||
### Tests Timeout Waiting for Server
|
||||
|
||||
**Cause**: Server taking too long to start or health check failing.
|
||||
|
||||
**Solution**: Increase timeout in `conftest_oauth.py`:
|
||||
```python
|
||||
container.wait_for_ready(timeout=60) # Increase from 30
|
||||
```
|
||||
|
||||
## Example: Complete GitHub Actions Workflow
|
||||
|
||||
```yaml
|
||||
name: Full OAuth Testing Suite
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
# Fast mock OAuth tests (always run)
|
||||
mock-oauth-tests:
|
||||
name: OAuth Tests (Mock)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements-dev.txt
|
||||
|
||||
- name: Run mock OAuth tests
|
||||
run: |
|
||||
pytest tests/test_oauth_integration_flows.py \
|
||||
-v \
|
||||
-m "not requires_external" \
|
||||
--cov=app.auth \
|
||||
--cov-report=term-missing
|
||||
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage.xml
|
||||
flags: oauth-mock
|
||||
|
||||
# Real OAuth tests (only for internal PRs/pushes)
|
||||
real-oauth-tests:
|
||||
name: OAuth Tests (Real - Internal)
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install -r requirements-dev.txt
|
||||
|
||||
- name: Run real OAuth tests
|
||||
env:
|
||||
AUTHENTIK_CLIENT_ID: ${{ secrets.AUTHENTIK_CLIENT_ID }}
|
||||
AUTHENTIK_CLIENT_SECRET: ${{ secrets.AUTHENTIK_CLIENT_SECRET }}
|
||||
AUTHENTIK_CONFIG_URL: ${{ secrets.AUTHENTIK_CONFIG_URL }}
|
||||
run: |
|
||||
pytest tests/test_oauth_integration_flows.py \
|
||||
-v \
|
||||
-m requires_external
|
||||
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage.xml
|
||||
flags: oauth-real
|
||||
```
|
||||
|
||||
This workflow:
|
||||
- ✅ Runs mock tests on all PRs (fast, no secrets needed)
|
||||
- ✅ Runs real tests only when secrets available
|
||||
- ✅ Uploads separate coverage reports
|
||||
- ✅ Provides detailed feedback
|
||||
Reference in New Issue
Block a user