Configurable OAuth2/OpenID Connect server for automated testing and development purposes
npm install oauth2-mock-serveroauth2-mock-servershell
npm install --save-dev oauth2-mock-server
`
$3
Here is an example for creating and running a server instance with a single random RSA key:
`js
import { OAuth2Server } from 'oauth2-mock-server';
// ...or in CommonJS style:
// const { OAuth2Server } = require('oauth2-mock-server');
let server = new OAuth2Server();
// Generate a new RSA key and add it to the keystore
await server.issuer.keys.generate('RS256');
// Start the server
await server.start(8080, 'localhost');
console.log('Issuer URL:', server.issuer.url); // -> http://localhost:8080
// Do some work with the server
// ...
// Stop the server
await server.stop();
`
Any number of existing JSON-formatted keys can be added to the keystore.
`js
// Add an existing JWK key to the keystore
await server.issuer.keys.add({
kid: 'some-key',
alg: 'RS256',
kty: 'RSA',
// ...
});
`
JSON Web Tokens (JWT) can be built programmatically:
`js
import axios from 'axios';
// Build a new token
let token = await server.issuer.buildToken();
// Call a remote API with the token
axios
.get('https://server.example.com/api/endpoint', {
headers: {
authorization: Bearer ${token},
},
})
.then((response) => {
/ ... /
})
.catch((error) => {
/ ... /
});
`
$3
- No authentication
- Client Credentials grant
- Resource Owner Password Credentials grant
- Authorization Code grant, with Proof Key for Code Exchange (PKCE) support
- Refresh token grant
$3
| Algorithm | kty | alg |
| ----------------- | --- | ------------------- |
| RSASSA-PKCS1-v1_5 | RSA | RS256, RS384, RS512 |
| RSASSA-PSS | RSA | PS256, PS384, PS512 |
| ECDSA | EC | ES256, ES384, ES512 |
| EdDSA | OKP | Ed25519 |
$3
It also provides a convenient way, through event emitters, to programmatically customize the server processing. This is particularly useful when expecting the OIDC service to behave in a specific way on one single test.
#### beforeTokenSigning
Typed signature: (token: MutableToken, req: TokenRequestIncomingMessage) => void
`js
// Modify the expiration time on next produced token
service.once('beforeTokenSigning', (token, req) => {
const timestamp = Math.floor(Date.now() / 1000);
token.payload.exp = timestamp + 400;
});
`
`js
const basicAuth = require('basic-auth');
// Add the client ID to a token
service.once('beforeTokenSigning', (token, req) => {
const credentials = basicAuth(req);
const clientId = credentials ? credentials.name : req.body.client_id;
token.payload.client_id = clientId;
});
`
#### beforeResponse
Typed signature: (tokenEndpointResponse: MutableResponse, req: TokenRequestIncomingMessage) => void
`js
// Force the oidc service to provide an invalid_grant response
// on next call to the token endpoint
service.once('beforeResponse', (tokenEndpointResponse, req) => {
tokenEndpointResponse.body = {
error: 'invalid_grant',
};
tokenEndpointResponse.statusCode = 400;
});
`
#### beforeUserinfo
Typed signature: (userInfoResponse: MutableResponse, req: IncomingMessage) => void
`js
// Force the oidc service to provide an error
// on next call to userinfo endpoint
service.once('beforeUserinfo', (userInfoResponse, req) => {
userInfoResponse.body = {
error: 'invalid_token',
error_message: 'token is expired',
};
userInfoResponse.statusCode = 401;
});
`
#### beforeRevoke
Typed signature: (revokeResponse: StatusCodeMutableResponse, req: IncomingMessage) => void
`js
// Simulates a custom token revocation result code
service.once('beforeRevoke', (revokeResponse, req) => {
revokeResponse.statusCode = 418;
});
`
#### beforeAuthorizeRedirect
Typed signature: (authorizeRedirectUri: MutableRedirectUri, req: IncomingMessage) => void
`js
// Modify the uri and query parameters
// before the authorization redirect
service.once('beforeAuthorizeRedirect', (authorizeRedirectUri, req) => {
authorizeRedirectUri.url.searchParams.set('foo', 'bar');
});
`
#### beforePostLogoutRedirect
Typed signature: (postLogoutRedirectUri: MutableRedirectUri, req: IncomingMessage) => void
`js
// Modify the uri and query parameters
// before the post_logout_redirect_uri redirect
service.once('beforePostLogoutRedirect', (postLogoutRedirectUri, req) => {
postLogoutRedirectUri.url.searchParams.set('foo', 'bar');
});
`
#### beforeIntrospect
Typed signature: (introspectResponse: MutableResponse, req: IncomingMessage) => void
`js
// Simulate a custom token introspection response body
service.once('beforeIntrospect', (introspectResponse, req) => {
introspectResponse.body = {
active: true,
scope: 'read write email',
client_id: '',
username: 'dummy',
exp: 1643712575,
};
});
`
$3
It also provides basic HTTPS support, an optional cert and key can be supplied to start the server with SSL/TLS using the in-built NodeJS HTTPS module.
We recommend using a package to create a locally trusted certificate, like mkcert.
`js
let server = new OAuth2Server(
'test-assets/mock-auth/key.pem',
'test-assets/mock-auth/cert.pem'
);
`
NOTE: Enabling HTTPS will also update the issuer URL to reflect the current protocol.
Supported endpoints
$3
Returns the OpenID Provider Configuration Information for the server.
$3
Returns the JSON Web Key Set (JWKS) of all the keys configured in the server.
$3
Issues access tokens.
$3
Simulates the user authentication. It will automatically redirect to the callback endpoint sent as parameter.
It currently supports only 'code' response_type.
$3
Provides extra userinfo claims.
$3
Simulates a token revocation. This endpoint should always return 200 as stated by RFC 7009.
$3
Simulates the end session endpoint. It will automatically redirect to the post_logout_redirect_uri sent as parameter.
$3
Simulates the token introspection endpoint.
Command-Line Interface
The server can be run from the command line.
`shell
npx oauth2-mock-server --help
`
Attributions
- jose`