Authentication
In this guide we'll show you how to identify users connecting to your Triplit server and how to model access control with that information. By the end of the guide, you'll be able to define a schema that allows users to insert and update their own data, but not other users' data:
export const schema = S.Collections({
blogPosts: {
schema: S.Schema({
id: S.Id(),
title: S.String(),
content: S.String(),
authorId: S.String(),
}),
permissions: {
authenticated: {
read: { filter: [true] },
insert: { filter: ['authorId', '=', '$token.sub'] },
update: { filter: ['authorId', '=', '$token.sub'] },
postUpdate: { filter: ['authorId', '=', '$token.sub'] },
delete: { filter: ['authorId', '=', '$token.sub'] },
},
},
},
});
Choose an authentication provider
You'll likely start out your project using the anon
and service
tokens provided by default. These are great for getting started, but don't provide any user-specific information that you'll need to model access control.
Choose from one of the many authentication providers that issue JWTs:
- Clerk (opens in a new tab): read the Triplit integration guide here
- Supabase (opens in a new tab): read the Triplit integration guide here
- Auth0 (opens in a new tab)
- Amazon Cognito (opens in a new tab)
- ...or any other provider that issues JWTs
Allow your Triplit server to verify JWTs
If you're using a third-party authentication provider, you'll need to provide the public key or secret that it's using to sign JWTs to your Triplit server so it can verify them. Triplit supports both symmetric (e.g. HMAC) and asymmetric (e.g. RSA) JWT encryption algorithms.
If you're connecting to a triplit.io
URL, you can use the dashboard (opens in a new tab). If you're fully self-hosting Triplit (and not pointed at a triplit.io
URL), you'll need to set the EXTERNAL_JWT_SECRET
environmental variable to the public key or symmetric secret.
Pass the token to Triplit
Once you have your authentication provider set up in your app and your user is signed in, you'll need to pass the JWT to Triplit. This is done by calling startSession
on the TriplitClient
instance.
import { TriplitClient } from '@triplit/client';
const client = new TriplitClient({
serverUrl: 'https://<project-id>.triplit.io',
});
function onPageLoad() {
// get the user's token from your authentication provider
const token = await auth.getToken({ skipCache: true });
if (token) {
if (client.token) {
await client.endSession();
}
await client.startSession(token);
}
}
Connections to the server are managed with the Sessions API. Read more about it here.
Add permissions to your schema
Access control to data on a Triplit server is defined by permissions in the schema. Permissions can reference the JWT claims that are passed to Triplit. Once you've added permissions, you need to run npx triplit schema push
to have them take effect on a deployed server (or just restart the development server if you're running it locally.).
export const schema = S.Collections({
blogPosts: {
schema: S.Schema({
id: S.Id(),
title: S.String(),
content: S.String(),
authorId: S.String(),
}),
permissions: {
authenticated: {
read: { filter: [true] },
insert: { filter: ['authorId', '=', '$token.sub'] },
update: { filter: ['authorId', '=', '$token.sub'] },
postUpdate: { filter: ['authorId', '=', '$token.sub'] },
delete: { filter: ['authorId', '=', '$token.sub'] },
},
},
},
});
These permissions will allow any authenticated user to read all blog posts, but only allow them to insert, update, and delete their own posts. Notice that permissions are defined as query filters. This means that a permissions can be as complex as a query, and use or
, exists
and other query operators. Permissions can also reference relationships and/or any of the JWT claims on the user's $token
. In this case, we're using the sub
claim to identify the user. This is the default claim that most authentication providers will use to identify the user.
Use token claims when inserting or updating data
When Triplit is given a JWT token, it makes the decoded claims available through the TriplitClient.vars.$token
. If your collections have fields that are set to identifiable information from the token, you can access it there and use it in your calls to insert
or update
.
Using the blogPosts
example from above, you can set the authorId
field to the user's sub
claim when inserting a new post:
import { TriplitClient } from '@triplit/client';
const client = new TriplitClient({
serverUrl: 'https://<project-id>.triplit.io',
});
const token = await auth.getToken({ skipCache: true });
await client.startSession(token);
await client.insert('posts', {
title: 'My first blog post',
content: 'Hello world!',
authorId: client.vars.$token.sub, // set the authorId to the user's id
});
Add additional roles (optional).
Triplit has two default roles: authenticated
and anonymous
. The authenticated
role is used for any user that connect with a JWT that has a sub
claim. The anonymous
role is assigned to any client that connects with the Triplit-generated anon
token. This is a special token that is safe to use on the client side and should be used when no user is logged in.
You may find that you need to create additional roles for your application. For example, you may have an admin role that is distinct from a normal user. See the permissions guide for more information.
Debugging authentication
If you are having trouble connecting to the server, there are a few things to try:
- pass the
logLevel: 'debug'
option to theTriplitClient
constructor to get more information about the connection process and failures. - confirm that your
serverUrl
is correct and that the server is running. - check that the JWT being issued by your authentication provider is valid and has not expired. You can use jwt.io (opens in a new tab) to decode the JWT and check its claims.
- check that the your Triplit server is configured to accept the JWT. If you're using a third-party authentication provider, make sure that the public key or secret is set correctly in your Triplit server. If you're using a self-hosted server, make sure that the
EXTERNAL_JWT_SECRET
environmental variable is set.