├── 01_setup_private_keys.sql ├── 02_generate_auth_link_function.sql ├── README.md ├── test_invite.sql ├── test_magiclink.sql ├── test_recovery.sql └── test_signup.sql /01_setup_private_keys.sql: -------------------------------------------------------------------------------- 1 | CREATE SCHEMA IF NOT EXISTS private; 2 | CREATE TABLE IF NOT EXISTS private.keys ( 3 | key text primary key not null, 4 | value text 5 | ); 6 | REVOKE ALL ON TABLE private.keys FROM PUBLIC; 7 | 8 | /******************************************************* 9 | * IMPORTANT: INSERT YOUR KEYS IN THE COMMANDS BELOW * 10 | ******************************************************** 11 | 12 | -- [SUPABASE_API_URL] 13 | -- Supabase Dashboard / settings / api / config / url 14 | 15 | -- [SUPABASE_SERVICE_KEY] 16 | -- Supabase Dashboard / settings / api / sevice_role (secret) 17 | 18 | ************************************************************************ 19 | *** NOTE: the service_role key is sensitive, DO NOT SHARE IT PUBICLY *** 20 | ************************************************************************/ 21 | 22 | INSERT INTO private.keys (key, value) values ('SUPABASE_API_URL', '[SUPABASE_API_URL]'); 23 | INSERT INTO private.keys (key, value) values ('SUPABASE_SERVICE_KEY', '[SUPABASE_SERVICE_KEY]'); 24 | 25 | -------------------------------------------------------------------------------- /02_generate_auth_link_function.sql: -------------------------------------------------------------------------------- 1 | drop function if exists private.generate_auth_link; 2 | create extension if not exists http; 3 | create schema if not exists private; 4 | create or replace function private.generate_auth_link(payload json) 5 | returns json language plpgsql security definer as 6 | $$ 7 | declare 8 | retval text; 9 | SUPABASE_API_URL text; 10 | SUPABASE_SERVICE_KEY text; 11 | begin 12 | 13 | SELECT value::text INTO SUPABASE_API_URL FROM private.keys WHERE key = 'SUPABASE_API_URL'; 14 | IF NOT found THEN RAISE 'missing entry in private.keys: SUPABASE_API_URL'; END IF; 15 | 16 | SELECT value::text INTO SUPABASE_SERVICE_KEY FROM private.keys WHERE key = 'SUPABASE_SERVICE_KEY'; 17 | IF NOT found THEN RAISE 'missing entry in private.keys: SUPABASE_SERVICE_KEY'; END IF; 18 | 19 | SELECT 20 | content INTO retval 21 | FROM 22 | http (('POST', 23 | SUPABASE_API_URL || '/auth/v1/admin/generate_link', 24 | ARRAY[ 25 | http_header ('Authorization', 'Bearer ' || SUPABASE_SERVICE_KEY), 26 | http_header ('apikey', SUPABASE_SERVICE_KEY) 27 | ], 28 | 'application/json', 29 | payload::text 30 | )); 31 | 32 | return retval; 33 | 34 | end; 35 | $$ 36 | revoke all on function private.generate_auth_link from public; 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # supabase-roll-your-own-auth 2 | Custom Supabase authentication using PostgreSQL functions 3 | 4 | This is a set of PostgreSQL functions that perform customizable Supabase auth functions. 5 | 6 | ## function: generate_auth_link(payload json) 7 | This is the core function that generates a necessary link for the following purposes: 8 | 9 | - invite (used in a user invite email) 10 | - magiclink (used for password-less sign in emails) 11 | - recovery (used for password recovery emails) 12 | - signup (used for confirming email address for email signups) 13 | 14 | ### generate an invite link 15 | Send a JSON object to the function `private.generate_auth_link` function as follows: 16 | 17 | ```sql 18 | select private.generate_auth_link( 19 | '{ 20 | "type": "invite", 21 | "email": "user@host.com", 22 | "data": { "foo": "bar2" }, 23 | "redirect_to": "http://localhost:3000" 24 | }' 25 | ); 26 | ``` 27 | 28 | #### Notes 29 | - according to the docs at [https://github.com/supabase/gotrue#post-admingenerate_link](https://github.com/supabase/gotrue#post-admingenerate_link) the `data` parameter should not work here, but in my tests, it does 30 | - the `redirect_to` parameter can be customized for your application, including a complete path 31 | 32 | ### generate a magiclink (password-less login) 33 | Send a JSON object to the function `private.generate_auth_link` function as follows: 34 | 35 | ```sql 36 | select private.generate_auth_link( 37 | '{ 38 | "type": "magiclink", 39 | "email": "user@host.com", 40 | "redirect_to": "http://localhost:3000" 41 | }' 42 | ); 43 | ``` 44 | 45 | #### Notes 46 | - according to the docs at [https://github.com/supabase/gotrue#post-admingenerate_link](https://github.com/supabase/gotrue#post-admingenerate_link) the `data` parameter should not work here, but in my tests, it does 47 | - the `redirect_to` parameter can be customized for your application, including a complete path 48 | 49 | ### generate a recovery link (password recovery) 50 | Send a JSON object to the function `private.generate_auth_link` function as follows: 51 | 52 | ```sql 53 | select private.generate_auth_link( 54 | '{ 55 | "type": "recovery", 56 | "email": "user@host.com", 57 | "redirect_to": "http://localhost:3000" 58 | }' 59 | ); 60 | ``` 61 | 62 | #### Notes 63 | - the `redirect_to` parameter can be customized for your application, including a complete path 64 | 65 | ### generate a signup link (email registration) 66 | Send a JSON object to the function `private.generate_auth_link` function as follows: 67 | 68 | ```sql 69 | select private.generate_auth_link( 70 | '{ 71 | "type": "signup", 72 | "email": "user@host.com", 73 | "password": "password123", 74 | "data": { "foo": "bar2" }, 75 | "redirect_to": "http://localhost:3000" 76 | }' 77 | ); 78 | ``` 79 | 80 | #### Notes 81 | - the `redirect_to` parameter can be customized for your application, including a complete path 82 | 83 | -------------------------------------------------------------------------------- /test_invite.sql: -------------------------------------------------------------------------------- 1 | select private.generate_auth_link( 2 | '{ 3 | "type": "invite", 4 | "email": "user@host.com", 5 | "data": { "foo": "bar2" }, 6 | "redirect_to": "http://localhost:3000" 7 | }' 8 | ); 9 | 10 | /* 11 | body: 12 | { 13 | "type": "signup" or "magiclink" or "recovery" or "invite", 14 | "email": "email@example.com", 15 | "password": "secret", // only if type = signup 16 | "data": { 17 | ... 18 | }, // only if type = signup 19 | "redirect_to": "https://supabase.io" // Redirect URL to send the user to after an email action. Defaults to SITE_URL. 20 | 21 | } 22 | */ -------------------------------------------------------------------------------- /test_magiclink.sql: -------------------------------------------------------------------------------- 1 | select private.generate_auth_link( 2 | '{ 3 | "type": "magiclink", 4 | "email": "user@host.com", 5 | "redirect_to": "http://localhost:3000" 6 | }' 7 | ); 8 | 9 | /* 10 | body: 11 | { 12 | "type": "signup" or "magiclink" or "recovery" or "invite", 13 | "email": "email@example.com", 14 | "password": "secret", // only if type = signup 15 | "data": { 16 | ... 17 | }, // only if type = signup 18 | "redirect_to": "https://supabase.io" // Redirect URL to send the user to after an email action. Defaults to SITE_URL. 19 | 20 | } 21 | */ -------------------------------------------------------------------------------- /test_recovery.sql: -------------------------------------------------------------------------------- 1 | select private.generate_auth_link( 2 | '{ 3 | "type": "recovery", 4 | "email": "user@host.com", 5 | "redirect_to": "http://localhost:3000" 6 | }' 7 | ); 8 | 9 | /* 10 | body: 11 | { 12 | "type": "signup" or "magiclink" or "recovery" or "invite", 13 | "email": "email@example.com", 14 | "password": "secret", // only if type = signup 15 | "data": { 16 | ... 17 | }, // only if type = signup 18 | "redirect_to": "https://supabase.io" // Redirect URL to send the user to after an email action. Defaults to SITE_URL. 19 | 20 | } 21 | */ -------------------------------------------------------------------------------- /test_signup.sql: -------------------------------------------------------------------------------- 1 | select private.generate_auth_link( 2 | '{ 3 | "type": "signup", 4 | "email": "user@host.com", 5 | "password": "password123", 6 | "data": { "foo": "bar2" }, 7 | "redirect_to": "http://localhost:3000" 8 | }' 9 | ); 10 | 11 | /* 12 | body: 13 | { 14 | "type": "signup" or "magiclink" or "recovery" or "invite", 15 | "email": "email@example.com", 16 | "password": "secret", // only if type = signup 17 | "data": { 18 | ... 19 | }, // only if type = signup 20 | "redirect_to": "https://supabase.io" // Redirect URL to send the user to after an email action. Defaults to SITE_URL. 21 | 22 | } 23 | */ --------------------------------------------------------------------------------