MockServer can simulate OAuth2 authorization servers and OpenID Connect providers, enabling you to test OAuth-secured applications without connecting to a real identity provider. This is useful for:

  • Integration testing — verify your application correctly handles token acquisition, refresh, and error scenarios
  • Offline development — develop and test locally without network access to an IdP
  • Fast CI pipelines — eliminate external dependencies and network latency from your test suite
  • Edge case testing — simulate expired tokens, invalid grants, and error responses that are difficult to trigger with a real IdP

The examples below show how to create MockServer expectations that simulate each major OAuth2 flow. All examples use the REST API to create expectations.

 

Client Credentials Flow

The client credentials flow is used for machine-to-machine communication where no user is involved. The client authenticates directly with the authorization server using its client_id and client_secret to obtain an access token.

This is the simplest OAuth2 flow and is commonly used for service-to-service API calls, background jobs, and microservice communication.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withHeader("Content-Type", "application/x-www-form-urlencoded")
            .withBody(
                params(
                    param("grant_type", "client_credentials"),
                    param("client_id", "my-client-id"),
                    param("client_secret", "my-client-secret")
                )
            )
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"access_token\": \"mock-access-token-12345\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read write\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Content-Type": ["application/x-www-form-urlencoded"]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["my-client-id"],
                "client_secret": ["my-client-secret"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-12345\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read write\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Content-Type": ["application/x-www-form-urlencoded"]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["my-client-id"],
                "client_secret": ["my-client-secret"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-12345\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read write\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Content-Type": ["application/x-www-form-urlencoded"]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["my-client-id"],
                "client_secret": ["my-client-secret"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-12345\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read write\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "headers": {
                "Content-Type": ["application/x-www-form-urlencoded"]
            },
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["client_credentials"],
                    "client_id": ["my-client-id"],
                    "client_secret": ["my-client-secret"]
                }
            }
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"access_token\": \"mock-access-token-12345\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read write\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""headers"": {
            ""Content-Type"": [""application/x-www-form-urlencoded""]
        },
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""client_credentials""],
                ""client_id"": [""my-client-id""],
                ""client_secret"": [""my-client-secret""]
            }
        }
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""access_token\"": \""mock-access-token-12345\"", \""token_type\"": \""Bearer\"", \""expires_in\"": 3600, \""scope\"": \""read write\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Content-Type": ["application/x-www-form-urlencoded"]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["my-client-id"],
                "client_secret": ["my-client-secret"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-12345\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read write\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Content-Type": ["application/x-www-form-urlencoded"]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["my-client-id"],
                "client_secret": ["my-client-secret"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-12345\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read write\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Content-Type": ["application/x-www-form-urlencoded"]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["my-client-id"],
                "client_secret": ["my-client-secret"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-12345\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read write\"}"
        }
    }
}'
new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withBody(
                params(
                    param("grant_type", "client_credentials"),
                    param("scope", "read")
                )
            )
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"access_token\": \"mock-read-only-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "scope": ["read"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-read-only-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "scope": ["read"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-read-only-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "scope": ["read"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-read-only-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["client_credentials"],
                    "scope": ["read"]
                }
            }
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"access_token\": \"mock-read-only-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""client_credentials""],
                ""scope"": [""read""]
            }
        }
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""access_token\"": \""mock-read-only-token\"", \""token_type\"": \""Bearer\"", \""expires_in\"": 3600, \""scope\"": \""read\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "scope": ["read"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-read-only-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "scope": ["read"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-read-only-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "scope": ["read"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-read-only-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"scope\": \"read\"}"
        }
    }
}'
new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withHeader("Authorization", "Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ=")
            .withBody(
                params(
                    param("grant_type", "client_credentials")
                )
            )
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"access_token\": \"mock-access-token-basic\", \"token_type\": \"Bearer\", \"expires_in\": 3600}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Authorization": ["Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ="]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-basic\", \"token_type\": \"Bearer\", \"expires_in\": 3600}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Authorization": ["Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ="]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-basic\", \"token_type\": \"Bearer\", \"expires_in\": 3600}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Authorization": ["Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ="]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-basic\", \"token_type\": \"Bearer\", \"expires_in\": 3600}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "headers": {
                "Authorization": ["Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ="]
            },
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["client_credentials"]
                }
            }
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"access_token\": \"mock-access-token-basic\", \"token_type\": \"Bearer\", \"expires_in\": 3600}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""headers"": {
            ""Authorization"": [""Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ=""]
        },
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""client_credentials""]
            }
        }
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""access_token\"": \""mock-access-token-basic\"", \""token_type\"": \""Bearer\"", \""expires_in\"": 3600}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Authorization": ["Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ="]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-basic\", \"token_type\": \"Bearer\", \"expires_in\": 3600}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Authorization": ["Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ="]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-basic\", \"token_type\": \"Bearer\", \"expires_in\": 3600}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "headers": {
            "Authorization": ["Basic bXktY2xpZW50LWlkOm15LWNsaWVudC1zZWNyZXQ="]
        },
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-basic\", \"token_type\": \"Bearer\", \"expires_in\": 3600}"
        }
    }
}'

Some OAuth2 clients send credentials as an HTTP Basic Authorization header (base64(client_id:client_secret)) instead of form parameters.

 

Authorization Code Flow

The authorization code flow is used by web applications and native apps to obtain tokens on behalf of a user. It involves two steps:

  1. The client redirects the user to the authorization server's /authorize endpoint, which authenticates the user and redirects back with an authorization code
  2. The client exchanges the authorization code for tokens at the /token endpoint

