• Stars
    star
    254
  • Rank 159,588 (Top 4 %)
  • Language
    JavaScript
  • Created over 2 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

How to implement custom claims with Supabase

Supabase Custom Claims

Want to know more about Custom Claims? See the FAQ below.

This is just one way to implement custom claims for a Supabase project. The goal here is simply to add JSON data to the access token that an authenticated user receives when logging into your application. That token (and thus the custom claims contained in that token) can be read and used by both your application and by your PostgreSQL database server. These custom claims are stored in the raw_app_meta_data field of the users table in the auth schema. (auth.users.raw_app_meta_data)

Installing the Functions

The file install.sql contains all the PostgreSQL functions you need to implement and manage custom claims in your Supabase project.

  1. Paste the SQL code from install.sql into the SQL Query Editor of your Supabase project.
  2. Click RUN to execute the code.

Uninstalling the Functions

  1. Paste the SQL code from uninstall.sql into the SQL Query Editor of your Supabase project.
  2. Click RUN to execute the code.

Security Considerations

If you want to tighten security so that custom claims can only be set or deleted from inside the query editor or inside your PostgreSQL functions or triggers, edit the function is_claims_admin() to disallow usage by app users (no usage through the API / Postgrest). Instructions are included in the function.

By default, usage is allowed through your API, but the ability to set or delete claims is restricted to only users who have the claims_admin custom claim set to true. This allows you to create an "admin" section of your app that allows designated users to modify custom claims for other users of your app.

Bootstrapping

If the only way to set or delete claims requires the claims_admin claim to be set to true and no users have that claim, how can I edit custom claims from within my app?

The answer is to "bootstrap" a user by running the following command inside your Supabase Query Editor window:

select set_claim('03acaa13-7989-45c1-8dfb-6eeb7cf0b92e', 'claims_admin', 'true');

where 03acaa13-7989-45c1-8dfb-6eeb7cf0b92e is the id of your admin user found in auth.users.

Usage

Inside the Query Editor

You can get, set, and delete claims for any user based on the user's id (uuid) with the following functions:

get_claims(uid uuid) returns jsonb

example

select get_claims('03acaa13-7989-45c1-8dfb-6eeb7cf0b92e');

result
| get_claims                                                                                                                                                                 |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| {"provider": "email", "userrole": "MANAGER", "providers": ["email"], "userlevel": 100, "useractive": true, "userjoined": "2022-05-20T14:07:27.742Z", "claims_admin": true} |

get_claim(uid uuid, claim text) returns jsonb

example

select get_claim('03acaa13-7989-45c1-8dfb-6eeb7cf0b92e', 'userlevel');

result
| get_claim |
| --------- |
| 100       |

set_claim(uid uuid, claim text, value jsonb) returns text

example

Set a number value. (Note value is passed as a jsonb value, so to set a number we need to pass it as a simple string.) select set_claim('03acaa13-7989-45c1-8dfb-6eeb7cf0b92e', 'userlevel', '200');

Set a text value. (Note value is passed as a jsonb value, so to set a number we need to pass it with double-quotes.) select set_claim('03acaa13-7989-45c1-8dfb-6eeb7cf0b92e', 'userrole', '"MANAGER"');

Common Mistake: If you forget the double-quotes for a string, and try to do this: select set_claim('03acaa13-7989-45c1-8dfb-6eeb7cf0b92e', 'userrole', 'MANAGER');, the result will be an error: invalid input syntax for type json

Set a boolean value. select set_claim('03acaa13-7989-45c1-8dfb-6eeb7cf0b92e', 'useractive', 'true');

Set an array value. select set_claim('03acaa13-7989-45c1-8dfb-6eeb7cf0b92e', 'items', '["bread", "cheese", "butter"]');

Set a complex, nested json / object value. select set_claim('03acaa13-7989-45c1-8dfb-6eeb7cf0b92e', 'gamestate', '{"level": 5, "items": ["knife", "gun"], "position":{"x": 15, "y": 22}}');

result (for any of the above)
| set_claim |
| --------- |
| OK        |

delete_claim(uid uuid, claim text) returns text

example

select delete_claim('03acaa13-7989-45c1-8dfb-6eeb7cf0b92e', 'gamestate');

result
| delete_claim |
| ------------ |
| OK           |

Inside PostgreSQL Functions and Triggers

When using custom claims from inside a PostgreSQL function or trigger, you can use any of the functions shown in the section above: Inside the Query Editor.

In addition, you can use the following functions that are specific to the currently logged-in user:

is_claims_admin() returns bool

example

select is_claims_admin();

result
| is_claims_admin |
| --------------- |
| true            |

get_my_claims() returns jsonb

example

select get_my_claims();

