Search…
JWT Templates
Customize your authentication tokens and integrate with anyone!

Introduction

Clerk offers the ability to generate JSON Web Tokens (JWTs) representing users that are signed in to your application.
JWT Templates is a powerful feature that extends this functionality by allowing you to customize the contents of these tokens.This enables you to integrate with any third-party services that support authentication with JWTs.

Overview

JWTs can be generated from your frontend using Clerk.js. Each token depicts a user who is authenticated to your application. Tokens can only be generated for signed in users and each token is tied to a particular user.
Tokens are cryptographically signed with your instance's private key and can be verified using its public key, which can be fetched from your instance's JWKS endpoint, typically https://clerk.your-site.com/.well-known/jwks.json.
With JWT Templates you can control the claims that will go into these tokens, by defining custom templates that fit your needs. An example use case is integrating with a third-party service that is able to consume JWTs, but requires them to be in a particular format.
At a glance, the process for creating and using a template is the following:
  1. 1.
    Create a template from Dashboard
  2. 2.
    From your frontend, generate tokens using that template
In the following sections we will dive into these steps and learn what we can achieve with JWT Templates. Finally, we will see a complete example of a template and its resulting tokens.

Template basics

Templates are essentially JSON objects that specify claims to be included in the generated tokens, along with their respective values.
Claim values can be either static or dynamic. Static values can be any of the regular JSON data types (strings, numbers, booleans, arrays, objects, null) and will be included as-is in the tokens. Dynamic values, also called shortcodes, are special strings that will be substituted for their actual values when the tokens are generated.
Example of a template that specifies both static values and shortcodes:
1
{
2
"aud": "https://example.com",
3
"interests": ["hiking", "knitting"],
4
"name": "{{user.first_name}}",
5
"surname": "{{user.last_name}}",
6
"email": "{{user.primary_email_address}}"
7
}
Copied!
In this example, the values of the aud and interests claims are static. The values of the name and email claims are dynamic.
A token generated using the template above, would look roughly like this:
1
{
2
"aud": "https://example.com",
3
"interests": ["hiking", "knitting"],
4
"name": "John",
5
"surname": null,
6
"email": "[email protected]"
7
// ...plus some automatically-included claims; more on that below
8
}
Copied!

Default claims

In the previous example we glanced over something called "automatically-included claims". Besides the claims specified by the template, there are certain claims that are automatically included in all tokens and cannot be overridden by templates. We call them default claims and they are the following:
  • sub: The user's unique ID (e.g. "user_abc1234def57")
  • iat: The time at which the token was issued, as a Unix timestamp (e.g. 1516239022)
  • iss: The Frontend API URL of your instance (e.g. "https://clerk.your-site.com")
  • jti: A random alphanumeric string, used as a unique identifier for the token
  • exp: The time after which the token will expire, as a Unix timestamp. Determined using the "Token lifetime" template setting.
  • nbf: The time before which the token is considered invalid, as a Unix timestamp. Determined using the "Allowed Clock Skew" template setting.
  • azp: The Origin of the request. If it's not present, this claim is omitted.
These are all standard claims registered by IANA. For generic information about them refer to RFC 7519.

Shortcodes

To include dynamic data in a token, you can use shortcodes - special strings that are substituted for their corresponding values during token generation. Shortcodes are typically used to refer to data that depend on the currently authenticated user.
Shortcodes are strings of the form "{{some.identifier}}" and they can be used in place of values (but not keys).
The available shortcodes are the following:
Shortcode
Description
Type
Example
"{{user.id}}"
The user's unique identifier in Clerk
string
"user_abc123bcde"
"{{user.first_name}}"
The user's first name, if provided.
string
null
"John"
"{{user.last_name}}"
The user's last name, if provided.
string
null
"Doe"
"{{user.created_at}}"
Date and time when the user has signed up to the instance, as a Unix timestamp.
number
1640104791
"{{user.updated_at}}"
Date and time when the user was last updated, as a Unix timestamp.
number
1640104748
"{{user.primary_email_address}}"
The user's primary email, if provided.
string
null
"{{user.primary_phone_number}}"
The user's primary phone, if provided.
string
null
"+12025550188"
"{{user.profile_image_url}}"
The user's avatar image URL. If not provided, the default avatar is used.
string
"https://www.gravatar.com/avatar?d=mp"
"{{user.public_metadata}}"
The user's public metadata object. To retrieve specific attributes, see "User metadata".
object
{"interests": ["hiking", "knitting"] }
"{{user.unsafe_metadata}}"
The user's unsafe metadata object. To retrieve specific attributes, see "User metadata".
object
{}
If you need something that is missing from this list, please do not hesitate to contact us.
Even though in templates shortcodes are string values, their type in the generated token depends on the original type of the information that's included. For example, "{{user.public_metadata}}" will be substituted for a JSON object, not a string.

User metadata