To mock this flow, create expectations for both the authorization endpoint and the token endpoint.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("GET")
            .withPath("/authorize")
            .withQueryStringParameter("response_type", "code")
            .withQueryStringParameter("client_id", "my-client-id")
            .withQueryStringParameter("redirect_uri", "https://myapp.example.com/callback")
    )
    .respond(
        template(
            HttpTemplate.TemplateType.JAVASCRIPT,
            "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-auth-code-xyz&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-client-id"],
            "redirect_uri": ["https://myapp.example.com/callback"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-auth-code-xyz&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-client-id"],
            "redirect_uri": ["https://myapp.example.com/callback"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-auth-code-xyz&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-client-id"],
            "redirect_uri": ["https://myapp.example.com/callback"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-auth-code-xyz&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "GET",
            "path": "/authorize",
            "queryStringParameters": {
                "response_type": ["code"],
                "client_id": ["my-client-id"],
                "redirect_uri": ["https://myapp.example.com/callback"]
            }
        },
        "httpResponseTemplate": {
            "templateType": "JAVASCRIPT",
            "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-auth-code-xyz&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""GET"",
        ""path"": ""/authorize"",
        ""queryStringParameters"": {
            ""response_type"": [""code""],
            ""client_id"": [""my-client-id""],
            ""redirect_uri"": [""https://myapp.example.com/callback""]
        }
    },
    ""httpResponseTemplate"": {
        ""templateType"": ""JAVASCRIPT"",
        ""template"": ""return { statusCode: 302, headers: { Location: [\""https://myapp.example.com/callback?code=mock-auth-code-xyz&state=\"" + (request.queryStringParameters[\""state\""] && request.queryStringParameters[\""state\""][0] ? request.queryStringParameters[\""state\""][0] : \""\"")] } };""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-client-id"],
            "redirect_uri": ["https://myapp.example.com/callback"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-auth-code-xyz&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-client-id"],
            "redirect_uri": ["https://myapp.example.com/callback"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-auth-code-xyz&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-client-id"],
            "redirect_uri": ["https://myapp.example.com/callback"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-auth-code-xyz&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}'

The authorization endpoint redirects the user's browser back to the client application's redirect_uri with an authorization code. The state parameter is echoed back using a JavaScript response template that reads from the incoming request's query string parameters, preserving CSRF protection.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withBody(
                params(
                    param("grant_type", "authorization_code"),
                    param("code", "mock-auth-code-xyz"),
                    param("redirect_uri", "https://myapp.example.com/callback"),
                    param("client_id", "my-client-id")
                )
            )
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"access_token\": \"mock-access-token-from-code\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-refresh-token-abc\", \"scope\": \"openid profile email\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-auth-code-xyz"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-from-code\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-refresh-token-abc\", \"scope\": \"openid profile email\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-auth-code-xyz"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-from-code\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-refresh-token-abc\", \"scope\": \"openid profile email\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-auth-code-xyz"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-from-code\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-refresh-token-abc\", \"scope\": \"openid profile email\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["authorization_code"],
                    "code": ["mock-auth-code-xyz"],
                    "redirect_uri": ["https://myapp.example.com/callback"],
                    "client_id": ["my-client-id"]
                }
            }
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"access_token\": \"mock-access-token-from-code\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-refresh-token-abc\", \"scope\": \"openid profile email\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""authorization_code""],
                ""code"": [""mock-auth-code-xyz""],
                ""redirect_uri"": [""https://myapp.example.com/callback""],
                ""client_id"": [""my-client-id""]
            }
        }
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""access_token\"": \""mock-access-token-from-code\"", \""token_type\"": \""Bearer\"", \""expires_in\"": 3600, \""refresh_token\"": \""mock-refresh-token-abc\"", \""scope\"": \""openid profile email\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-auth-code-xyz"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-from-code\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-refresh-token-abc\", \"scope\": \"openid profile email\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-auth-code-xyz"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-from-code\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-refresh-token-abc\", \"scope\": \"openid profile email\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-auth-code-xyz"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-access-token-from-code\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-refresh-token-abc\", \"scope\": \"openid profile email\"}"
        }
    }
}'

This example omits client_secret from the token request. Confidential clients (server-side web apps) must also include client_secret as a form parameter or via HTTP Basic authentication. Public clients (SPAs, mobile apps) omit it.

 

Refresh Token Flow

The refresh token flow allows clients to obtain a new access token without requiring user interaction, using a previously issued refresh token. This is used when access tokens expire and the client needs to maintain a session.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withBody(
                params(
                    param("grant_type", "refresh_token"),
                    param("refresh_token", "mock-refresh-token-abc"),
                    param("client_id", "my-client-id")
                )
            )
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"access_token\": \"mock-new-access-token-67890\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-new-refresh-token-def\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["mock-refresh-token-abc"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-new-access-token-67890\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-new-refresh-token-def\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["mock-refresh-token-abc"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-new-access-token-67890\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-new-refresh-token-def\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["mock-refresh-token-abc"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-new-access-token-67890\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-new-refresh-token-def\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["refresh_token"],
                    "refresh_token": ["mock-refresh-token-abc"],
                    "client_id": ["my-client-id"]
                }
            }
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"access_token\": \"mock-new-access-token-67890\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-new-refresh-token-def\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""refresh_token""],
                ""refresh_token"": [""mock-refresh-token-abc""],
                ""client_id"": [""my-client-id""]
            }
        }
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""access_token\"": \""mock-new-access-token-67890\"", \""token_type\"": \""Bearer\"", \""expires_in\"": 3600, \""refresh_token\"": \""mock-new-refresh-token-def\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["mock-refresh-token-abc"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-new-access-token-67890\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-new-refresh-token-def\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["mock-refresh-token-abc"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-new-access-token-67890\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-new-refresh-token-def\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["mock-refresh-token-abc"],
                "client_id": ["my-client-id"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-new-access-token-67890\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"refresh_token\": \"mock-new-refresh-token-def\"}"
        }
    }
}'

