Skip to content

Commit f10f76e

Browse files
issuedatdnywhChrisChinchilla
authored
feat(docs): add account changes notifications email customization (supabase#40141)
* feat(docs): add account changes notifications email customization * chore: update CLI config * chore: update CLI docs and template descriptions * customizing-email-template copywriting * send-email-hook copywriting * auth-email-templates edits * fix error * chore: cleanup example `email_data` * Undo formatting * Prettier * copy improvements --------- Co-authored-by: Danny White <[email protected]> Co-authored-by: Chris Chinchilla <[email protected]>
1 parent cf18948 commit f10f76e

File tree

4 files changed

+328
-66
lines changed

4 files changed

+328
-66
lines changed

apps/docs/content/guides/auth/auth-email-templates.mdx

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,48 @@ description: 'Learn how to manage the email templates in Supabase.'
44
subtitle: 'Learn how to manage the email templates in Supabase.'
55
---
66

7-
You can customize the email messages used for the authentication flows. You can edit the following email templates:
7+
Email templates in Supabase fall into two categories: authentication and security notifications.
88

9-
- Confirm signup
9+
Authentication emails:
10+
11+
- Confirm sign up
1012
- Invite user
11-
- Magic Link
12-
- Change Email Address
13-
- Reset Password
13+
- Magic link
14+
- Change email address
15+
- Reset password
16+
- Reauthentication
17+
18+
Security notification emails:
19+
20+
- Password changed
21+
- Email address changed
22+
- Phone number changed
23+
- Identity linked
24+
- Identity unlinked
25+
- Multi-factor authentication (MFA) method added
26+
- Multi-factor authentication (MFA) method removed
27+
28+
Security emails are only sent to users if the respective security notifications have been enabled at a project-level.
1429

1530
## Terminology
1631

1732
The templating system provides the following variables for use:
1833

1934
| Name | Description |
2035
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
21-
| `{{ .ConfirmationURL }}` | Contains the confirmation URL. For example, a signup confirmation URL would look like: `https://project-ref.supabase.co/auth/v1/verify?token={{ .TokenHash }}&type=email&redirect_to=https://example.com/path` . |
22-
| `{{ .Token }}` | Contains a 6-digit One-Time-Password (OTP) that can be used instead of the `{{. ConfirmationURL }}` . |
36+
| `{{ .ConfirmationURL }}` | Contains the confirmation URL. For example, a signup confirmation URL would look like: `https://project-ref.supabase.co/auth/v1/verify?token={{ .TokenHash }}&type=email&redirect_to=https://example.com/path`. |
37+
| `{{ .Token }}` | Contains a 6-digit One-Time-Password (OTP) that can be used instead of the `{{. ConfirmationURL }}`. |
2338
| `{{ .TokenHash }}` | Contains a hashed version of the `{{ .Token }}`. This is useful for constructing your own email link in the email template. |
2439
| `{{ .SiteURL }}` | Contains your application's Site URL. This can be configured in your project's [authentication settings](/dashboard/project/_/auth/url-configuration). |
2540
| `{{ .RedirectTo }}` | Contains the redirect URL passed when `signUp`, `signInWithOtp`, `signInWithOAuth`, `resetPasswordForEmail` or `inviteUserByEmail` is called. The redirect URL allow list can be configured in your project's [authentication settings](/dashboard/project/_/auth/url-configuration). |
2641
| `{{ .Data }}` | Contains metadata from `auth.users.user_metadata`. Use this to personalize the email message. |
2742
| `{{ .Email }}` | Contains the original email address of the user. Empty when when trying to [link an email address to an anonymous user](/docs/guides/auth/auth-anonymous#link-an-email--phone-identity). |
28-
| `{{ .NewEmail }}` | Contains the new email address of the user. This variable is only supported in the "Change Email Address" template. |
43+
| `{{ .NewEmail }}` | Contains the new email address of the user. This variable is only supported in the "Change email address" template. |
44+
| `{{ .OldEmail }}` | Contains the old email address of the user. This variable is only supported in the "Email address changed notification" template. |
45+
| `{{ .Phone }}` | Contains the new phone number of the user. This variable is only supported in the "Phone number changed notification" template. |
46+
| `{{ .OldPhone }}` | Contains the old phone address of the user. This variable is only supported in the "Phone number changed notification" template. |
47+
| `{{ .Provider }}` | Contains the provider of the newly linked/unlinked identity. This variable is only supported in the "Identity linked notification" and "Identity unlinked notification" templates. |
48+
| `{{ .FactorType }}` | Contains the type of the newly enrolled/unenrolled MFA method. This variable is only supported in the "MFA method added notification" and "MFA method removed notification" templates. |
2949

3050
## Editing email templates
3151

@@ -56,8 +76,31 @@ curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \
5676
"mailer_templates_recovery_content": "<h2>Reset Password</h2><p>Follow this link to reset the password for your user:</p><p><a href=\"{{ .ConfirmationURL }}\">Reset Password</a></p>",
5777
"mailer_subjects_invite": "You have been invited",
5878
"mailer_templates_invite_content": "<h2>You have been invited</h2><p>You have been invited to create a user on {{ .SiteURL }}. Follow this link to accept the invite:</p><p><a href=\"{{ .ConfirmationURL }}\">Accept the invite</a></p>",
79+
"mailer_subjects_reauthentication": "Confirm reauthentication",
80+
"mailer_templates_reauthentication_content": "<h2>Confirm reauthentication</h2><p>Enter the code: {{token}}</p>",
5981
"mailer_subjects_email_change": "Confirm email change",
6082
"mailer_templates_email_change_content": "<h2>Confirm email change</h2><p>Follow this link to confirm the update of your email:</p><p><a href=\"{{ .ConfirmationURL }}\">Change email</a></p>",
83+
"mailer_notifications_password_changed_enabled": true,
84+
"mailer_subjects_password_changed_notification": "Your password has been changed",
85+
"mailer_templates_password_changed_notification_content": "<h2>Your password has been changed</h2>\n\n<p>This is a confirmation that the password for your account {{ .Email }} has just been changed.</p>\n<p>If you did not make this change, please contact support.</p>",
86+
"mailer_notifications_email_changed_enabled": true,
87+
"mailer_subjects_email_changed_notification": "Your email address has been changed",
88+
"mailer_templates_email_changed_notification_content": "<h2>Your email address has been changed</h2>\n\n<p>The email address for your account has been changed from {{ .OldEmail }} to {{ .Email }}.</p>\n<p>If you did not make this change, please contact support.</p>",
89+
"mailer_notifications_phone_changed_enabled": true,
90+
"mailer_subjects_phone_changed_notification": "Your phone number has been changed",
91+
"mailer_templates_phone_changed_notification_content": "<h2>Your phone number has been changed</h2>\n\n<p>The phone number for your account {{ .Email }} has been changed from {{ .OldPhone }} to {{ .Phone }}.</p>\n<p>If you did not make this change, please contact support immediately.</p>",
92+
"mailer_notifications_mfa_factor_enrolled_enabled": true,
93+
"mailer_subjects_mfa_factor_enrolled_notification": "A new MFA factor has been enrolled",
94+
"mailer_templates_mfa_factor_enrolled_notification_content": "<h2>A new MFA factor has been enrolled</h2>\n\n<p>A new factor ({{ .FactorType }}) has been enrolled for your account {{ .Email }}.</p>\n<p>If you did not make this change, please contact support immediately.</p>",
95+
"mailer_notifications_mfa_factor_unenrolled_enabled": true,
96+
"mailer_subjects_mfa_factor_unenrolled_notification": "An MFA factor has been unenrolled",
97+
"mailer_templates_mfa_factor_unenrolled_notification_content": "<h2>An MFA factor has been unenrolled</h2>\n\n<p>A factor ({{ .FactorType }}) has been unenrolled for your account {{ .Email }}.</p>\n<p>If you did not make this change, please contact support immediately.</p>",
98+
"mailer_notifications_identity_linked_enabled": true,
99+
"mailer_subjects_identity_linked_notification": "A new identity has been linked",
100+
"mailer_templates_identity_linked_notification_content": "<h2>A new identity has been linked</h2>\n\n<p>A new identity ({{ .Provider }}) has been linked to your account {{ .Email }}.</p>\n<p>If you did not make this change, please contact support immediately.</p>",
101+
"mailer_notifications_identity_unlinked_enabled": true,
102+
"mailer_subjects_identity_unlinked_notification": "An identity has been unlinked",
103+
"mailer_templates_identity_unlinked_notification_content": "<h2>An identity has been unlinked</h2>\n\n<p>An identity ({{ .Provider }}) has been unlinked from your account {{ .Email }}.</p>\n<p>If you did not make this change, please contact support immediately.</p>"
61104
}'
62105
```
63106

apps/docs/content/guides/auth/auth-hooks/send-email-hook.mdx

Lines changed: 77 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,17 @@
11
---
22
id: 'send-email-hook'
33
title: 'Send Email Hook'
4-
subtitle: 'Use a custom email provider to send authentication messages'
4+
subtitle: 'Use a custom email provider to send authentication messages.'
55
---
66

7-
The Send Email Hook runs before an email is sent and allows for flexibility around email sending. You can use this hook to configure a back-up email provider or add internationalization to your emails.
8-
9-
## Email sending behavior
10-
11-
Email sending depends on two settings: Email Provider and Auth Hook status.
12-
13-
| Email Provider | Auth Hook | Result |
14-
| -------------- | --------- | -------------------------------------------------------------------- |
15-
| Enabled | Enabled | Auth Hook handles email sending (SMTP not used) |
16-
| Enabled | Disabled | SMTP handles email sending (custom if configured, default otherwise) |
17-
| Disabled | Enabled | Email Signups Disabled |
18-
| Disabled | Disabled | Email Signups Disabled |
19-
20-
## Email change behavior and token hash mapping
21-
22-
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.
23-
24-
- 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.
25-
- Secure Email Change disabled: only one OTP is generated for the new email. You send a single email.
26-
27-
<Admonition type="note">
28-
29-
Important quirk (backward compatibility):
30-
31-
- `email_data.token_hash_new` = Hash(`user.email`, `email_data.token`)
32-
- `email_data.token_hash` = Hash(`user.email_new`, `email_data.token_new`)
33-
34-
This naming is historical and kept for backward compatibility. Do not assume that the `_new` suffix refers to the new email.
35-
36-
</Admonition>
37-
38-
### What to send
39-
40-
If both `token_hash` and `token_hash_new` are present, send two messages:
41-
42-
- To the current email (`user.email`): use `token` with `token_hash_new`.
43-
- To the new email (`user.email_new`): use `token_new` with `token_hash`.
44-
45-
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.
7+
The Send Email Hook runs before an email is sent and allows for flexibility around email sending. You can use this hook to configure a backup email provider or to add internationalization to your emails.
468

479
**Inputs**
4810

49-
| Field | Type | Description |
50-
| ------- | ------------------------------------------------- | ---------------------------------------------------------------------------------- |
51-
| `user` | [`User`](/docs/guides/auth/users#the-user-object) | The user attempting to sign in. |
52-
| `email` | `object` | Metadata specific to the email sending process. Includes the OTP and `token_hash`. |
11+
| Field | Type | Description |
12+
| ------- | ------------------------------------------------- | ---------------------------------------------- |
13+
| `user` | [`User`](/docs/guides/auth/users#the-user-object) | The user account taking the action |
14+
| `email` | `object` | Metadata specific to the email sending process |
5315

5416
<Tabs
5517
scrollable
@@ -105,7 +67,11 @@ If only one token/hash pair is present, send a single email. In non-secure mode,
10567
"email_action_type": "signup",
10668
"site_url": "http://localhost:9999",
10769
"token_new": "",
108-
"token_hash_new": ""
70+
"token_hash_new": "",
71+
"old_email": "",
72+
"old_phone": "",
73+
"provider": "",
74+
"factor_type": ""
10975
}
11076
}
11177
```
@@ -316,7 +282,14 @@ If only one token/hash pair is present, send a single email. In non-secure mode,
316282
"recovery",
317283
"email_change",
318284
"email",
319-
"reauthentication"
285+
"reauthentication",
286+
"password_changed_notification",
287+
"email_changed_notification",
288+
"phone_changed_notification",
289+
"identity_linked_notification",
290+
"identity_unlinked_notification",
291+
"mfa_factor_enrolled_notification",
292+
"mfa_factor_unenrolled_notification"
320293
]
321294
},
322295
"site_url": {
@@ -338,6 +311,24 @@ If only one token/hash pair is present, send a single email. In non-secure mode,
338311
"x-faker": {
339312
"fake": "{{random.alphaNumeric(30)}}"
340313
}
314+
},
315+
"old_email": {
316+
"type": "string",
317+
"x-faker": "internet.email"
318+
},
319+
"old_phone": {
320+
"type": "string",
321+
"x-faker": {
322+
"fake": "{{phone.phoneNumber('+1##########')}}"
323+
}
324+
},
325+
"provider": {
326+
"type": "string",
327+
"enum": ["email"]
328+
},
329+
"factor_type": {
330+
"type": "string",
331+
"enum": ["totp"]
341332
}
342333
},
343334
"required": [
@@ -362,6 +353,46 @@ If only one token/hash pair is present, send a single email. In non-secure mode,
362353

363354
- No outputs are required. An empty response with a status code of 200 is taken as a successful response.
364355

356+
## Email sending behavior
357+
358+
Email sending depends on two settings: Email Provider and Auth Hook status.
359+
360+
| Email Provider | Auth Hook | Result |
361+
| -------------- | --------- | -------------------------------------------------------------------- |
362+
| Enabled | Enabled | Auth Hook handles email sending (SMTP not used) |
363+
| Enabled | Disabled | SMTP handles email sending (custom if configured, default otherwise) |
364+
| Disabled | Enabled | Email signups disabled |
365+
| Disabled | Disabled | Email signups disabled |
366+
367+
## Email change behavior and token hash mapping
368+
369+
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.
370+
371+
- 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.
372+
- Secure Email Change disabled: only one OTP is generated for the new email. You send a single email.
373+
374+
<Admonition type="caution" title="Counterintuitive field naming">
375+
376+
The token hash field names are reversed due to backward compatibility. Pay careful attention to which token/hash pair goes with which email address:
377+
378+
- `token_hash_new` → use with the **current** email address (`user.email`) and `token`
379+
- `token_hash` → use with the **new** email address (`user.email_new`) and `token_new`
380+
381+
Do not assume the `_new` suffix refers to the new email address.
382+
383+
</Admonition>
384+
385+
### What to send
386+
387+
When Secure Email Change is enabled (both token/hash pairs present):
388+
389+
- Send to **current** email address (`user.email`): use `token` with `token_hash_new`
390+
- Send to **new** email address (`user.email_new`): use `token_new` with `token_hash`
391+
392+
When Secure Email Change is **disabled** (only one token/hash pair present):
393+
394+
- Send a single email to the **new** email address. Use `token` with `token_hash` or `token_new` with `token_hash`, depending on which fields are present in the payload.
395+
365396
<Tabs
366397
scrollable
367398
size="small"

0 commit comments

Comments
 (0)