5e8ee5440f
- 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>
361 lines
9.1 KiB
Markdown
361 lines
9.1 KiB
Markdown
# 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
|