Many authorization servers issue a new refresh token alongside the new access token (refresh token rotation). This example demonstrates that pattern.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withBody(
                params(
                    param("grant_type", "refresh_token"),
                    param("refresh_token", "expired-refresh-token")
                )
            )
    )
    .respond(
        response()
            .withStatusCode(400)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"error\": \"invalid_grant\", \"error_description\": \"The refresh token has expired\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["expired-refresh-token"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The refresh token has expired\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["expired-refresh-token"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The refresh token has expired\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["expired-refresh-token"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The refresh token has expired\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["refresh_token"],
                    "refresh_token": ["expired-refresh-token"]
                }
            }
        },
        "httpResponse": {
            "statusCode": 400,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The refresh token has expired\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""refresh_token""],
                ""refresh_token"": [""expired-refresh-token""]
            }
        }
    },
    ""httpResponse"": {
        ""statusCode"": 400,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""error\"": \""invalid_grant\"", \""error_description\"": \""The refresh token has expired\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["expired-refresh-token"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The refresh token has expired\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["expired-refresh-token"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The refresh token has expired\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["refresh_token"],
                "refresh_token": ["expired-refresh-token"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The refresh token has expired\"}"
        }
    }
}'

Use this pattern to test how your application handles refresh token expiry — typically the user should be redirected to re-authenticate.

 

PKCE Extension

PKCE (Proof Key for Code Exchange, RFC 7636) is an extension to the authorization code flow designed for public clients (single-page apps, mobile apps) that cannot securely store a client secret. The client generates a random code_verifier and sends a derived code_challenge in the authorization request, then proves possession of the verifier when exchanging the code for tokens.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("GET")
            .withPath("/authorize")
            .withQueryStringParameter("response_type", "code")
            .withQueryStringParameter("client_id", "my-spa-client")
            .withQueryStringParameter("redirect_uri", "https://myapp.example.com/callback")
            .withQueryStringParameter("code_challenge_method", "S256")
            .withQueryStringParameter("code_challenge", ".+")
    )
    .respond(
        template(
            HttpTemplate.TemplateType.JAVASCRIPT,
            "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-pkce-auth-code&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-spa-client"],
            "redirect_uri": ["https://myapp.example.com/callback"],
            "code_challenge_method": ["S256"],
            "code_challenge": [".+"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-pkce-auth-code&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-spa-client"],
            "redirect_uri": ["https://myapp.example.com/callback"],
            "code_challenge_method": ["S256"],
            "code_challenge": [".+"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-pkce-auth-code&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-spa-client"],
            "redirect_uri": ["https://myapp.example.com/callback"],
            "code_challenge_method": ["S256"],
            "code_challenge": [".+"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-pkce-auth-code&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "GET",
            "path": "/authorize",
            "queryStringParameters": {
                "response_type": ["code"],
                "client_id": ["my-spa-client"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "code_challenge_method": ["S256"],
                "code_challenge": [".+"]
            }
        },
        "httpResponseTemplate": {
            "templateType": "JAVASCRIPT",
            "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-pkce-auth-code&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""GET"",
        ""path"": ""/authorize"",
        ""queryStringParameters"": {
            ""response_type"": [""code""],
            ""client_id"": [""my-spa-client""],
            ""redirect_uri"": [""https://myapp.example.com/callback""],
            ""code_challenge_method"": [""S256""],
            ""code_challenge"": ["".+""]
        }
    },
    ""httpResponseTemplate"": {
        ""templateType"": ""JAVASCRIPT"",
        ""template"": ""return { statusCode: 302, headers: { Location: [\""https://myapp.example.com/callback?code=mock-pkce-auth-code&state=\"" + (request.queryStringParameters[\""state\""] && request.queryStringParameters[\""state\""][0] ? request.queryStringParameters[\""state\""][0] : \""\"")] } };""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-spa-client"],
            "redirect_uri": ["https://myapp.example.com/callback"],
            "code_challenge_method": ["S256"],
            "code_challenge": [".+"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-pkce-auth-code&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-spa-client"],
            "redirect_uri": ["https://myapp.example.com/callback"],
            "code_challenge_method": ["S256"],
            "code_challenge": [".+"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-pkce-auth-code&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/authorize",
        "queryStringParameters": {
            "response_type": ["code"],
            "client_id": ["my-spa-client"],
            "redirect_uri": ["https://myapp.example.com/callback"],
            "code_challenge_method": ["S256"],
            "code_challenge": [".+"]
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 302, headers: { Location: [\"https://myapp.example.com/callback?code=mock-pkce-auth-code&state=\" + (request.queryStringParameters[\"state\"] && request.queryStringParameters[\"state\"][0] ? request.queryStringParameters[\"state\"][0] : \"\")] } };"
    }
}'

The code_challenge parameter is matched with a regex (.+) since it will be a different Base64url-encoded hash each time. The state parameter is echoed back using a JavaScript response template. In a real flow, the authorization server validates the challenge against the verifier during token exchange.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withBody(
                params(
                    param("grant_type", "authorization_code"),
                    param("code", "mock-pkce-auth-code"),
                    param("redirect_uri", "https://myapp.example.com/callback"),
                    param("client_id", "my-spa-client"),
                    param("code_verifier", ".+")
                )
            )
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"access_token\": \"mock-pkce-access-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"id_token\": \"mock-id-token-jwt\", \"scope\": \"openid profile\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-pkce-auth-code"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-spa-client"],
                "code_verifier": [".+"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-pkce-access-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"id_token\": \"mock-id-token-jwt\", \"scope\": \"openid profile\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-pkce-auth-code"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-spa-client"],
                "code_verifier": [".+"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-pkce-access-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"id_token\": \"mock-id-token-jwt\", \"scope\": \"openid profile\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-pkce-auth-code"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-spa-client"],
                "code_verifier": [".+"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-pkce-access-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"id_token\": \"mock-id-token-jwt\", \"scope\": \"openid profile\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["authorization_code"],
                    "code": ["mock-pkce-auth-code"],
                    "redirect_uri": ["https://myapp.example.com/callback"],
                    "client_id": ["my-spa-client"],
                    "code_verifier": [".+"]
                }
            }
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"access_token\": \"mock-pkce-access-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"id_token\": \"mock-id-token-jwt\", \"scope\": \"openid profile\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""authorization_code""],
                ""code"": [""mock-pkce-auth-code""],
                ""redirect_uri"": [""https://myapp.example.com/callback""],
                ""client_id"": [""my-spa-client""],
                ""code_verifier"": ["".+""]
            }
        }
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""access_token\"": \""mock-pkce-access-token\"", \""token_type\"": \""Bearer\"", \""expires_in\"": 3600, \""id_token\"": \""mock-id-token-jwt\"", \""scope\"": \""openid profile\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-pkce-auth-code"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-spa-client"],
                "code_verifier": [".+"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-pkce-access-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"id_token\": \"mock-id-token-jwt\", \"scope\": \"openid profile\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-pkce-auth-code"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-spa-client"],
                "code_verifier": [".+"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-pkce-access-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"id_token\": \"mock-id-token-jwt\", \"scope\": \"openid profile\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["mock-pkce-auth-code"],
                "redirect_uri": ["https://myapp.example.com/callback"],
                "client_id": ["my-spa-client"],
                "code_verifier": [".+"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"access_token\": \"mock-pkce-access-token\", \"token_type\": \"Bearer\", \"expires_in\": 3600, \"id_token\": \"mock-id-token-jwt\", \"scope\": \"openid profile\"}"
        }
    }
}'

The code_verifier is matched with a regex since MockServer does not need to verify the PKCE proof — it just needs to accept the token exchange request. Note that PKCE flows for public clients do not include a client_secret.

 

Dynamic Token Responses with Response Templates

The examples above return static token values. For more realistic testing — especially when running multiple tests in parallel — you can use response templates to generate unique tokens for each request.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withBody(
                params(
                    param("grant_type", "client_credentials")
                )
            )
    )
    .respond(
        template(
            HttpTemplate.TemplateType.MUSTACHE,
            "{\"statusCode\": 200, \"headers\": {\"Content-Type\": [\"application/json\"]}, \"body\": \"{\\\"access_token\\\": \\\"{{uuid}}\\\", \\\"token_type\\\": \\\"Bearer\\\", \\\"expires_in\\\": 3600, \\\"scope\\\": \\\"read write\\\"}\"}"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "MUSTACHE",
        "template": "{\"statusCode\": 200, \"headers\": {\"Content-Type\": [\"application/json\"]}, \"body\": \"{\\\"access_token\\\": \\\"{{uuid}}\\\", \\\"token_type\\\": \\\"Bearer\\\", \\\"expires_in\\\": 3600, \\\"scope\\\": \\\"read write\\\"}\"}"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "MUSTACHE",
        "template": "{\"statusCode\": 200, \"headers\": {\"Content-Type\": [\"application/json\"]}, \"body\": \"{\\\"access_token\\\": \\\"{{uuid}}\\\", \\\"token_type\\\": \\\"Bearer\\\", \\\"expires_in\\\": 3600, \\\"scope\\\": \\\"read write\\\"}\"}"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "MUSTACHE",
        "template": "{\"statusCode\": 200, \"headers\": {\"Content-Type\": [\"application/json\"]}, \"body\": \"{\\\"access_token\\\": \\\"{{uuid}}\\\", \\\"token_type\\\": \\\"Bearer\\\", \\\"expires_in\\\": 3600, \\\"scope\\\": \\\"read write\\\"}\"}"
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["client_credentials"]
                }
            }
        },
        "httpResponseTemplate": {
            "templateType": "MUSTACHE",
            "template": "{\"statusCode\": 200, \"headers\": {\"Content-Type\": [\"application/json\"]}, \"body\": \"{\\\"access_token\\\": \\\"{{uuid}}\\\", \\\"token_type\\\": \\\"Bearer\\\", \\\"expires_in\\\": 3600, \\\"scope\\\": \\\"read write\\\"}\"}"
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""client_credentials""]
            }
        }
    },
    ""httpResponseTemplate"": {
        ""templateType"": ""MUSTACHE"",
        ""template"": ""{\""statusCode\"": 200, \""headers\"": {\""Content-Type\"": [\""application/json\""]}, \""body\"": \""{\\\""access_token\\\"": \\\""{{uuid}}\\\"", \\\""token_type\\\"": \\\""Bearer\\\"", \\\""expires_in\\\"": 3600, \\\""scope\\\"": \\\""read write\\\""}\""}""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "MUSTACHE",
        "template": "{\"statusCode\": 200, \"headers\": {\"Content-Type\": [\"application/json\"]}, \"body\": \"{\\\"access_token\\\": \\\"{{uuid}}\\\", \\\"token_type\\\": \\\"Bearer\\\", \\\"expires_in\\\": 3600, \\\"scope\\\": \\\"read write\\\"}\"}"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "MUSTACHE",
        "template": "{\"statusCode\": 200, \"headers\": {\"Content-Type\": [\"application/json\"]}, \"body\": \"{\\\"access_token\\\": \\\"{{uuid}}\\\", \\\"token_type\\\": \\\"Bearer\\\", \\\"expires_in\\\": 3600, \\\"scope\\\": \\\"read write\\\"}\"}"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "MUSTACHE",
        "template": "{\"statusCode\": 200, \"headers\": {\"Content-Type\": [\"application/json\"]}, \"body\": \"{\\\"access_token\\\": \\\"{{uuid}}\\\", \\\"token_type\\\": \\\"Bearer\\\", \\\"expires_in\\\": 3600, \\\"scope\\\": \\\"read write\\\"}\"}"
    }
}'

Each request receives a unique access_token generated by the {{uuid}} built-in template variable.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withBody(
                params(
                    param("grant_type", "client_credentials")
                )
            )
    )
    .respond(
        template(
            HttpTemplate.TemplateType.JAVASCRIPT,
            "return { statusCode: 200, headers: { \"Content-Type\": [\"application/json\"] }, body: JSON.stringify({ access_token: uuid, token_type: \"Bearer\", expires_in: 3600, issued_at: parseInt(now_epoch), scope: \"read write\" }) };"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 200, headers: { \"Content-Type\": [\"application/json\"] }, body: JSON.stringify({ access_token: uuid, token_type: \"Bearer\", expires_in: 3600, issued_at: parseInt(now_epoch), scope: \"read write\" }) };"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 200, headers: { \"Content-Type\": [\"application/json\"] }, body: JSON.stringify({ access_token: uuid, token_type: \"Bearer\", expires_in: 3600, issued_at: parseInt(now_epoch), scope: \"read write\" }) };"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 200, headers: { \"Content-Type\": [\"application/json\"] }, body: JSON.stringify({ access_token: uuid, token_type: \"Bearer\", expires_in: 3600, issued_at: parseInt(now_epoch), scope: \"read write\" }) };"
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["client_credentials"]
                }
            }
        },
        "httpResponseTemplate": {
            "templateType": "JAVASCRIPT",
            "template": "return { statusCode: 200, headers: { \"Content-Type\": [\"application/json\"] }, body: JSON.stringify({ access_token: uuid, token_type: \"Bearer\", expires_in: 3600, issued_at: parseInt(now_epoch), scope: \"read write\" }) };"
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""client_credentials""]
            }
        }
    },
    ""httpResponseTemplate"": {
        ""templateType"": ""JAVASCRIPT"",
        ""template"": ""return { statusCode: 200, headers: { \""Content-Type\"": [\""application/json\""] }, body: JSON.stringify({ access_token: uuid, token_type: \""Bearer\"", expires_in: 3600, issued_at: parseInt(now_epoch), scope: \""read write\"" }) };""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 200, headers: { \"Content-Type\": [\"application/json\"] }, body: JSON.stringify({ access_token: uuid, token_type: \"Bearer\", expires_in: 3600, issued_at: parseInt(now_epoch), scope: \"read write\" }) };"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 200, headers: { \"Content-Type\": [\"application/json\"] }, body: JSON.stringify({ access_token: uuid, token_type: \"Bearer\", expires_in: 3600, issued_at: parseInt(now_epoch), scope: \"read write\" }) };"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"]
            }
        }
    },
    "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 200, headers: { \"Content-Type\": [\"application/json\"] }, body: JSON.stringify({ access_token: uuid, token_type: \"Bearer\", expires_in: 3600, issued_at: parseInt(now_epoch), scope: \"read write\" }) };"
    }
}'

JavaScript templates provide access to built-in template variables like uuid and now_epoch for generating dynamic values. The issued_at field contains the epoch time when the token was issued.

 

Matching Protected Resources by Bearer Token

After obtaining an access token, clients include it in the Authorization header when calling protected APIs. You can mock these protected endpoints to accept any bearer token, require a specific token, or reject missing/invalid tokens.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("GET")
            .withPath("/api/resource")
            .withHeader("Authorization", "Bearer .+")
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"id\": 1, \"name\": \"Protected Resource\", \"description\": \"This resource requires authentication\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\", \"description\": \"This resource requires authentication\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\", \"description\": \"This resource requires authentication\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\", \"description\": \"This resource requires authentication\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "GET",
            "path": "/api/resource",
            "headers": {
                "Authorization": ["Bearer .+"]
            }
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"id\": 1, \"name\": \"Protected Resource\", \"description\": \"This resource requires authentication\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""GET"",
        ""path"": ""/api/resource"",
        ""headers"": {
            ""Authorization"": [""Bearer .+""]
        }
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""id\"": 1, \""name\"": \""Protected Resource\"", \""description\"": \""This resource requires authentication\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\", \"description\": \"This resource requires authentication\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\", \"description\": \"This resource requires authentication\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\", \"description\": \"This resource requires authentication\"}"
        }
    }
}'

