# 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](https://docs.sign.singpass.gov.sg/for-relying-parties/api-documentation/sign-v3/initiate-sign-request "mention") and [get-signing-result](https://docs.sign.singpass.gov.sg/for-relying-parties/api-documentation/sign-v3/get-signing-result "mention").

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 %}
