# JWKS Specification

## JWKS Endpoint requirements for RP&#x20;

The client must provide the public key(s) during onboarding by host the JWKS on a publicly accessible URL. This endpoint must be compatible with Singpass's [#jwks-url-service-level-expectations](#jwks-url-service-level-expectations "mention"). These public keys will be used to verify the signature of the token in [Initiate Sign Request](/for-relying-parties/api-documentation/sign-v3/initiate-sign-request.md) and [Get Signing Result](/for-relying-parties/api-documentation/sign-v3/get-signing-result.md).

RP are allowed to provide multiple keys on the JWKS url.

{% hint style="info" %}
&#x20;[mkjwk.org](https://mkjwk.org/) is a useful open-source tool to generate different types of JWK for signing and encryption; compliant with Singpass's broad requirements on structure. While we **DO NOT** suggest this as a secure way to generate your *real* keypair (including private key); this can be a useful tool to understand how JWK works; and how it is represented for signing and encryption purposes; while you are reviewing against our supported algorithms below.
{% endhint %}

### **Key requirements:**

* Must have key `use` of value `sig` per [rfc7517#section-4.2](https://tools.ietf.org/html/rfc7517#section-4.2)
* Must contain a key ID in the standard `kid` field per [rfc7517#section-4.5](https://tools.ietf.org/html/rfc7517#section-4.5)
  * Will be used by Singpass to select the relevant key to verify the client assertion
* Must be an EC key, with curves: `P-256`, `P-384` or `P-521` *(NIST curves, aka `secp256r1`, `secp384r1`, `secp521r1` respectively)*

### **Example**

```json
{
    "keys": [
        {
            "kty": "EC",
            "use": "sig",
            "crv": "P-256",
            "kid": "6X_-_oLSH0DQLtz16o-NTKcm0lG0J-VDGHOz6tPx0Jc",
            "x": "1tR88zrGoPUV-Fr4bh_9NR-mDhC9rLswDp85hkbKBT0",
            "y": "1vYh1M53NK_b7l9Y-1FgCENOp6Fl9StVVLr3KqK_Ka8",
            "alg": "ES256"
        }
    ]
}
```

### **Key Rotation Best Practices**

Our server caches the key for <mark style="color:red;">**1 hour**</mark>. When performing key rotation, do ensure that:

* New key should be added to the JWKS at least 1 hour before it is used to sign new JWTs.
* Old key must remain available for at least 1 hour after stop signing with it, ensuring that previously issued JWTs can still be validated.

### Service Level Expectations <a href="#jwks-url-service-level-expectations" id="jwks-url-service-level-expectations"></a>

Sign requires that any JWKS is published on an endpoint that

* is served behind HTTPS on port `443` using a TLS server certificate issued by a standard *publicly verifiable CA issuer* (no private CAs), with *complete cert chain* presented by the server;
* is publicly accessible (no IP whitelisting, mTLS or other custom HTTP header requirements outside standard HTTP headers such as `Content-Type`, `Accept`);
* is able to respond in a timely fashion with respect to the below configuration.
  * Per try timeout: 3s
  * Max attempts: 3
  * Cache duration for retrieved JWKS: 1h

> Note: While the above is a technical requirement; the user experience of your users may be affected if we are unable to retrieve your JWKS in a timely fashion upon our cache expiry due to slower token exchanges with your backend. We recommend aiming for this response to be as fast as possible based on an in-memory cache; or simple static asset retrieval.

## Sign with Singpass JWKS

RP can verify the signature of a JWT from Sign with Singpass by acquiring the signing public key from this endpoint. More information about a JSON Web Key (JWK) endpoint can be found [here](https://tools.ietf.org/html/rfc7517).

Public keys returned from this endpoint could be in random sequence or rotated for security enhancement.&#x20;

### URLs

<table><thead><tr><th width="178">Environment</th><th width="576">URL</th></tr></thead><tbody><tr><td>Staging</td><td><a href="https://static.staging.sign.singpass.gov.sg/.well-known/keys.json">https://static.staging.sign.singpass.gov.sg/.well-known/keys.json</a></td></tr><tr><td>Production</td><td><a href="https://static.app.sign.singpass.gov.sg/.well-known/keys.json">https://static.app.sign.singpass.gov.sg/.well-known/keys.json</a></td></tr></tbody></table>

### Cache and key rotation

For varying reasons, keys used for signing can and will be rotated/changed with **no defined schedule**, and at the full discretion of Singpass. When a key rotation happens, the new key will be available from the JWKS endpoint and will have a different `kid` value. The new `kid` value will be reflected in all the new JWTs signed by Singpass. In such cases, cached copies of Singpass public keys must be refreshed by re-invoking the JWKS endpoint.

If the validation of the Singpass signature fails, re-fetch from the JWKS endpoint once for that validation.

{% hint style="danger" %}
Please read through the list of **DON’Ts** below:

* Do not assume the position of a signing key among the list of the returned keys.
* Do not validate Singpass signatures using a hardcoded public key. Always determine the correct key (for signature verification) by inspecting the `kid` from the JWS header, and use it to retrieve the public key from our JWKS endpoint.
* Do not cache only 1 key. Caching should be done for the entire JWKS.
  {% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.sign.singpass.gov.sg/for-relying-parties/api-documentation/sign-v3/jwks-specification.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