The regex Bearer .+ matches any non-empty bearer token value.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("GET")
            .withPath("/api/resource")
            .withHeader("Authorization", "Bearer mock-access-token-12345")
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"id\": 1, \"name\": \"Protected Resource\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer mock-access-token-12345"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer mock-access-token-12345"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer mock-access-token-12345"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "GET",
            "path": "/api/resource",
            "headers": {
                "Authorization": ["Bearer mock-access-token-12345"]
            }
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"id\": 1, \"name\": \"Protected Resource\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""GET"",
        ""path"": ""/api/resource"",
        ""headers"": {
            ""Authorization"": [""Bearer mock-access-token-12345""]
        }
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""id\"": 1, \""name\"": \""Protected Resource\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer mock-access-token-12345"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer mock-access-token-12345"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "Authorization": ["Bearer mock-access-token-12345"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"id\": 1, \"name\": \"Protected Resource\"}"
        }
    }
}'

Match a specific token to ensure your application sends the correct token obtained from the mock token endpoint.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("GET")
            .withPath("/api/resource")
            .withHeaders(
                // negate the header NAME so this matches requests WITHOUT a valid Bearer token
                header(string("!Authorization"), string("Bearer .+"))
            )
    )
    .respond(
        response()
            .withStatusCode(401)
            .withHeader("Content-Type", "application/json")
            .withHeader("WWW-Authenticate", "Bearer realm=\"mockserver\", error=\"invalid_token\"")
            .withBody(json("{\"error\": \"unauthorized\", \"error_description\": \"Access token is missing or invalid\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "!Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"invalid_token\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unauthorized\", \"error_description\": \"Access token is missing or invalid\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "!Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"invalid_token\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unauthorized\", \"error_description\": \"Access token is missing or invalid\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "!Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"invalid_token\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unauthorized\", \"error_description\": \"Access token is missing or invalid\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "GET",
            "path": "/api/resource",
            "headers": {
                "!Authorization": ["Bearer .+"]
            }
        },
        "httpResponse": {
            "statusCode": 401,
            "headers": {
                "Content-Type": ["application/json"],
                "WWW-Authenticate": [
                    "Bearer realm=\"mockserver\", error=\"invalid_token\""
                ]
            },
            "body": {
                "type": "JSON",
                "json": "{\"error\": \"unauthorized\", \"error_description\": \"Access token is missing or invalid\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""GET"",
        ""path"": ""/api/resource"",
        ""headers"": {
            ""!Authorization"": [""Bearer .+""]
        }
    },
    ""httpResponse"": {
        ""statusCode"": 401,
        ""headers"": {
            ""Content-Type"": [""application/json""],
            ""WWW-Authenticate"": [
                ""Bearer realm=\""mockserver\"", error=\""invalid_token\""""
            ]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""error\"": \""unauthorized\"", \""error_description\"": \""Access token is missing or invalid\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "!Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"invalid_token\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unauthorized\", \"error_description\": \"Access token is missing or invalid\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "!Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"invalid_token\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unauthorized\", \"error_description\": \"Access token is missing or invalid\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/api/resource",
        "headers": {
            "!Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"invalid_token\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unauthorized\", \"error_description\": \"Access token is missing or invalid\"}"
        }
    }
}'

The ! prefix on the header name creates a negative matcher — this expectation matches requests that do not have a valid Bearer token. Use this with a lower priority to create a catch-all 401 response alongside a higher-priority expectation that matches valid tokens.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("DELETE")
            .withPath("/api/admin/resource")
            .withHeader("Authorization", "Bearer mock-read-only-token")
    )
    .respond(
        response()
            .withStatusCode(403)
            .withHeader("Content-Type", "application/json")
            .withHeader("WWW-Authenticate", "Bearer realm=\"mockserver\", error=\"insufficient_scope\", scope=\"admin\"")
            .withBody(json("{\"error\": \"forbidden\", \"error_description\": \"Insufficient scope for this resource\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "DELETE",
        "path": "/api/admin/resource",
        "headers": {
            "Authorization": ["Bearer mock-read-only-token"]
        }
    },
    "httpResponse": {
        "statusCode": 403,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"insufficient_scope\", scope=\"admin\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"forbidden\", \"error_description\": \"Insufficient scope for this resource\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "DELETE",
        "path": "/api/admin/resource",
        "headers": {
            "Authorization": ["Bearer mock-read-only-token"]
        }
    },
    "httpResponse": {
        "statusCode": 403,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"insufficient_scope\", scope=\"admin\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"forbidden\", \"error_description\": \"Insufficient scope for this resource\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "DELETE",
        "path": "/api/admin/resource",
        "headers": {
            "Authorization": ["Bearer mock-read-only-token"]
        }
    },
    "httpResponse": {
        "statusCode": 403,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"insufficient_scope\", scope=\"admin\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"forbidden\", \"error_description\": \"Insufficient scope for this resource\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "DELETE",
            "path": "/api/admin/resource",
            "headers": {
                "Authorization": ["Bearer mock-read-only-token"]
            }
        },
        "httpResponse": {
            "statusCode": 403,
            "headers": {
                "Content-Type": ["application/json"],
                "WWW-Authenticate": [
                    "Bearer realm=\"mockserver\", error=\"insufficient_scope\", scope=\"admin\""
                ]
            },
            "body": {
                "type": "JSON",
                "json": "{\"error\": \"forbidden\", \"error_description\": \"Insufficient scope for this resource\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""DELETE"",
        ""path"": ""/api/admin/resource"",
        ""headers"": {
            ""Authorization"": [""Bearer mock-read-only-token""]
        }
    },
    ""httpResponse"": {
        ""statusCode"": 403,
        ""headers"": {
            ""Content-Type"": [""application/json""],
            ""WWW-Authenticate"": [
                ""Bearer realm=\""mockserver\"", error=\""insufficient_scope\"", scope=\""admin\""""
            ]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""error\"": \""forbidden\"", \""error_description\"": \""Insufficient scope for this resource\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "DELETE",
        "path": "/api/admin/resource",
        "headers": {
            "Authorization": ["Bearer mock-read-only-token"]
        }
    },
    "httpResponse": {
        "statusCode": 403,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"insufficient_scope\", scope=\"admin\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"forbidden\", \"error_description\": \"Insufficient scope for this resource\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "DELETE",
        "path": "/api/admin/resource",
        "headers": {
            "Authorization": ["Bearer mock-read-only-token"]
        }
    },
    "httpResponse": {
        "statusCode": 403,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"insufficient_scope\", scope=\"admin\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"forbidden\", \"error_description\": \"Insufficient scope for this resource\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "DELETE",
        "path": "/api/admin/resource",
        "headers": {
            "Authorization": ["Bearer mock-read-only-token"]
        }
    },
    "httpResponse": {
        "statusCode": 403,
        "headers": {
            "Content-Type": ["application/json"],
            "WWW-Authenticate": [
                "Bearer realm=\"mockserver\", error=\"insufficient_scope\", scope=\"admin\""
            ]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"forbidden\", \"error_description\": \"Insufficient scope for this resource\"}"
        }
    }
}'

Simulate a scenario where the access token is valid but does not have the required scope for the requested operation.

 

OpenID Connect Discovery

OpenID Connect (OIDC) clients typically start by fetching the provider's discovery document from /.well-known/openid-configuration to learn the authorization, token, and JWKS endpoint URLs. Many OAuth2 client libraries require this endpoint to be available during initialization.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("GET")
            .withPath("/.well-known/openid-configuration")
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"issuer\": \"http://localhost:1080\", \"authorization_endpoint\": \"http://localhost:1080/authorize\", \"token_endpoint\": \"http://localhost:1080/oauth/token\", \"userinfo_endpoint\": \"http://localhost:1080/userinfo\", \"jwks_uri\": \"http://localhost:1080/.well-known/jwks.json\", \"response_types_supported\": [\"code\", \"token\", \"id_token\"], \"subject_types_supported\": [\"public\"], \"id_token_signing_alg_values_supported\": [\"RS256\"], \"scopes_supported\": [\"openid\", \"profile\", \"email\"], \"token_endpoint_auth_methods_supported\": [\"client_secret_basic\", \"client_secret_post\"], \"grant_types_supported\": [\"authorization_code\", \"client_credentials\", \"refresh_token\"]}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/openid-configuration"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"issuer\": \"http://localhost:1080\", \"authorization_endpoint\": \"http://localhost:1080/authorize\", \"token_endpoint\": \"http://localhost:1080/oauth/token\", \"userinfo_endpoint\": \"http://localhost:1080/userinfo\", \"jwks_uri\": \"http://localhost:1080/.well-known/jwks.json\", \"response_types_supported\": [\"code\", \"token\", \"id_token\"], \"subject_types_supported\": [\"public\"], \"id_token_signing_alg_values_supported\": [\"RS256\"], \"scopes_supported\": [\"openid\", \"profile\", \"email\"], \"token_endpoint_auth_methods_supported\": [\"client_secret_basic\", \"client_secret_post\"], \"grant_types_supported\": [\"authorization_code\", \"client_credentials\", \"refresh_token\"]}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/openid-configuration"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"issuer\": \"http://localhost:1080\", \"authorization_endpoint\": \"http://localhost:1080/authorize\", \"token_endpoint\": \"http://localhost:1080/oauth/token\", \"userinfo_endpoint\": \"http://localhost:1080/userinfo\", \"jwks_uri\": \"http://localhost:1080/.well-known/jwks.json\", \"response_types_supported\": [\"code\", \"token\", \"id_token\"], \"subject_types_supported\": [\"public\"], \"id_token_signing_alg_values_supported\": [\"RS256\"], \"scopes_supported\": [\"openid\", \"profile\", \"email\"], \"token_endpoint_auth_methods_supported\": [\"client_secret_basic\", \"client_secret_post\"], \"grant_types_supported\": [\"authorization_code\", \"client_credentials\", \"refresh_token\"]}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/openid-configuration"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"issuer\": \"http://localhost:1080\", \"authorization_endpoint\": \"http://localhost:1080/authorize\", \"token_endpoint\": \"http://localhost:1080/oauth/token\", \"userinfo_endpoint\": \"http://localhost:1080/userinfo\", \"jwks_uri\": \"http://localhost:1080/.well-known/jwks.json\", \"response_types_supported\": [\"code\", \"token\", \"id_token\"], \"subject_types_supported\": [\"public\"], \"id_token_signing_alg_values_supported\": [\"RS256\"], \"scopes_supported\": [\"openid\", \"profile\", \"email\"], \"token_endpoint_auth_methods_supported\": [\"client_secret_basic\", \"client_secret_post\"], \"grant_types_supported\": [\"authorization_code\", \"client_credentials\", \"refresh_token\"]}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "GET",
            "path": "/.well-known/openid-configuration"
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"issuer\": \"http://localhost:1080\", \"authorization_endpoint\": \"http://localhost:1080/authorize\", \"token_endpoint\": \"http://localhost:1080/oauth/token\", \"userinfo_endpoint\": \"http://localhost:1080/userinfo\", \"jwks_uri\": \"http://localhost:1080/.well-known/jwks.json\", \"response_types_supported\": [\"code\", \"token\", \"id_token\"], \"subject_types_supported\": [\"public\"], \"id_token_signing_alg_values_supported\": [\"RS256\"], \"scopes_supported\": [\"openid\", \"profile\", \"email\"], \"token_endpoint_auth_methods_supported\": [\"client_secret_basic\", \"client_secret_post\"], \"grant_types_supported\": [\"authorization_code\", \"client_credentials\", \"refresh_token\"]}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""GET"",
        ""path"": ""/.well-known/openid-configuration""
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""issuer\"": \""http://localhost:1080\"", \""authorization_endpoint\"": \""http://localhost:1080/authorize\"", \""token_endpoint\"": \""http://localhost:1080/oauth/token\"", \""userinfo_endpoint\"": \""http://localhost:1080/userinfo\"", \""jwks_uri\"": \""http://localhost:1080/.well-known/jwks.json\"", \""response_types_supported\"": [\""code\"", \""token\"", \""id_token\""], \""subject_types_supported\"": [\""public\""], \""id_token_signing_alg_values_supported\"": [\""RS256\""], \""scopes_supported\"": [\""openid\"", \""profile\"", \""email\""], \""token_endpoint_auth_methods_supported\"": [\""client_secret_basic\"", \""client_secret_post\""], \""grant_types_supported\"": [\""authorization_code\"", \""client_credentials\"", \""refresh_token\""]}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/openid-configuration"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"issuer\": \"http://localhost:1080\", \"authorization_endpoint\": \"http://localhost:1080/authorize\", \"token_endpoint\": \"http://localhost:1080/oauth/token\", \"userinfo_endpoint\": \"http://localhost:1080/userinfo\", \"jwks_uri\": \"http://localhost:1080/.well-known/jwks.json\", \"response_types_supported\": [\"code\", \"token\", \"id_token\"], \"subject_types_supported\": [\"public\"], \"id_token_signing_alg_values_supported\": [\"RS256\"], \"scopes_supported\": [\"openid\", \"profile\", \"email\"], \"token_endpoint_auth_methods_supported\": [\"client_secret_basic\", \"client_secret_post\"], \"grant_types_supported\": [\"authorization_code\", \"client_credentials\", \"refresh_token\"]}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/openid-configuration"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"issuer\": \"http://localhost:1080\", \"authorization_endpoint\": \"http://localhost:1080/authorize\", \"token_endpoint\": \"http://localhost:1080/oauth/token\", \"userinfo_endpoint\": \"http://localhost:1080/userinfo\", \"jwks_uri\": \"http://localhost:1080/.well-known/jwks.json\", \"response_types_supported\": [\"code\", \"token\", \"id_token\"], \"subject_types_supported\": [\"public\"], \"id_token_signing_alg_values_supported\": [\"RS256\"], \"scopes_supported\": [\"openid\", \"profile\", \"email\"], \"token_endpoint_auth_methods_supported\": [\"client_secret_basic\", \"client_secret_post\"], \"grant_types_supported\": [\"authorization_code\", \"client_credentials\", \"refresh_token\"]}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/openid-configuration"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"issuer\": \"http://localhost:1080\", \"authorization_endpoint\": \"http://localhost:1080/authorize\", \"token_endpoint\": \"http://localhost:1080/oauth/token\", \"userinfo_endpoint\": \"http://localhost:1080/userinfo\", \"jwks_uri\": \"http://localhost:1080/.well-known/jwks.json\", \"response_types_supported\": [\"code\", \"token\", \"id_token\"], \"subject_types_supported\": [\"public\"], \"id_token_signing_alg_values_supported\": [\"RS256\"], \"scopes_supported\": [\"openid\", \"profile\", \"email\"], \"token_endpoint_auth_methods_supported\": [\"client_secret_basic\", \"client_secret_post\"], \"grant_types_supported\": [\"authorization_code\", \"client_credentials\", \"refresh_token\"]}"
        }
    }
}'

