Berth 8080 · Manifest № RFC-6749 Moored at github.com/IAmLuisJ/tokendock ↗

Fake OAuth 2.0 authorization server · for CI

Tokendock

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, browser logins included, finally run in CI.

View on GitHub docker run · 30-second setup
Image size
~4 MB no OS, just the binary
Cold start
<100 ms
Config required
None to first token
Runtime deps
Zero one static binary

The problem

Auth is where CI testing goes to die

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, authorization code and refresh token grants, JWKS, userinfo, OIDC discovery — that your app validates its tokens, and runs its login, exactly as it would in production. No test flags. No mocked middleware. No code changes.

Honest by design — TokenDock signs whatever you configure with an ephemeral key that dies with the container. It is deliberately useless outside a test run.

Bill of lading

Three calls, no ceremony

  1. Berth the container

    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.

  2. Request a token

    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.

  3. Validate like production

    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.

Boarding pass · new in v1.6

Browser logins, waved aboard

Point your app's OIDC login at TokenDock like any identity provider. It approves every sign-in on the spot and sends the browser straight back, so Playwright and Cypress tests have nothing to click.

  1. Your app redirects to TokenDock

    The browser lands on /authorize, as it would at your real provider. Send login_hint to choose who signs in.

  2. TokenDock sends it straight back

    No login screen, nothing to click: the browser returns to your callback with a code and your state.

  3. Your backend redeems the code

    A normal POST /token. PKCE verifiers are checked, and each code works once.

  4. Tokens your app already trusts

    An access token, plus an OIDC ID token when you ask for openid. Both verify against the JWKS.

PKCE
S256 or plain
ID tokens
OIDC typ: JWT
SPAs
Public clients with CORS
Redirect URIs
Any or an exact list

Quickstart

Cast off

Anywhere Docker runs
# 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
GitHub Actions workflow
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

What's in the hold

Harbor traffic

Not the only ship in port

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.

Comparative manifest
Line itemTokenDockmock-oauth2-server
Runtime & image~4 MB static Go binary no OS layer~200 MB JVM image Kotlin
Cold startMillisecondsSeconds JVM startup
Grant typesClient credentials, authorization code, refresh token, token exchange, JWT bearer PKCE · OIDC ID tokens · RFC 8693 · RFC 7523Authorization code, token exchange, JWT bearer, refresh & more
Interactive login pageOpt-in by default /authorize approves instantlyYes for browser-driven E2E tests
Embed in test codeContainer onlyJVM library JUnit-friendly
IssuersOne per containerMultiple per instance
ConfigurationZero-config default, then env vars or YAMLJSON or programmatic API
CI wrapperComposite GitHub Action health-wait + outputs—

Sail with TokenDock when…

  • You're testing machine-to-machine bearer tokens — services calling services
  • Your browser login tests just need to land signed in as a known user
  • You want a service container that's ready before your app finishes booting
  • Your stack isn't JVM and a library-mode server buys you nothing
  • You'd rather declare clients in six lines of env vars than maintain a config API

Sail with mock-oauth2-server when…

  • You're on the JVM and want the server embedded in your JUnit lifecycle
  • You need several issuers from one instance