Skip to main content

Rotate the channel assertion key

Channel authentication signs identity assertions with one RSA key. Supabase Auth verifies them through the API's published JWKS. Rotation publishes the new public key before anything signs with it, and keeps the old public key until nothing signed with it is still in flight.

Prerequisites​

  • OpenSSL 3 and Node.js 22 on the machine where you generate keys.
  • Access to the API's secret configuration and a way to deploy the API.

Generate keys on a trusted machine and move them straight into your secret store. Never commit them or paste them into tickets or chat.

Rotate the key​

  1. Generate a new RSA key:

    openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out assertion-key.pem
  2. Export the current key's public half as a JWK array. Pass the current private key file:

    node -e 'const {createPublicKey}=require("node:crypto");const {kty,n,e}=createPublicKey(require("node:fs").readFileSync(process.argv[1])).export({format:"jwk"});console.log(JSON.stringify([{kty,n,e}]))' current-assertion-key.pem
  3. Update the API's configuration and deploy it:

    VariableNew value
    CHANNEL_AUTH_ASSERTION_PRIVATE_KEYThe contents of assertion-key.pem
    CHANNEL_AUTH_ASSERTION_RETIRED_PUBLIC_KEYS_JSONThe output of step 2

    Expected result: GET https://<api-origin>/.well-known/jwks.json lists two keys. The first is the new key, and new assertions carry its kid.

  4. Keep the retired key published while Supabase Auth may still verify an assertion against it. Assertions live 60 seconds, but Supabase Auth also caches the JWKS, and this project has not measured for how long. Keep the retired key for at least 24 hours unless you have confirmed a shorter cache time for your Auth version.

  5. Clear CHANNEL_AUTH_ASSERTION_RETIRED_PUBLIC_KEYS_JSON and deploy again. Expected result: the JWKS lists one key.

Rotation does not affect existing users. People are identified by their stable channel identifier, not by key, and Supabase sessions do not depend on the assertion key once issued.

Revoke a compromised key​

Whoever holds the key can sign assertions that Supabase Auth turns into a session for any existing channel identity. Remove it at once instead of waiting out an overlap window. Sign-ins fail during the switch, and that is acceptable.

  1. Generate a new key, set it as CHANNEL_AUTH_ASSERTION_PRIVATE_KEY, leave CHANNEL_AUTH_ASSERTION_RETIRED_PUBLIC_KEYS_JSON empty, and deploy.
  2. Confirm that the JWKS no longer lists the compromised kid.
  3. Treat sessions issued since the leak as untrusted. Revoke them from Supabase Auth with operator credentials, and review the API logs for exchanges you cannot explain.