The discovery document tells OIDC clients where to find all the other endpoints. All URLs point to MockServer (localhost:1080) so the client library interacts entirely with the mock.

Tip: if your application actually verifies JWT signatures, the easiest path is the turnkey OpenID Connect mock provider — it generates a real key pair and signs verifiable tokens for you, so you don't have to hand-roll a matching JWKS and signed tokens. The hand-rolled recipe below is for cases where you only need a static placeholder key.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("GET")
            .withPath("/.well-known/jwks.json")
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"keys\": [{\"kty\": \"RSA\", \"use\": \"sig\", \"kid\": \"mock-key-1\", \"alg\": \"RS256\", \"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\", \"e\": \"AQAB\"}]}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/jwks.json"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"keys\": [{\"kty\": \"RSA\", \"use\": \"sig\", \"kid\": \"mock-key-1\", \"alg\": \"RS256\", \"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\", \"e\": \"AQAB\"}]}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/jwks.json"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"keys\": [{\"kty\": \"RSA\", \"use\": \"sig\", \"kid\": \"mock-key-1\", \"alg\": \"RS256\", \"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\", \"e\": \"AQAB\"}]}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/jwks.json"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"keys\": [{\"kty\": \"RSA\", \"use\": \"sig\", \"kid\": \"mock-key-1\", \"alg\": \"RS256\", \"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\", \"e\": \"AQAB\"}]}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "GET",
            "path": "/.well-known/jwks.json"
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"keys\": [{\"kty\": \"RSA\", \"use\": \"sig\", \"kid\": \"mock-key-1\", \"alg\": \"RS256\", \"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\", \"e\": \"AQAB\"}]}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""GET"",
        ""path"": ""/.well-known/jwks.json""
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""keys\"": [{\""kty\"": \""RSA\"", \""use\"": \""sig\"", \""kid\"": \""mock-key-1\"", \""alg\"": \""RS256\"", \""n\"": \""0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\"", \""e\"": \""AQAB\""}]}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/jwks.json"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"keys\": [{\"kty\": \"RSA\", \"use\": \"sig\", \"kid\": \"mock-key-1\", \"alg\": \"RS256\", \"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\", \"e\": \"AQAB\"}]}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/jwks.json"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"keys\": [{\"kty\": \"RSA\", \"use\": \"sig\", \"kid\": \"mock-key-1\", \"alg\": \"RS256\", \"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\", \"e\": \"AQAB\"}]}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/.well-known/jwks.json"
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"keys\": [{\"kty\": \"RSA\", \"use\": \"sig\", \"kid\": \"mock-key-1\", \"alg\": \"RS256\", \"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\", \"e\": \"AQAB\"}]}"
        }
    }
}'