result
| get_my_claims                                                                                                                                                              |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| {"provider": "email", "userrole": "MANAGER", "providers": ["email"], "userlevel": 100, "useractive": true, "userjoined": "2022-05-20T14:07:27.742Z", "claims_admin": true} |

get_my_claim(claim TEXT) returns jsonb

example

select get_my_claim('userlevel');

result
| get_my_claim |
| ------------ |
| 100          |

Inside an RLS (Row Level Security) Policy

To use custom claims in an RLS Policy, you'll normally use the get_my_claim to check a specific claim for the currently logged in user.

examples

only allow users with userrole "MANAGER"

get_my_claim('userrole') = '"MANAGER"' (which the UI will change into the more formal): ((get_my_claim('userrole'::text)) = '"MANAGER"'::jsonb)

only allow users with userlevel over 100

coalesce(get_my_claim('userlevel')::numeric,0) > 100

only allow users with claim_admin = true

coalesce(get_my_claim('claims_admin')::bool,false)

Inside your app (using .rpc())

Getting Claims Data from Local Session Data

You can extract claims information from the session object you get when the user is logged in. For example:

		supabase.auth.onAuthStateChange((_event, session) => {
            if (session?.user) {
    			console.log(session?.user?.app_metadata) // show custom claims
            }
		})

If any claims have changed since your last log in, you may need to log out and back in to see these changes.

Getting Claims Data from the Server

You can also query the server to see what claims are set for the current user.

Here are some sample functions that can be used by any authenticated (logged-in) user of your application:

  public get_my_claims = async () => {
    const { data, error } = await supabase
    .rpc('get_my_claims', {});
    return { data, error };
  }
  public get_my_claim = async (claim: string) => {
    const { data, error } = await supabase
    .rpc('get_my_claim', {claim});
    return { data, error };
  }
  public is_claims_admin = async () => {
    const { data, error } = await supabase
    .rpc('is_claims_admin', {});
    return { data, error };
  }

The following functions can only be used by a "claims admin", that is, a user who has the claims_admin custom claim set to true:

(Note: these functions allow you to view, set, and delete claims for any user of your application, so these would be appropriate for an administrative branch of your application to be used only by high-level users with the proper security rights (i.e. claims_admin level users.))

  public get_claims = async (uid: string) => {
    const { data, error } = await supabase
    .rpc('get_claims', {uid});
    return { data, error };
  }
  public get_claim = async (uid: string, claim: string) => {
    const { data, error } = await supabase
    .rpc('get_claim', {uid, claim});
    return { data, error };
  }
  public set_claim = async (uid: string, claim: string, value: object) => {
    const { data, error } = await supabase
    .rpc('set_claim', {uid, claim, value});
    return { data, error };
  }
  public delete_claim = async (uid: string, claim: string) => {
    const { data, error } = await supabase
    .rpc('delete_claim', {uid, claim});
    return { data, error };
  }

Running an older project (Postgres 13 or earlier?)

See this issue

FAQ

What are custom claims?

Custom Claims are special attributes attached to a user that you can use to control access to portions of your application.

For example:

plan: "TRIAL"
user_level: 100
group_name: "Super Guild!"
joined_on: "2022-05-20T14:28:18.217Z"
group_manager: false
items: ["toothpick", "string", "ring"]

What type of data can I store in a custom claim?

Any valid JSON data can be stored in a claim. You can store a string, number, boolean, date (as a string), array, or even a complex, nested, complete JSON object.

Where are these custom claims stored?

Custom claims are stored in the auth.users table, in the raw_app_meta_data column for a user.

Are there any naming restrictions?

The Supabase Auth System (GoTrue) currently uses the following custom claims: provider and providers, so DO NOT use these. Any other valid string should be ok as the name for your custom claim(s), though.

Why use custom claims instead of just creating a table?

Performance, mostly. Custom claims are stored in the security token a user receives when logging in, and these claims are made available to the PostgreSQL database as a configuration parameter, i.e. current_setting('request.jwt.claims', true). So the database has access to these values immediately without needing to do any disk i/o.

This may sound trivial, but this could have a significant effect on scalability if you use claims in an RLS (Row Level Security) Policy, as it could potentially eliminate thousands (or even millions) of database calls.

What are the drawbacks to using custom claims?

One drawback is that claims don't get updated automatically, so if you assign a user a new custom claim, they may need to log out and log back in to have the new claim available to them. The same goes for deleting or changing a claim. So this is not a good tool for storing data that changes frequently.

You can force a refresh of the current session token by calling supabase.auth.refreshSession() on the client, but if a claim is changed by a server process or by a claims adminstrator manually, there's no easy way to notify the user that their claims have changed. You can provide a "refresh" button or a refresh function inside your app to update the claims at any time, though.

How can I write a query to find all the users who have a specific custom claim set?

