Verifying JWTs
Canva uses JSON Web Tokens (JWTs) to encode information about the current user, including their ID and the ID of their team. To extract this information, you need to decode and verify the JWT.
There are two situations where this is necessary:
- Before an app sends an HTTP request to a backend, it should call the
getCanvaUserToken
method. This method returns a JWT, which the app can use to verify the authenticity of the request. To learn more, see Verifying HTTP requests. - When authenticating a user via a third-party platform, Canva appends a
canva_user_token
query parameter to the Redirect URL. This parameter contains a JWT that the app can use to identify the user. To learn more, see Authenticating users.
In both cases, the steps for decoding and verifying the JWT are the same.
Step 1: Get the JWT header
JWTs are made up of three parts:
- Header
- Payload
- Signature
The part we're interested in is the header.
To extract the header, we recommend using a library. By default though, some libraries don't extract the header. When using the jsonwebtoken
library, for instance, you must enable the behavior:
import jwt from "jsonwebtoken";const decoded = jwt.decode("<JWT_GOES_HERE>", { complete: true });console.log(decoded.header);
Step 2: Get the kid
property
The JWT header is an object. This object has a kid
property, which is short for key ID. You need to grab this property from the header object as it's required in a later step:
const { kid } = decoded.header;console.log(kid);
Step 3: Get the active key
Canva exposes a JSON Web Key Set (JWKS) via the following endpoint:
https://api.canva.com/rest/v1/apps/<YOUR_APP_ID>/jwks
This is a JSON file that looks something like this:
{"keys": [{"kid": "292e133c-2afe-4cb6-8e8d-43468affa32a","kty": "RSA","n": "39fdyga5zNmwBhc0Hsdpd_u5DrJa8-OS8KkyoD_sipY4rbD6yyBSr1kqJa3n8qG1K2d96OEVZH-_BdpeLMHmP3NkhCacT1dkzpM_b0mWLCYA-xKt-eAFVIAxiVjorjQHtX6qD-UtborDwMKMm0ul3TFJPU2LVNmLePZrfPkb3jMkzYQPixprmdh5XfR-r853RhphhkscvbLJIcSdz56_6gQZrp6peGOn_7XSxiOSDbFdEgPMAxaFP1vHStp8yj09K_UKGOFQye06Dz26DIN8U8F8_QFafLuIp0fl-2eehfUT8f_iFUE3kuOkzJsXL3Wg4kjmsVoSlVIFhM0KPVs_hw","e": "AQAB"}]}
Each object in the keys
array is a JSON Web Key (JWK) — also known as a public key. The array may contain multiple keys, but only one key is active at any point in time.
Your app's backend must:
- Download the JWKS file.
- Get the active key.
The active key is the key with a kid
property that matches the kid
property from the JWT header.
If possible, we recommend using a library to handle these steps. For example, the following code sample demonstrates how to use the jwks-rsa
library:
import { JwksClient } from "jwks-rsa";import jwt from "jsonwebtoken";const { APP_ID } = process.env;const CACHE_EXPIRY_MS = 60 * 60 * 1_000; // 60 minutesconst TIMEOUT_MS = 30 * 1_000; // 30 secondsconst decoded = jwt.decode("<JWT_GOES_HERE>", { complete: true });const { kid } = decoded.header;const jwks = new JwksClient({cache: true,cacheMaxAge: CACHE_EXPIRY_MS,timeout: TIMEOUT_MS,rateLimit: true,jwksUri: `https://api.canva.com/rest/v1/apps/${APP_ID}/jwks`,});const publicKey = await jwks.getSigningKey(kid).getPublicKey();
An additional benefit of this library is that it caches the JSON file for the specified amount of time — in this case, 60 minutes. This means the app doesn't have to repeatedly download the file, which:
- Improves the performance of the app
- Reduces the risk of DDOS attacks
If you don't use a library, we strongly recommend caching the file or redownloading it on a schedule rather than downloading it any time the backend receives a request.
Step 4: Verify the token
Use the public key to verify the JWT:
const verified = jwt.verify("<JWT_GOES_HERE>", publicKey, {audience: APP_ID,});
The exact syntax will depend on the library you're using.
If the token is valid, the returned object will contain the following properties:
aud
- The ID of the app.brandId
- The ID of the user's team.userId
- The ID of the user.
If these properties are not available, then the token, key, or combination of the two is invalid.