The JWKS endpoint exposes public keys that OIDC clients use to verify the signature of ID tokens. For testing purposes you can use a placeholder RSA key — unless your application actually validates JWT signatures, in which case you should generate a real key pair and sign your mock ID tokens with the corresponding private key.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("GET")
            .withPath("/userinfo")
            .withHeader("Authorization", "Bearer .+")
    )
    .respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"sub\": \"mock-user-123\", \"name\": \"Test User\", \"email\": \"test@example.com\", \"email_verified\": true, \"picture\": \"https://example.com/photo.jpg\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "GET",
        "path": "/userinfo",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"sub\": \"mock-user-123\", \"name\": \"Test User\", \"email\": \"test@example.com\", \"email_verified\": true, \"picture\": \"https://example.com/photo.jpg\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "GET",
        "path": "/userinfo",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"sub\": \"mock-user-123\", \"name\": \"Test User\", \"email\": \"test@example.com\", \"email_verified\": True, \"picture\": \"https://example.com/photo.jpg\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "GET",
        "path": "/userinfo",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"sub\": \"mock-user-123\", \"name\": \"Test User\", \"email\": \"test@example.com\", \"email_verified\": true, \"picture\": \"https://example.com/photo.jpg\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "GET",
            "path": "/userinfo",
            "headers": {
                "Authorization": ["Bearer .+"]
            }
        },
        "httpResponse": {
            "statusCode": 200,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"sub\": \"mock-user-123\", \"name\": \"Test User\", \"email\": \"test@example.com\", \"email_verified\": true, \"picture\": \"https://example.com/photo.jpg\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""GET"",
        ""path"": ""/userinfo"",
        ""headers"": {
            ""Authorization"": [""Bearer .+""]
        }
    },
    ""httpResponse"": {
        ""statusCode"": 200,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""sub\"": \""mock-user-123\"", \""name\"": \""Test User\"", \""email\"": \""test@example.com\"", \""email_verified\"": true, \""picture\"": \""https://example.com/photo.jpg\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "GET",
        "path": "/userinfo",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"sub\": \"mock-user-123\", \"name\": \"Test User\", \"email\": \"test@example.com\", \"email_verified\": true, \"picture\": \"https://example.com/photo.jpg\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "GET",
        "path": "/userinfo",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"sub\": \"mock-user-123\", \"name\": \"Test User\", \"email\": \"test@example.com\", \"email_verified\": true, \"picture\": \"https://example.com/photo.jpg\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/userinfo",
        "headers": {
            "Authorization": ["Bearer .+"]
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"sub\": \"mock-user-123\", \"name\": \"Test User\", \"email\": \"test@example.com\", \"email_verified\": true, \"picture\": \"https://example.com/photo.jpg\"}"
        }
    }
}'