examples

find all users who have claims_admin set to true

select * from auth.users where (auth.users.raw_app_meta_data->'claims_admin')::bool = true;

find all users who have a userlevel over 100

select * from auth.users where (auth.users.raw_app_meta_data->'userlevel')::numeric > 100;

find all users whose userrole is set to "MANAGER"

(note for strings you need to add double-quotes becuase data is data is stored as JSONB) select * from auth.users where (auth.users.raw_app_meta_data->'userrole')::text = '"MANAGER"';

What's the difference between auth.users.raw_app_meta_data and auth.users.raw_user_meta_data?

The auth.users table used by Supabase Auth (GoTrue) has both raw_app_meta_data and a raw_user_meta_data fields.

raw_user_meta_data is designed for profile data and can be created and modified by a user. For example, this data can be set when a user signs up: sign-up-with-additional-user-meta-data or this data can be modified by a user with auth-update

raw_app_meta_data is designed for use by the application layer and is used by GoTrue to handle authentication (For exampple, the provider and providers claims are used by GoTrue to track authentication providers.) raw_app_meta_data is not accessible to the user by default.

NOTES:

updating claims from a server process or edge function

https://supabase.com/docs/reference/javascript/auth-api-updateuserbyid#updates-a-users-app_metadata

Warning

Be sure to watch for reserved claims in your particular development environment. For example, the claims exp and role are reserved by the Supabase Realtime system and can cause problems if you try use these names. To avoid these potential problems, it's good practice to use a custom identifier in your custom claims, such as MY_COMPANY_item1, MY_COMPANY_item2, etc.

More Repositories

1

postgres_lsp

A Language Server for Postgres
Rust
3,188
star
2

nextjs-openai-doc-search

Template for building your own custom ChatGPT style doc search powered by Next.js, OpenAI, and Supabase.
TypeScript
1,249
star
3

copycat

Generate deterministic fake values: The same input will always generate the same fake-output.
TypeScript
734
star
4

supabase-csharp

A C# Client library for Supabase
C#
452
star
5

supabase-on-aws

Self-hosted Supabase on AWS
TypeScript
382
star
6

supabase-kt

A Kotlin Multiplatform Client for Supabase.
Kotlin
364
star
7

sql-examples

Curated list of SQL to help you find useful script easily 🚀
Vue
349
star
8

postgrest-rs

Rust client for PostgREST
Rust
328
star
9

chatgpt-your-files

Production-ready MVP for securely chatting with your documents using pgvector
TypeScript
277
star
10

snapshot

Capture a snapshot (or subset) of your Postgres database whilst transforming the data.
TypeScript
211
star
11

supabase-graphql-example

A HackerNews-like clone built with Supabase and pg_graphql
PLpgSQL
207
star
12

supabase-kubernetes

Helm 3 charts to deploy a Supabase on Kubernetes
Smarty
206
star
13

svelte-kanban

PLpgSQL
203
star
14

nuxt-supabase

A supa simple wrapper around Supabase.js to enable usage within Nuxt.
TypeScript
166
star
15

godot-engine.supabase

A lightweight addon which integrates Supabase APIs for Godot Engine out of the box.
GDScript
159
star
16

postgrest-go

Isomorphic Go client for PostgREST. (Now Updating)
Go
125
star
17

supabase-ui-svelte

Supabase authentication UI for Svelte
Svelte
120
star
18

postgrest-csharp

A C# Client library for Postgrest
C#
114
star
19

firebase-to-supabase

Firebase to Supabase Migration Guide
JavaScript
100
star
20

vue-supabase

A supa simple wrapper around Supabase.js to enable usage within Vue.
TypeScript
86
star
21

expo-stripe-payments-with-supabase-functions

Bring the Func(🕺)
TypeScript
79
star
22

svelte-supabase

JavaScript
71
star
23

realtime-csharp

A C# client library for supabase/realtime.
C#
69
star
24

partner-gallery-example

Supabase Partner Gallery Example
TypeScript
67
star
25

supabase-sveltekit-example

Svelte
65
star
26

supabase-flutter-quickstart

Flutter implementation of the Quickstart Supabase User Management app.
C++
61
star
27

deno-fresh-openai-doc-search

Template for building your own custom ChatGPT style doc search powered by Fresh, Deno, OpenAI, and Supabase.
TypeScript
60
star
28

supabase-traefik

Python
57
star
29

flutter-stripe-payments-with-supabase-functions

Dart
55
star
30

auth-py

Python client implementation for Supabase Auth
Python
54
star
31

supabase-terraform

HCL
53
star
32

postgrest-kt

Postgrest Kotlin Client
Kotlin
52
star
33

flutter-auth-ui

Supabase Auth UI library for Flutter
Dart
49
star
34

