Using Clerk as your Identity Provider with Grafbase

Using Clerk as your Identity Provider with Grafbase

Clerk provides all the necessary components to add authentication to your frontend in minutes, with only a few lines of code!

Grafbase provides the ability to configure rules to allow accessing data stored in your backend for signed-in users, specific groups of users, and rules for only reading, creating, updating, or deleting data.

In this guide we'll show you how to configure Clerk as your Grafbase Identity Provider for authenticating, and authorizing requests.

Update: Since this guide was first published Clerk have updated how they do API keys. "Issuer URL" is the first part of the JWKS URL which can be found inside the API keys > Advanced > JWT public key.

Start by creating a new application, or using an existing application from your Clerk Dashboard.

Create new Clerk Application

Here you can configure your authentication preferences, and more.

After you've created a new application, create a new JWT Template:

Create new JWT template

Give your template a name, and configure the token lifetime:

Add JWT template name

You can optionally configure the Claims for your JWT if you opt to use Grafbase group based authorization.

Configure JWT claims

Make a copy of your JWT template name.

Now grab the first part of the JWKS URL. This can be found inside API keys > Advanced > JWT public key. We'll use this for the issuer value later.

JWKS URL

Inside of grafbase/schema.graphql we'll use the @auth directive to configure Clerk as our provider.

schema @auth(providers: [{ type: oidc, issuer: "{{ env.CLERK_ISSUER_URL }}" }]) { query: Query }

Then we can configure the rules, as well as operations for accessing data.

We can restrict access to signed-in users only, as well as specific groups of users, and even control the types of operations users can perform — read, get, list, create, update, and delete.

schema @auth( providers: [{ type: oidc, issuer: "{{ env.CLERK_ISSUER_URL }}" }] rules: [{ allow: private }] ) { query: Query }

If you don't have a project with Grafbase already, you can get started locally using the CLI.

npx grafbase init

Next add the Issuer URL to the file grafbase/.env:

CLERK_ISSUER_URL=
npx grafbase dev

Alternatively, add your CLERK_ISSUER_URL as a Environment Variable inside your Grafbase project settings.

Depending on your frontend stack, you'll want to use the Clerk SDK to get a valid token.

The token from Clerk must be sent as a header in the format of authorization: Bearer TOKEN along with every request to your Grafbase GraphQL API.

The schema models for Post and Comment as well as a rule for signed-in users.

Use a schema similar to the following to restrict access to only signed-in users:

schema @auth( providers: [{ type: oidc, issuer: "{{ env.CLERK_ISSUER_URL }}" }] rules: [{ allow: private }] ) { query: Query } type Post @model { title: String! comments: [Comment!]! } type Comment @model { message: String! post: Post! }

The schema above will generate a query to fetch by collection. For example, the following query fetches all Posts:

{ postCollection(first: 100) { edges { node { id title comments(first: 10) { edges { node { id message } } } } } } }

For a frontend example, we'll use the @clerk/nextjs library to handle fetching tokens for our configured JWT template.

You can make any fetch request to your Grafbase API URL (local, or hosted) and pass along the authorization HTTP header.

Here we can await getToken({ template: 'grafbase' }) passing along the name of our JWT template we created earlier:

await fetch(process.env.NEXT_PUBLIC_GRAFBASE_API_URL as string, { method: 'POST', headers: { 'content-type': 'application/json', authorization: `Bearer ${await getToken({ template: 'grafbase', })}`, }, body: JSON.stringify({ query }), })

Here's what the full example looks like with Next.js:

import { useState } from 'react' import { useAuth } from '@clerk/nextjs' const query = ` { postCollection(first: 100) { edges { node { id title comments(first: 10) { edges { node { id message } } } } } } } ` const SchemaPage = () => { const [data, setData] = useState() const { getToken } = useAuth() const fetchData = async () => { await fetch(process.env.NEXT_PUBLIC_GRAFBASE_API_URL as string, { method: 'POST', headers: { 'Content-Type': 'application/json', authorization: `Bearer ${await getToken({ template: 'grafbase', })}`, }, body: JSON.stringify({ query }), }).then(res => res.json().then(({ data }) => setData(data))) } return ( <div> <button onClick={fetchData}>Fetch data</button> <pre>{JSON.stringify({ data }, null, 2)}</pre> </div> ) } export default SchemaPage

Now finish by creating .env in the root of your project that contains the values for the following:

NEXT_PUBLIC_GRAFBASE_API_URL= NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= # Optional secret key CLERK_SECRET_KEY= # Clerk Legacy API keys use the following: NEXT_PUBLIC_CLERK_FRONTEND_API=

The value NEXT_PUBLIC_GRAFBASE_API_URL should be http://localhost:4000/graphql when working locally with the Grafbase CLI.

That's it! You should now be able to authenticate with your Grafbase backend using tokens issued by Clerk.

Get Started

Start using Clerk as your Identity Provider.