The UserInfo endpoint returns claims about the authenticated user. This is called by OIDC clients after obtaining an access token.

 

Turnkey OIDC Mock Identity Provider

The examples above show how to build individual OIDC/OAuth2 expectations by hand. For cases where you need a complete identity provider in one call, MockServer provides a turnkey OIDC mock via PUT /mockserver/oidc. A single request stands up a full provider that issues real, cryptographically verifiable tokens, so OIDC client libraries (jose, Nimbus JOSE, Spring Security) work without modification.

It generates the discovery, JWKS, token, userinfo, introspection, and revocation endpoints, plus authorization-code (/authorize), RP-initiated logout / end-session (/logout), and device-authorization (RFC 8628) endpoints. Tokens can be signed with RSA (RS256/RS384/RS512) or ECDSA (ES256/ES384/ES512), and you can supply your own key (jwkJson / keyId), enforce client authentication, issue opaque access tokens, and script device-code polling.

Full reference: the turnkey provider has its own dedicated page — Mock OpenID Connect Provider. That page is the single source of truth for the complete endpoint list, every configuration field (signing algorithms, custom keys, enforceClientAuthentication, opaqueAccessToken, deviceCodePendingPolls), the negative-testing flags, and worked examples. The quick start below is just enough to get going.

new MockServerClient("localhost", 1080).mockOpenIdProvider();
const http = require('http');

