Fake OAuth 2.0 authorization server · for CI
Your pipeline can't reach your real identity provider. TokenDock issues real RS256-signed JWTs from fake credentials — so the app under test validates tokens with its normal middleware, and your JWT-protected flows finally run in CI.
The problem
Every service you test in CI expects a bearer token signed by your identity provider — which lives behind a VPN, rate-limits you, or simply shouldn't be handed to a build runner. So teams stub out auth, mark those tests skip, and ship JWT-handling code that has never actually been exercised.
TokenDock replaces the identity provider, not your auth code. It speaks enough real OAuth 2.0 — client credentials grant, JWKS, OIDC discovery — that your app validates its tokens exactly as it would in production. No test flags. No mocked middleware. No code changes.
Bill of lading
Add TokenDock to your workflow's services: block or run it anywhere Docker runs. It boots in milliseconds with a built-in demo client, or takes your clients via env vars or YAML.
Your service or test calls POST /token with its client ID and secret — the standard client credentials grant, Basic or form-body auth, RFC 6749 errors and all.
The app under test discovers signing keys from /.well-known/jwks.json via OIDC discovery and verifies the RS256 signature — the same code path it runs in production.
Quickstart
# zero config: demo client included
docker run -p 8080:8080 ghcr.io/iamluisj/tokendock:latest
# fetch a signed JWT
curl -u tokendock:tokendock-secret \
-d grant_type=client_credentials \
http://localhost:8080/token
services:
tokendock:
image: ghcr.io/iamluisj/tokendock:latest
ports: ["8080:8080"]
env:
TOKENDOCK_CLIENT_ID: my-service
TOKENDOCK_CLIENT_SECRET: ci-secret
TOKENDOCK_SCOPES: read,write
TOKENDOCK_AUDIENCE: my-api
Prefer a one-liner? The composite action starts the container, waits for health, and hands your next step the issuer, token-endpoint, and jwks-uri as outputs.
Cargo manifest
Starts with a demo client and a loud warning. Real clients come later; a working token comes in 30 seconds.
Defaults, then YAML file, then env vars — later wins. Single-client setups need no file at all.
Audience, subject, lifetime, and arbitrary claims like roles — so you can test authorization logic, not just authentication.
Client credentials and RFC 8693 token exchange grants, OIDC discovery, JWKS with proper kid, RFC 6749 error responses. Your JWT library already understands it.
A fresh RSA-2048 keypair per start — nothing to leak, nothing to rotate. Mount a PEM if you need reproducibility.
A static Go binary in a bare container — no shell, no OS packages. amd64 + arm64, Docker HEALTHCHECK built into the binary. Works in GitHub Actions, GitLab CI, and docker-compose alike.
Harbor traffic
The established vessel here is navikt/mock-oauth2-server — mature, capable, and the right choice for plenty of crews. Here's an honest manifest of the differences.
| Line item | TokenDock | mock-oauth2-server |
|---|---|---|
| Runtime & image | ~4 MB static Go binary no OS layer | ~200 MB JVM image Kotlin |
| Cold start | Milliseconds | Seconds JVM startup |
| Grant types | Client credentials + token exchange RFC 8693 | Authorization code, token exchange, JWT bearer, refresh & more |
| Interactive login page | None | Yes for browser-driven E2E tests |
| Embed in test code | Container only | JVM library JUnit-friendly |
| Issuers | One per container | Multiple per instance |
| Configuration | Zero-config default, then env vars or YAML | JSON or programmatic API |
| CI wrapper | Composite GitHub Action health-wait + outputs | — |