Control plane (/_mock)¶
The /_mock control plane is the out-of-band API for driving mock-oidc from a
test: minting tokens, enqueueing one-shot scenarios, reading captured requests,
and controlling the clock. All request and response bodies are JSON.
- Location — co-located on the API listener (
:8080by default). A dedicated-listener mode moves it to a separate control address. - Enabled — on by default. When disabled, every
/_mock/*route returns404. - Authentication — an optional
X-Mock-Control-Tokenheader gate, active only when a control token is configured. - Response header — every control-plane response carries
X-Mock-Oidc: testing-only.
For testing only
The control plane mints and rewrites tokens for arbitrary identities with no authentication beyond the optional token gate. It must never be exposed on a production surface. See The security model.
Common behavior¶
| Aspect | Value |
|---|---|
| Base path | /_mock |
| Body format | application/json |
| Listener | API listener (:8080) by default; a configured control address moves it to a dedicated listener |
| Enabled by default | Yes (--control-enabled, default true) |
| Disabled behavior | All /_mock/* routes return 404 |
| Auth gate | X-Mock-Control-Token header, required only when --control-token is set; compared in constant time |
| Response header | X-Mock-Oidc: testing-only on every response |
| Error format | RFC 9457 application/problem+json |
Dedicated-listener mode disables request capture
When the control plane runs on its own address, the API listener carries no request-recording middleware and the request-capture endpoints record nothing. Request capture is available only in the default co-located mode. See Lock down the control plane.
The --control-enabled and --control-token settings are documented in the
configuration reference.
Mint¶
Mints a signed token directly, bypassing any grant. The result is byte-identical
to a granted token: it verifies against /{issuer}/jwks and is accepted at
/{issuer}/userinfo. Issuers with the reserved _mock prefix are rejected.
POST /_mock/mint¶
Request
| Field | Type | Description |
|---|---|---|
issuer |
string | Issuer id the token is minted under. Its signing key kid equals this id. Reserved-prefix issuers are rejected. |
issuerUrl |
string (optional) | Explicit issuer URL to stamp as the iss claim, overriding the per-request derived value. |
subject |
string | Value of the sub claim. |
audience |
string[] | Value of the aud claim. |
scope |
string[] | Scopes carried by the token. |
clientId |
string | Client id associated with the token. |
kind |
string | access_token or id_token. |
typ |
string | JWS typ header (for example JWT or at+jwt). |
claims |
object | Additional claims merged into the token. |
expirySeconds |
number | Token lifetime in seconds. |
Response
| Field | Type | Description |
|---|---|---|
token |
string | The signed compact JWS. |
kid |
string | Signing key id (equals the issuer id). |
algorithm |
string | Signing algorithm (for example RS256). |
issuer |
string | Issuer the token was minted under. |
expiresAt |
string | Expiry timestamp. |
claims |
object | The full claim set of the minted token. |
{
"issuer": "default",
"subject": "alice",
"audience": ["my-api"],
"scope": ["read"],
"clientId": "my-app",
"kind": "access_token",
"typ": "at+jwt",
"claims": { "role": "admin" },
"expirySeconds": 3600
}
Claim shaping is covered in Shape token claims; claim semantics are in Tokens and claims.
Scenarios¶
Scenarios enqueue one-shot, issuer-matched token callbacks. A scenario alters
the next matching token for its issuer only, then reverts (single-use). The
refresh_token grant consults the same queue. A scenario body has the same
shape as a config tokenCallbacks entry (see the
configuration reference).
POST /_mock/scenarios¶
Request
| Field | Type | Description |
|---|---|---|
issuer |
string | Issuer the scenario matches. |
subject |
string | Overrides the sub claim. |
audience |
string[] | Overrides the aud claim (an explicit empty list is honored). |
claims |
object | Claims merged into the matched token. |
typ |
string | JWS typ header for the matched token. |
expirySeconds |
number | Token lifetime in seconds. |
requestMappings |
object[] | Per-request conditional overrides (see below). |
Each requestMappings entry:
| Field | Type | Description |
|---|---|---|
param |
string | Request parameter to match on. |
match |
string | * (any), an exact value, or a regular expression. |
typeHeader |
string | JWS typ header applied when the mapping matches. |
claims |
object | Claims applied when the mapping matches. |
Response
| Field | Type | Description |
|---|---|---|
scenarioId |
string | Identifier of the enqueued scenario. |
queueDepth |
number | Number of scenarios in the queue after enqueue. |
GET /_mock/scenarios¶
Lists the queued scenarios non-destructively.
| Field | Type | Description |
|---|---|---|
queueDepth |
number | Number of scenarios currently queued. |
scenarios |
object[] | One entry per queued scenario: { issuer, kind }. |
Each scenarios entry:
| Field | Type | Description |
|---|---|---|
issuer |
string | Issuer the scenario matches. |
kind |
string | default or requestMapping. |
DELETE /_mock/scenarios¶
Clears the scenario queue.
| Field | Type | Description |
|---|---|---|
queueDepth |
number | Always 0. |
Request capture¶
mock-oidc records every inbound protocol request except the routes on the
capture blacklist. These endpoints read the log.
POST /_mock/requests/take¶
A destructive FIFO long-poll. Blocks until a request matches the filter (or the
timeout elapses), then removes and returns the oldest match. On timeout it
returns 404 — a clean miss, not an error.
Request
| Field | Type | Description |
|---|---|---|
timeoutMs |
number | Maximum time to block waiting for a match, in milliseconds. |
issuer |
string | Issuer to match. |
endpoint |
string | Endpoint to match (see the endpoint enum). |
Response — a single CapturedRequest, or 404 on timeout.
GET /_mock/requests¶
A non-destructive snapshot. Both query parameters are optional; omitting them returns every recorded request.
| Query parameter | Description |
|---|---|
issuer |
Filter to a single issuer. |
endpoint |
Filter to a single endpoint. |
Response
| Field | Type | Description |
|---|---|---|
count |
number | Number of requests returned. |
requests |
object[] | An array of CapturedRequest. |
DELETE /_mock/requests¶
Clears the request log.
| Field | Type | Description |
|---|---|---|
cleared |
boolean | Always true. |
CapturedRequest¶
| Field | Type | Description |
|---|---|---|
id |
string | Unique id of the captured request. |
receivedAt |
string | Timestamp the request was received. |
issuer |
string | Issuer the request targeted. |
method |
string | HTTP method. |
path |
string | Request path. |
url |
string | Full request URL. |
query |
object | Query parameters (name to values). |
headers |
object | Request headers (name to values). |
bodyBase64 |
string | Exact raw request body bytes, base64-encoded — preserves order, duplicate keys, and + / % encoding. |
body |
string | Best-effort UTF-8 decode of the body, for convenience. |
Endpoint enum¶
The endpoint field of take and the endpoint query parameter of GET
/_mock/requests accept exactly these values:
| Value | Protocol endpoint |
|---|---|
authorize |
GET/POST /{issuer}/authorize |
token |
POST /{issuer}/token |
userinfo |
GET /{issuer}/userinfo |
introspect |
POST /{issuer}/introspect |
revoke |
POST /{issuer}/revoke |
endsession |
GET/POST /{issuer}/endsession |
jwks |
GET /{issuer}/jwks |
Capture blacklist¶
The recorder never captures the control plane or the operational surface. These routes are always excluded:
/_mock/*/healthz,/readyz,/isalive/metrics/openapi*,/docs/favicon.ico
Request-capture usage is covered in Capture and assert requests.
Clock¶
A single clock drives both token issuance and verification. Freezing or
advancing it can make a previously-valid token introspect active:false and
fail at userinfo.
GET /_mock/clock¶
Reports the current clock state.
| Field | Type | Description |
|---|---|---|
frozen |
boolean | Whether the clock is frozen. |
now |
string | The clock's current instant. |
PUT /_mock/clock¶
Sets the clock state.
| Field | Type | Description |
|---|---|---|
frozen |
boolean | Whether to freeze the clock. |
instant |
string (optional) | The instant to freeze at. Required when frozen is true. |
POST /_mock/clock/advance¶
Freezes the clock, then advances it by a duration.
| Field | Type | Description |
|---|---|---|
duration |
string | A Go duration string, for example 90s, 5m, or 1h1m. |
Time-simulation usage is covered in Simulate expiry and time.
Reset¶
POST /_mock/reset¶
Clears the scenario queue and the request log and unfreezes the clock. Signing keys are preserved, so already-fetched JWKS still verifies. The request body is empty.
| Field | Type | Description |
|---|---|---|
| — | — | Returns an empty success response. |
The full request and response schemas are also published in the API Reference.