Skip to content

DarkAuth Mock

DarkAuth Mock lets you develop an OIDC application without installing, configuring, and running a full DarkAuth instance. It presents a simple identity picker instead of password authentication, then issues realistic signed tokens using users, organizations, roles, and permissions that you define in YAML.

Your application continues to use its normal OIDC integration. Moving between DarkAuth Mock and a real DarkAuth instance should only change environment configuration, not application code.

DarkAuth Mock implements the parts of DarkAuth most applications need during day-to-day development:

  • OAuth 2.0 Authorization Code flow with S256 PKCE.
  • OpenID Connect discovery and EdDSA-signed ID tokens.
  • Access tokens, rotating refresh tokens, and JWKS.
  • Configured or one-off custom identities.
  • Organization membership, switching, roles, and permissions.
  • RP-initiated logout.
  • CORS for configured application origins.

It supports public clients without a client secret. It deliberately does not reproduce confidential-client authentication, password authentication, OPAQUE, MFA, registration, password reset, federation, SCIM, administration, email, or production security controls. Use a real DarkAuth instance when you need to test those behaviors.

Create darkauth-mock.yaml in your application repository:

server:
host: 0.0.0.0
port: 3020
issuer: http://localhost:3020
allowedOrigins:
- http://localhost:3000
- http://localhost:5173
app:
name: Example App
clients:
- example
permissions:
- example:login
- example:admin
roles:
admin:
- example:login
- example:admin
member:
- example:login
organizations:
- { id: acme, slug: acme, name: Acme Corp }
- { id: globex, slug: globex, name: Globex }
users:
- sub: ava
email: ava@example.test
name: Ava Admin
memberships:
- { org: acme, roles: [admin] }
- sub: ben
email: ben@example.test
name: Ben Builder
memberships:
- { org: acme, roles: [member] }
- { org: globex, roles: [member] }

The client ID becomes the token audience. Role permissions and any permissions assigned directly to a membership are combined in the token. A user with multiple memberships can switch organization context without signing in again.

The repository also contains a complete example configuration.

Mount the configuration file:

Terminal window
docker run --rm \
-p 3020:3020 \
-v "$PWD/darkauth-mock.yaml:/config/darkauth-mock.yaml" \
ghcr.io/puzed/darkauth-mock:latest

Open http://localhost:3020 to inspect the configured issuer. The container reads /config/darkauth-mock.yaml by default. When running outside the container, set DARKAUTH_MOCK_CONFIG to the configuration path; if it is omitted, the server starts with a generic built-in identity.

On first launch, the mock generates an Ed25519 signing key and adds it to the YAML as signingKey. Later launches reuse that key, keeping the JWKS stable across container restarts. The mounted configuration must therefore be writable. It contains a private development signing key, so never reuse it for a real identity provider.

Configure your existing OIDC client with:

Setting Local value
Issuer http://localhost:3020
Client ID / audience example
Discovery http://localhost:3020/.well-known/openid-configuration
JWKS http://localhost:3020/.well-known/jwks.json

Start your normal login flow. The browser opens the mock’s authorization screen, where you can select a configured user and organization or expand the custom identity form. After selection, the mock redirects to your existing callback with an authorization code for the normal PKCE exchange.

Tokens contain the selected identity and organization context:

  • sub, email, name, and picture.
  • org_id and org_slug.
  • organizations with all configured memberships.
  • roles and permissions for the active organization.

Your backend should verify issuer, audience, expiry, and signature exactly as it does with real DarkAuth tokens.

A typical application stack can own the YAML file and run the published, app-agnostic image:

services:
app:
environment:
OIDC_ISSUER: http://localhost:3020
OIDC_AUDIENCE: example
OIDC_JWKS_URL: http://darkauth-mock:3020/.well-known/jwks.json
depends_on:
- darkauth-mock
darkauth-mock:
image: ghcr.io/puzed/darkauth-mock:latest
ports:
- "3020:3020"
volumes:
- ./darkauth-mock.yaml:/config/darkauth-mock.yaml

This mirrors the integration used by Atlas. The issuer remains http://localhost:3020 because that is the URL seen by the browser and written into tokens. Its API container uses http://darkauth-mock:3020/.well-known/jwks.json to fetch the same signing keys over the Compose network.

If your OIDC library always obtains the JWKS URL from discovery, make the configured issuer reachable from both the browser and your application container instead of overriding the JWKS URL.

For an isolated preview, set server.issuer to the preview’s externally reachable HTTPS URL, configure the preview application’s origin in allowedOrigins, and keep the configuration file private to that environment. Use only fictional identities and non-sensitive permissions.

The mock intentionally accepts redirect URIs without a production client registry or consent policy. Restrict access to the preview and never treat it as a hardened identity boundary.