diff --git a/apps/docs/content/guides/auth/auth-hooks.mdx b/apps/docs/content/guides/auth/auth-hooks.mdx index 0286afb148f..04fef7cbb09 100644 --- a/apps/docs/content/guides/auth/auth-hooks.mdx +++ b/apps/docs/content/guides/auth/auth-hooks.mdx @@ -313,23 +313,37 @@ Hooks return status codes based on the nature of the response. These status code | HTTP Status Code | Description | Example Usage | | ---------------- | ------------------------------------------------------------- | ---------------------------------------------- | | 200, 202, 204 | Valid response, proceed | Successful processing of the request | -| 429, 503 | Retry-able errors with Retry-after header supported | Temporary server overload or maintenance | | 403, 400 | Treated as Internal Server Errors and return a 500 Error Code | Malformed requests or insufficient permissions | +| 429, 503 | Retry-able errors | Temporary server overload or maintenance | -Errors are responses which contain status codes 400 and above. On a retry-able error, such as an error with a `429` or `503` status code, HTTP Hooks will attempt up to three retries with a back-off of two seconds. + + +`204` Status is not supported by the following hooks which require a response body: + +- [Custom Access Token](/docs/guides/auth/auth-hooks/custom-access-token-hook) +- [MFA Verification Attempt](/docs/guides/auth/auth-hooks/mfa-verification-hook) +- [Password Verification Attempt](/docs/guides/auth/auth-hooks/password-verification-hook) + + + +Errors are responses which contain status codes 400 and above. On a retry-able error, such as an error with a `429` or `503` status code, HTTP Hooks will attempt up to three retries with a back-off of two seconds. We have a time budget of 5s for the entire webhook invocation, including retry requests. Here's a sample HTTP retry schedule: -| Time Since Start (HH:MM:SS) | Event | Notes | -| --------------------------- | ----------------------- | ------------------------------------------------- | -| 00:00:00 | Initial Attempt | Initial invocation begins. | -| 00:00:05 | Initial Attempt Timeout | Initial invocation must complete. | -| 00:00:07 | Retry Start #1 | After 2 sec delay, first retry begins. | -| 00:00:12 | Retry Timeout #1 | First retry timeout. | -| 00:00:14 | Retry Start #2 | After 2 sec delay, second retry begins. | -| 00:00:19 | Retry Timeout #2 | Second retry timeout. Returns an error on failure | +| Time Since Start (HH:MM:SS) | Event | Notes | +| --------------------------- | --------------------- | -------------------------------------------------------------------------------- | +| 00:00:00 | Initial Attempt | Initial invocation begins. | +| 00:00:02 | Initial Attempt Fails | Initial invocation returns `429` or `503` with non-empty `retry-after` header. | +| 00:00:04 | Retry Start #1 | After 2 sec delay, first retry begins. | +| 00:00:05 | Retry Timeout #1 | First retry times out, exceeded 5 second budget and invocation returns an error. | -Return a retry-able error by attaching a appropriate status code (`429`, `503` ) and a non-empty `retry-after` header +Return a retry-able error by attaching a appropriate status code (`429`, `503`) and a non-empty `retry-after` header + + + +`Retry-After` Supabase Auth does not fully support the `Retry-After` header as described in RFC7231, we only check if it is a non-empty value such as `true` or `10`. Setting this to your preferred value is fine as a future update may address this. + + ```jsx return new Response( diff --git a/apps/docs/content/guides/auth/auth-hooks/send-email-hook.mdx b/apps/docs/content/guides/auth/auth-hooks/send-email-hook.mdx index d18fd214ab2..7b765a018ca 100644 --- a/apps/docs/content/guides/auth/auth-hooks/send-email-hook.mdx +++ b/apps/docs/content/guides/auth/auth-hooks/send-email-hook.mdx @@ -17,6 +17,33 @@ Email sending depends on two settings: Email Provider and Auth Hook status. | Disabled | Enabled | Email Signups Disabled | | Disabled | Disabled | Email Signups Disabled | +## Email change behavior and token hash mapping + +When `email_action_type` is `email_change`, the hook payload can include one or two OTPs and their hashes. This depends on your [Secure Email Change](/dashboard/project/_/auth/providers?provider=Email) setting. + +- Secure Email Change enabled: two OTPs are generated, one for the current email (`user.email`) and one for the new email (`user.email_new`). You must send two emails. +- Secure Email Change disabled: only one OTP is generated for the new email. You send a single email. + + + +Important quirk (backward compatibility): + +- `email_data.token_hash_new` = Hash(`user.email`, `email_data.token`) +- `email_data.token_hash` = Hash(`user.email_new`, `email_data.token_new`) + +This naming is historical and kept for backward compatibility. Do not assume that the `_new` suffix refers to the new email. + + + +### What to send + +If both `token_hash` and `token_hash_new` are present, send two messages: + +- To the current email (`user.email`): use `token` with `token_hash_new`. +- To the new email (`user.email_new`): use `token_new` with `token_hash`. + +If only one token/hash pair is present, send a single email. In non-secure mode, this is typically the new email OTP. Use `token` with `token_hash` or `token_new` with `token_hash`, depending on which fields are present in the payload. + **Inputs** | Field | Type | Description | @@ -282,7 +309,15 @@ Email sending depends on two settings: Email Provider and Auth Hook status. }, "email_action_type": { "type": "string", - "enum": ["signup", "invite", "magiclink", "recovery", "email_change", "email"] + "enum": [ + "signup", + "invite", + "magiclink", + "recovery", + "email_change", + "email", + "reauthentication" + ] }, "site_url": { "type": "string", @@ -578,6 +613,7 @@ import { readAll } from 'https://deno.land/std/io/read_all.ts' const postmarkEndpoint = 'https://api.postmarkapp.com/email' // Replace this with your email const FROM_EMAIL = 'myemail@gmail.com' +const PROJECT_REF = '' // Email Subjects const subjects = { @@ -642,8 +678,14 @@ const templates = { } function generateConfirmationURL(email_data) { - // TODO: replace the ref with your project ref - return `https://.supabase.co/auth/v1/verify?token=${email_data.token_hash}&type=${email_data.email_action_type}&redirect_to=${email_data.redirect_to}` + const baseUrl = `https://${PROJECT_REF}.supabase.co/auth/v1/verify` + const params = new URLSearchParams({ + token: email_data.token_hash, + type: email_data.email_action_type, + redirect_to: email_data.redirect_to, + }) + + return `${baseUrl}?${params.toString()}` } Deno.serve(async (req) => {