Auth

Auth Hooks

Use HTTP or Postgres Functions to customize your authentication flow


What is a hook

A hook is an endpoint that allows you to alter the default Supabase Auth flow at specific execution points. Developers can use hooks to add custom behavior that's not supported natively.

Hooks help you:

  • Track the origin of user signups by adding metadata
  • Improve security by adding additional checks to password and multi-factor authentication
  • Support legacy systems by integrating with identity credentials from external authentication systems
  • Add additional custom claims to your JWT
  • Send authentication emails or SMS messages through a custom provider

The following hooks are available:

HookAvailable on Plan
Custom Access TokenFree, Pro
Send SMSFree, Pro
Send EmailFree, Pro
MFA Verification AttemptTeams and Enterprise
Password Verification AttemptTeams and Enterprise

Supabase supports 2 ways to configure a hook in your project:

A postgres function can be configured as a hook. The function should take in a single argument -- the event of type JSONB -- and return a JSONB object. Since the postgres function runs on your database, the request does not leave your project's instance.

Security Model

Sign the payload and grant permissions selectively in order to guard the integrity of the payload.

You will need to assign additional permissions so that Supabase Auth can access the hook as well as the tables it interacts with.

The supabase_auth_admin role does not have permissions to the public schema. You need to grant the role permission to execute your hook function:


_10
grant execute
_10
on function public.custom_access_token_hook
_10
to supabase_auth_admin;

You also need to grant usage to supabase_auth_admin:


_10
grant usage on schema public to supabase_auth_admin;

Also revoke permissions from the authenticated, public, and anon roles to ensure the function is not accessible by Supabase Serverless APIs. The public role has access to functions created on public.* by default and anon and authenticated inherit permissions from the public role. Permission is revoked from the public role in order to prevent the anon and authenticated roles from inheriting permissions to invoke the Postgres Hook.


_10
revoke execute
_10
on function public.custom_access_token_hook
_10
from authenticated, anon, public;

For security, we recommend against the use of the security definer tag. The security definer tag specifies that the function is to be executed with the privileges of the user that owns it. When a function is created via the Supabase dashboard with the tag, it will have the extensive permissions of the postgres role which make it easier for undesirable actions to occur.

We recommend that you do not use any tag and explicitly grant permissions to supabase_auth_admin as described above.

Read more about the security definer tag in our database guide.

Using Hooks

Developing

Let us develop a Hook locally and then deploy it to the cloud. As a recap, here’s a list of available Hooks

HookSuggested Function NameWhen it is calledWhat it Does
Send SMSsend_smsEach time an SMS is sentAllows you to customize message content and SMS Provider
Send Emailsend_emailEach time an Email is sentAllows you to customize message content and Email Provider
Custom Access Tokencustom_access_tokenEach time a new JWT is createdReturns the claims you wish to be present in the JWT.
MFA Verification Attemptmfa_verification_attemptEach time a user tries to verify an MFA factor.Returns a decision on whether to reject the attempt and future ones, or to allow the user to keep trying.
Password Verification Attemptpassword_verification_attemptEach time a user tries to sign in with a password.Return a decision whether to allow the user to reject the attempt, or to allow the user to keep trying.

Edit config.toml to set up the Auth Hook locally.

Modify the auth.hook.<hook_name> field and set uri to a value of pg-functions://postgres/<schema>/<function_name>


_10
[auth.hook.<hook_name>]
_10
enabled = true
_10
uri = "pg-functions://...."

You need to assign additional permissions so that Supabase Auth can access the hook as well as the tables it interacts with.

The supabase_auth_admin role does not have permissions to the public schema. You need to grant the role permission to execute your hook:


_10
grant execute
_10
on function public.custom_access_token_hook
_10
to supabase_auth_admin;

You also need to grant usage to supabase_auth_admin:


_10
grant usage on schema public to supabase_auth_admin;

Also revoke permissions from the authenticated and anon roles to ensure the function is not accessible by Supabase Serverless APIs.


_10
revoke execute
_10
on function public.custom_access_token_hook
_10
from authenticated, anon;

For security, we recommend against the use the security definer tag. The security definer tag specifies that the function is to be executed with the privileges of the user that owns it. When a function is created via the Supabase dashboard with the tag, it will have the extensive permissions of the postgres role which make it easier for undesirable actions to occur.

We recommend that you do not use any tag and explicitly grant permissions to supabase_auth_admin as described above.

Read more about security definer tag in our database guide.

There are no restrictions as to what language can be used to write a Postgres Hook. If PL/pgSQL is too difficult consider using the plv8 extension which lets you use JavaScript to define functions.

Once done, save your Auth Hook as a migration in order to version the Auth Hook and share it with other team members. Run supabase migration new to create a migration.

Here is an example hook signature:


_11
create or replace function public.custom_access_token_hook(event jsonb)
_11
returns jsonb
_11
language plpgsql
_11
as $$
_11
declare
_11
-- Insert variables here
_11
begin
_11
-- Insert logic here
_11
return event;
_11
end;
_11
$$;

You can visit SQL Editor > Templates for hook templates.

Deploying

In the dashboard, navigate to Authentication > Hooks and select the appropriate function type (SQL or HTTP) from the dropdown menu.

Error Handling

You should return an error when facing a runtime error. Runtime errors are specific to your application and arise from specific business rules rather than programmer errors.

Runtime errors could happen when:

  • The user does not have appropriate permissions
  • The event payload received does not have required claims.
  • The user has performed an action which violates a business rule.
  • The email or phone provider used in the webhook returned an error.

The error is a JSON object and has the following properties:

  • error An object that contains information about the error.
    • http_code A number indicating the HTTP code to be returned. If not set, the code is HTTP 500 Internal Server Error.
    • message A message to be returned in the HTTP response. Required.

Here's an example:


_10
{
_10
"error": {
_10
"http_code": 429,
_10
"message": "You can only verify a factor once every 10 seconds."
_10
}
_10
}

Errors returned from a Postgres Hook are not retry-able. When an error is returned, the error is propagated from the hook to Supabase Auth and translated into a HTTP error which is returned to your application. Supabase Auth will only take into account the error and disregard the rest of the payload.

Outside of runtime errors, both HTTP Hooks and Postgres Hooks return timeout errors. Postgres Hooks have 2 seconds to complete processing while HTTP Hooks should complete in 5 seconds. Both HTTP Hooks and Postgres Hooks are run in a transaction do limit the duration of execution to avoid delays in authentication process.

Available Hooks

Each Hook description contains an example JSONSchema which you can use in conjunction with JSONSchema Faker in order to generate a mock payload. For HTTP Hooks, you can also use the Standard Webhooks Testing Tool to simulate a request.