const req = http.request({
    host: 'localhost',
    port: 1080,
    method: 'PUT',
    path: '/mockserver/oidc',
    headers: { 'Content-Type': 'application/json' }
});
req.write(JSON.stringify({}));
req.end();
import json
import urllib.request

req = urllib.request.Request(
    "http://localhost:1080/mockserver/oidc",
    data=json.dumps({}).encode("utf-8"),
    method="PUT",
    headers={"Content-Type": "application/json"}
)
urllib.request.urlopen(req)
require 'net/http'
require 'uri'

uri = URI('http://localhost:1080/mockserver/oidc')
req = Net::HTTP::Put.new(uri, 'Content-Type' => 'application/json')
req.body = '{}'
Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/oidc",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/oidc",
    new StringContent("{}", Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
client.put("http://localhost:1080/mockserver/oidc")
    .header("Content-Type", "application/json")
    .body("{}")
    .send()
    .unwrap();
$ch = curl_init('http://localhost:1080/mockserver/oidc');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => '{}',
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/oidc" \
  -H "Content-Type: application/json" \
  -d '{}'

MockServer stands up the full provider with default paths and claims. The token endpoint immediately returns valid signed JWTs (by default RS256) that verify end-to-end against the JWKS endpoint. See the Mock OpenID Connect Provider page for custom claims, alternative signing algorithms, and negative-testing flags.

 

Error Responses

Testing error scenarios is critical for building resilient OAuth2 integrations. The OAuth2 specification defines standard error codes that authorization servers return when requests fail.

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withBody(
                params(
                    param("grant_type", "client_credentials"),
                    param("client_id", "invalid-client")
                )
            )
    )
    .respond(
        response()
            .withStatusCode(401)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"error\": \"invalid_client\", \"error_description\": \"Client authentication failed\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["invalid-client"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_client\", \"error_description\": \"Client authentication failed\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["invalid-client"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_client\", \"error_description\": \"Client authentication failed\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["invalid-client"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_client\", \"error_description\": \"Client authentication failed\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["client_credentials"],
                    "client_id": ["invalid-client"]
                }
            }
        },
        "httpResponse": {
            "statusCode": 401,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"error\": \"invalid_client\", \"error_description\": \"Client authentication failed\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""client_credentials""],
                ""client_id"": [""invalid-client""]
            }
        }
    },
    ""httpResponse"": {
        ""statusCode"": 401,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""error\"": \""invalid_client\"", \""error_description\"": \""Client authentication failed\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["invalid-client"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_client\", \"error_description\": \"Client authentication failed\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["invalid-client"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_client\", \"error_description\": \"Client authentication failed\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["client_credentials"],
                "client_id": ["invalid-client"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 401,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_client\", \"error_description\": \"Client authentication failed\"}"
        }
    }
}'
new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withBody(
                params(
                    param("grant_type", "authorization_code"),
                    param("code", "expired-or-used-code")
                )
            )
    )
    .respond(
        response()
            .withStatusCode(400)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"error\": \"invalid_grant\", \"error_description\": \"The authorization code has expired or has already been used\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["expired-or-used-code"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The authorization code has expired or has already been used\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["expired-or-used-code"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The authorization code has expired or has already been used\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["expired-or-used-code"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The authorization code has expired or has already been used\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["authorization_code"],
                    "code": ["expired-or-used-code"]
                }
            }
        },
        "httpResponse": {
            "statusCode": 400,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The authorization code has expired or has already been used\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""authorization_code""],
                ""code"": [""expired-or-used-code""]
            }
        }
    },
    ""httpResponse"": {
        ""statusCode"": 400,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""error\"": \""invalid_grant\"", \""error_description\"": \""The authorization code has expired or has already been used\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["expired-or-used-code"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The authorization code has expired or has already been used\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["expired-or-used-code"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The authorization code has expired or has already been used\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["authorization_code"],
                "code": ["expired-or-used-code"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"invalid_grant\", \"error_description\": \"The authorization code has expired or has already been used\"}"
        }
    }
}'
new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/oauth/token")
            .withBody(
                params(
                    param("grant_type", "password")
                )
            )
    )
    .respond(
        response()
            .withStatusCode(400)
            .withHeader("Content-Type", "application/json")
            .withBody(json("{\"error\": \"unsupported_grant_type\", \"error_description\": \"The authorization grant type is not supported\"}"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["password"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unsupported_grant_type\", \"error_description\": \"The authorization grant type is not supported\"}"
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["password"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unsupported_grant_type\", \"error_description\": \"The authorization grant type is not supported\"}"
        }
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["password"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unsupported_grant_type\", \"error_description\": \"The authorization grant type is not supported\"}"
        }
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
        "httpRequest": {
            "method": "POST",
            "path": "/oauth/token",
            "body": {
                "type": "PARAMETERS",
                "parameters": {
                    "grant_type": ["password"]
                }
            }
        },
        "httpResponse": {
            "statusCode": 400,
            "headers": {
                "Content-Type": ["application/json"]
            },
            "body": {
                "type": "JSON",
                "json": "{\"error\": \"unsupported_grant_type\", \"error_description\": \"The authorization grant type is not supported\"}"
            }
        }
    }`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""method"": ""POST"",
        ""path"": ""/oauth/token"",
        ""body"": {
            ""type"": ""PARAMETERS"",
            ""parameters"": {
                ""grant_type"": [""password""]
            }
        }
    },
    ""httpResponse"": {
        ""statusCode"": 400,
        ""headers"": {
            ""Content-Type"": [""application/json""]
        },
        ""body"": {
            ""type"": ""JSON"",
            ""json"": ""{\""error\"": \""unsupported_grant_type\"", \""error_description\"": \""The authorization grant type is not supported\""}""
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["password"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unsupported_grant_type\", \"error_description\": \"The authorization grant type is not supported\"}"
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["password"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unsupported_grant_type\", \"error_description\": \"The authorization grant type is not supported\"}"
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/oauth/token",
        "body": {
            "type": "PARAMETERS",
            "parameters": {
                "grant_type": ["password"]
            }
        }
    },
    "httpResponse": {
        "statusCode": 400,
        "headers": {
            "Content-Type": ["application/json"]
        },
        "body": {
            "type": "JSON",
            "json": "{\"error\": \"unsupported_grant_type\", \"error_description\": \"The authorization grant type is not supported\"}"
        }
    }
}'

See Also

  • Control Plane JWT Authentication — use JWTs (such as those issued by your mocked OAuth2 endpoint) to authenticate control-plane requests
  • HTTPS & TLS — serve the mocked OAuth2 token and JWKS endpoints over HTTPS with a trusted certificate
  • CORS Support — allow browser-based OAuth2 clients to call the mocked token endpoint cross-origin
  • TLS Configuration Properties — configure the certificate used to serve OAuth2 endpoints over TLS