While you can use "{{user.public_metadata}}" or "{{user.unsafe_metadata}}" to include the complete metadata object in the final token, there might be cases where you only need a specific piece of information.
To keep your tokens lean, there is a syntax you can use to do just that.
Let's assume the user's public metadata are the following:
1
{
2
"interests": ["hiking", "knitting"],
3
"addresses": {
4
"Home": "2355 Pointe Lane, 56301 Minnesota",
5
"Work": "3759 Newton Street, 33487 Florida"
6
}
Copied!
To access the interests array, you would use the shortcode "{{user.public_metadata.interests}}". To access the Home address, you would use "{{user.public_metadata.addresses.Home}}".
An example follows:
1
// template
2
{
3
"likes_to_do": "{{user.public_metadata.interests}}",
4
"shipping_address": "{{user.public_metadata.addresses.Home}}"
5
}
6
7
// actual token
8
{
9
"likes_to_do": ["hiking", "knitting"],
10
"shipping_address": "2355 Pointe Lane, 56301 Minnesota"
11
}
Copied!
Using the same notation (.) you can fetch data from nested fields of arbitrary depth. The examples above use public metadata, but the same notation works for unsafe metadata as well.
Future extensions to the template language may provide more ways to query for specific data. Until then, please contact us if you need something that is not currently supported.

Creating a template

A template consists of the following (4) properties:
  1. 1.
    Template name: a unique identifier for the template. When generating a token, you will have to specify the template to use, using this name. This is a required field.
  2. 2.
    Token lifetime: the time in seconds, after which tokens generated with this template will expire. This setting determines the value of the exp claim (i.e. exp=current_time+lifetime). Default is 60 seconds.
  3. 3.
    Token allowed clock skew: the time in seconds, provided as a leeway to account for clock skews between different servers. This setting determines the value of the nbf claim (i.e. nbf=current_time-allowed_clock_skew). Default is 5 seconds.
  4. 4.
    Claims: the actual template that's entered into the JSON editor (see screenshot). A template is essentially a JSON object that describes what the final token claims will look like (shortcodes can be used here). This is a required field.
Templates can be created from Dashboard by navigating to your instance's settings and clicking the JWT Templates option.
Creating a new template

Using a template

Tokens are generated by your frontend via Clerk.js, by specifying the template to use.
Assuming you named your template my-template-1, you can generate a token using that template, like so:
1
try {
2
await Clerk.session.getToken({ template: 'my-template-1' }) // => "eyJhbGciOiJSUzI1NiIsImtpZC..."
3
} catch(e) {
4
// handle error
5
}
Copied!
The return value is a Promise, since this code will query the Frontend API (clerk.your-site.com) to generate the JWT and fetch it, encoded in Base64.
Tokens can only be generated if the current user is signed in

Complete example

A more detailed example follows, which demonstrates the full capabilities of JWT Templates, including static claim values, dynamic claim values via shortcodes and automatically included claims.
Given the following user:
  • First name: Maria
  • Profile picture URL: https://example.com/avatar.jpg
  • Clerk ID: user_abcdef123456789
  • Email address (verified): [email protected]
  • Phone number: (not provided)
  • Public metadata: { "profile" : {"interests": ["reading", "climbing"] } }
  • Unsafe metadata: { "foo" : { "bar": 42 } }
And given the following template (comments added here for clarity):
1
{
2
// static values
3
"aud": "https://my-site.com",
4
"version": 1,
5
"foo": { "bar": [1,2,3] },
6
7
// dynamic values
8
"user_id": "{{user.id}}",
9
"avatar": "{{user.profile_image_url}}",
10
"first_name": "{{user.first_name}}",
11
"last_name": "{{user.last_name}}",
12
"email": "{{user.primary_email_address}}",
13
"phone": "{{user.primary_phone_address}}",
14
"registration_date": "{{user.created_at}}",
15
"likes_to_do": "{{user.public_metadata.profile.interests}}",
16
"unsafe_meta": "{{user.unsafe_metadata}}",
17
"invalid_shortcode": "{{user.i_dont_exist}}"
18
}
Copied!
Generating a token using the above template, for the aforementioned user, would result in roughly the following claims:
1
{
2
"aud": "https://my-site.com",
3
"version": 1,
4
"foo": { "bar": [1,2,3] },
5
"user_id": "user_abcdef123456789",
6
"avatar": "https://example.com/avatar.jpg",
7
"first_name": "Maria",
8
"last_name": null,
9
"email": "[email protected]",
10
"phone": null,
11
"registration_date": 1227618844,
12
"likes_to_do": ["reading", "climbing"],
13
"unsafe_meta": {
14
"foo" : {
15
"bar": 42
16
}
17
},
18
"invalid_shortcode": "{{user.i_dont_exist}}",
19
20
// default claims, included automatically
21
"iat": 1639398272,
22
"nbf": 1639398220,
23
"jti": "ab293jfirnbmc90qhjwen21",
24
"exp": 1639398300,
25
"iss": "https://clerk.my-site.com",
26
"sub": "user_abcdef123456789"
27
}
Copied!
Last modified 15d ago