supabase-rb

An isomorphic Ruby client for Supabase.
Ruby
46
star
35

realtime-swift

A Swift client for Supabase Realtime server.
Swift
45
star
36

supabase-by-example

TypeScript
44
star
37

flutter-chat

Simple chat application built with Flutter and Supabase.
Dart
44
star
38

postgrest-swift

Swift client for PostgREST
Swift
43
star
39

gotrue-csharp

C# implementation of Supabase's GoTrue
C#
38
star
40

supabase-vscode-extension

Supabase Extension for VS Code and GitHub Copilot.
TypeScript
35
star
41

postgrest-rb

Isomorphic Ruby client for PostgREST.
Ruby
32
star
42

gotrue-swift

A Swift client library for GoTrue.
Swift
32
star
43

storage-go

Storage util client for Supabase in Go
Go
31
star
44

vec2pg

Migrate vector workloads to Postgres
Python
29
star
45

chatgpt-plugin-template-deno

Template for building ChatGPT Plugins in TypeScript that run on Supabase's custom Deno Edge Runtime.
TypeScript
27
star
46

storage-swift

Swift client library to interact with Supabase Storage
Swift
25
star
47

base64url-js

Pure TypeScript implementation of Base64-URL encoding for JavaScript strings.
TypeScript
22
star
48

gotrue-kt

Kotlin Client for GoTrue API
Kotlin
21
star
49

storage-csharp

A C# implementation of Supabase's Object Storage API
C#
19
star
50

postgrest-ex

Elixir Client library for PostgREST
Elixir
18
star
51

gotrue-ex

An Elixir client for the GoTrue authentication service
Elixir
16
star
52

heroku-to-supabase

Heroku to Supabase Migration Guide
16
star
53

sql-to-rest

SQL to PostgREST translator
TypeScript
15
star
54

nuxt3-quickstarter

CSS
13
star
55

gotrue-go

Typed Golang cilent for the Supabase fork of GoTrue
Go
12
star
56

sveltekit-subscription-payments

Clone, deploy, and fully customize a SaaS subscription application with SvelteKit.
TypeScript
11
star
57

supabase-php

PHP
11
star
58

pg_headerkit

A set of functions for adding special features to your application that uses PostgREST API calls to your PostgreSQL database.
PLpgSQL
11
star
59

functions-csharp

C# client for interacting with Supabase Functions
C#
11
star
60

gotrue-java

A Java client library for the GoTrue API.
Java
10
star
61

onesignal

Next.js app showcasing how you can implement push notification using OneSignal and Supabase
TypeScript
10
star
62

postgres-wasm-fdw

A WebAssembly foreign data wrapper example project
Rust
9
star
63

functions-swift

Swift Client library to interact with Supabase Functions.
Swift
8
star
64

supabase-adminpack

A Trusted Language Extension containing a variety of useful databse admin queries.
8
star
65

functions-go

Golang client library to interact with Supabase Functions.
Go
7
star
66

unboring.sg

A website and browser extensions to discover things to eat, do, and learn.
TypeScript
7
star
67

supabase-community-bot

TypeScript
5
star
68

ai-writer

An AI writing assistant powered by OpenAI, Supabase, and Next.js
TypeScript
5
star
69

functions-py

Python
5
star
70

storage-java

A Java client library for the Supabase Storage API
Java
4
star
71

supabase-management-js

Convenience wrapper for the Supabase Management API: https://supabase.com/docs/reference/api/introduction
TypeScript
4
star
72

supabase-go

Go
4
star
73

deno-supabase-js

Using supabase-js in Deno.
4
star
74

launchweek.dev

Tracking launch weeks across the industry, for product discovery and inspiration.
JavaScript
3
star
75

.github

Supabase Community
3
star
76

core-swift

Shared interfaces and helpers for Swift libraries
Swift
2
star
77

supa-jam

TypeScript
2
star
78

nftree-garden

Mint an NFT, plant a Tree!
JavaScript
1
star
79

realtime-go

1
star
80

functions-rs

Rust
1
star
81

squad

1
star
82

gotrue-php

PHP
1
star
83

storage-php

PHP
1
star
84

postgrest-php

PHP
1
star
85

deno-storage-js

Repo to overwrite https://deno.land/x/storagee listing.
1
star
86

supabase-fastify-api

TypeScript
1
star
87

gotrue-fsharp

F# client for interacting with Supabase GoTrue
F#
1
star
88

realtime-php

1
star
89

core-py

Shared interfaces and helpers for the supabase-py libs
Python
1
star
90

hacktoberfest-hackathon-template

Template for the Supabase Hacktoberfest Hackathon that follows https://hacktoberfest.digitalocean.com/resources/maintainers
1
star
91

postgrest-java

1
star