Skip to content

Commit 2317aba

Browse files
authored
Merge branch 'develop' into jpa
2 parents 66e3ccd + 4054f20 commit 2317aba

File tree

8 files changed

+633
-150
lines changed

8 files changed

+633
-150
lines changed

pkg/config/auth.go

Lines changed: 354 additions & 141 deletions
Large diffs are not rendered by default.

pkg/config/auth_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,57 @@ func TestEmailDiff(t *testing.T) {
552552
Content: cast.Ptr("reauthentication-content"),
553553
},
554554
},
555+
Notification: map[string]notification{
556+
"password_changed": {
557+
Enabled: true,
558+
emailTemplate: emailTemplate{
559+
Subject: cast.Ptr("password-changed-subject"),
560+
Content: cast.Ptr("password-changed-content"),
561+
},
562+
},
563+
"email_changed": {
564+
Enabled: true,
565+
emailTemplate: emailTemplate{
566+
Subject: cast.Ptr("email-changed-subject"),
567+
Content: cast.Ptr("email-changed-content"),
568+
},
569+
},
570+
"phone_changed": {
571+
Enabled: true,
572+
emailTemplate: emailTemplate{
573+
Subject: cast.Ptr("phone-changed-subject"),
574+
Content: cast.Ptr("phone-changed-content"),
575+
},
576+
},
577+
"identity_linked": {
578+
Enabled: true,
579+
emailTemplate: emailTemplate{
580+
Subject: cast.Ptr("identity-linked-subject"),
581+
Content: cast.Ptr("identity-linked-content"),
582+
},
583+
},
584+
"identity_unlinked": {
585+
Enabled: true,
586+
emailTemplate: emailTemplate{
587+
Subject: cast.Ptr("identity-unlinked-subject"),
588+
Content: cast.Ptr("identity-unlinked-content"),
589+
},
590+
},
591+
"mfa_factor_enrolled": {
592+
Enabled: true,
593+
emailTemplate: emailTemplate{
594+
Subject: cast.Ptr("mfa-enrolled-subject"),
595+
Content: cast.Ptr("mfa-enrolled-content"),
596+
},
597+
},
598+
"mfa_factor_unenrolled": {
599+
Enabled: true,
600+
emailTemplate: emailTemplate{
601+
Subject: cast.Ptr("mfa-unenrolled-subject"),
602+
Content: cast.Ptr("mfa-unenrolled-content"),
603+
},
604+
},
605+
},
555606
Smtp: &smtp{
556607
Enabled: true,
557608
Host: "smtp.sendgrid.net",
@@ -596,6 +647,28 @@ func TestEmailDiff(t *testing.T) {
596647
MailerTemplatesEmailChangeContent: nullable.NewNullableWithValue("email-change-content"),
597648
MailerSubjectsReauthentication: nullable.NewNullableWithValue("reauthentication-subject"),
598649
MailerTemplatesReauthenticationContent: nullable.NewNullableWithValue("reauthentication-content"),
650+
// Notifications
651+
MailerNotificationsPasswordChangedEnabled: nullable.NewNullableWithValue(true),
652+
MailerSubjectsPasswordChangedNotification: nullable.NewNullableWithValue("password-changed-subject"),
653+
MailerTemplatesPasswordChangedNotificationContent: nullable.NewNullableWithValue("password-changed-content"),
654+
MailerNotificationsEmailChangedEnabled: nullable.NewNullableWithValue(true),
655+
MailerSubjectsEmailChangedNotification: nullable.NewNullableWithValue("email-changed-subject"),
656+
MailerTemplatesEmailChangedNotificationContent: nullable.NewNullableWithValue("email-changed-content"),
657+
MailerNotificationsPhoneChangedEnabled: nullable.NewNullableWithValue(true),
658+
MailerSubjectsPhoneChangedNotification: nullable.NewNullableWithValue("phone-changed-subject"),
659+
MailerTemplatesPhoneChangedNotificationContent: nullable.NewNullableWithValue("phone-changed-content"),
660+
MailerNotificationsIdentityLinkedEnabled: nullable.NewNullableWithValue(true),
661+
MailerSubjectsIdentityLinkedNotification: nullable.NewNullableWithValue("identity-linked-subject"),
662+
MailerTemplatesIdentityLinkedNotificationContent: nullable.NewNullableWithValue("identity-linked-content"),
663+
MailerNotificationsIdentityUnlinkedEnabled: nullable.NewNullableWithValue(true),
664+
MailerSubjectsIdentityUnlinkedNotification: nullable.NewNullableWithValue("identity-unlinked-subject"),
665+
MailerTemplatesIdentityUnlinkedNotificationContent: nullable.NewNullableWithValue("identity-unlinked-content"),
666+
MailerNotificationsMfaFactorEnrolledEnabled: nullable.NewNullableWithValue(true),
667+
MailerSubjectsMfaFactorEnrolledNotification: nullable.NewNullableWithValue("mfa-enrolled-subject"),
668+
MailerTemplatesMfaFactorEnrolledNotificationContent: nullable.NewNullableWithValue("mfa-enrolled-content"),
669+
MailerNotificationsMfaFactorUnenrolledEnabled: nullable.NewNullableWithValue(true),
670+
MailerSubjectsMfaFactorUnenrolledNotification: nullable.NewNullableWithValue("mfa-unenrolled-subject"),
671+
MailerTemplatesMfaFactorUnenrolledNotificationContent: nullable.NewNullableWithValue("mfa-unenrolled-content"),
599672
})
600673
// Check error
601674
assert.NoError(t, err)
@@ -633,6 +706,45 @@ func TestEmailDiff(t *testing.T) {
633706
Content: cast.Ptr(""),
634707
},
635708
},
709+
Notification: map[string]notification{
710+
"password_changed": {
711+
Enabled: true,
712+
emailTemplate: emailTemplate{
713+
Subject: cast.Ptr("password-changed-subject"),
714+
Content: cast.Ptr("password-changed-content"),
715+
},
716+
},
717+
"email_changed": {
718+
Enabled: true,
719+
emailTemplate: emailTemplate{
720+
Subject: cast.Ptr("email-changed-subject"),
721+
Content: cast.Ptr("email-changed-content"),
722+
},
723+
},
724+
"phone_changed": {
725+
Enabled: true,
726+
emailTemplate: emailTemplate{
727+
Subject: cast.Ptr("phone-changed-subject"),
728+
Content: cast.Ptr("phone-changed-content"),
729+
},
730+
},
731+
"identity_linked": {
732+
Enabled: true,
733+
emailTemplate: emailTemplate{
734+
Subject: cast.Ptr("identity-linked-subject"),
735+
Content: cast.Ptr("identity-linked-content"),
736+
},
737+
},
738+
"identity_unlinked": {
739+
Enabled: true,
740+
},
741+
"mfa_factor_enrolled": {
742+
Enabled: true,
743+
},
744+
"mfa_factor_unenrolled": {
745+
Enabled: true,
746+
},
747+
},
636748
Smtp: &smtp{
637749
Enabled: true,
638750
Host: "smtp.sendgrid.net",
@@ -663,6 +775,21 @@ func TestEmailDiff(t *testing.T) {
663775
MailerSubjectsRecovery: nullable.NewNullableWithValue("recovery-subject"),
664776
MailerSubjectsMagicLink: nullable.NewNullableWithValue("magic-link-subject"),
665777
MailerTemplatesEmailChangeContent: nullable.NewNullableWithValue("email-change-content"),
778+
// Notifications
779+
MailerNotificationsPasswordChangedEnabled: nullable.NewNullableWithValue(false),
780+
MailerSubjectsPasswordChangedNotification: nullable.NewNullableWithValue("password-changed-subject"),
781+
MailerTemplatesPasswordChangedNotificationContent: nullable.NewNullableWithValue("password-changed-content"),
782+
MailerNotificationsEmailChangedEnabled: nullable.NewNullableWithValue(false),
783+
MailerSubjectsEmailChangedNotification: nullable.NewNullableWithValue("email-changed-subject"),
784+
MailerNotificationsPhoneChangedEnabled: nullable.NewNullableWithValue(false),
785+
MailerTemplatesPhoneChangedNotificationContent: nullable.NewNullableWithValue("phone-changed-content"),
786+
MailerNotificationsIdentityLinkedEnabled: nullable.NewNullableWithValue(false),
787+
MailerNotificationsIdentityUnlinkedEnabled: nullable.NewNullableWithValue(false),
788+
MailerSubjectsIdentityUnlinkedNotification: nullable.NewNullableWithValue("identity-unlinked-subject"),
789+
MailerTemplatesIdentityUnlinkedNotificationContent: nullable.NewNullableWithValue("identity-unlinked-content"),
790+
MailerNotificationsMfaFactorEnrolledEnabled: nullable.NewNullableWithValue(false),
791+
MailerSubjectsMfaFactorEnrolledNotification: nullable.NewNullableWithValue("mfa-enrolled-subject"),
792+
MailerNotificationsMfaFactorUnenrolledEnabled: nullable.NewNullableWithValue(false),
666793
})
667794
// Check error
668795
assert.NoError(t, err)
@@ -681,6 +808,15 @@ func TestEmailDiff(t *testing.T) {
681808
"email_change": {},
682809
"reauthentication": {},
683810
},
811+
Notification: map[string]notification{
812+
"password_changed": {},
813+
"email_changed": {},
814+
"phone_changed": {},
815+
"identity_linked": {},
816+
"identity_unlinked": {},
817+
"mfa_factor_enrolled": {},
818+
"mfa_factor_unenrolled": {},
819+
},
684820
MaxFrequency: time.Minute,
685821
OtpLength: 8,
686822
OtpExpiry: 86400,
@@ -713,6 +849,24 @@ func TestEmailDiff(t *testing.T) {
713849
MailerTemplatesEmailChangeContent: nullable.NewNullableWithValue("email-change-content"),
714850
MailerSubjectsReauthentication: nullable.NewNullableWithValue("reauthentication-subject"),
715851
MailerTemplatesReauthenticationContent: nullable.NewNullableWithValue("reauthentication-content"),
852+
// Notifications
853+
MailerNotificationsPasswordChangedEnabled: nullable.NewNullableWithValue(true),
854+
MailerSubjectsPasswordChangedNotification: nullable.NewNullableWithValue("password-changed-subject"),
855+
MailerTemplatesPasswordChangedNotificationContent: nullable.NewNullableWithValue("password-changed-content"),
856+
MailerNotificationsEmailChangedEnabled: nullable.NewNullableWithValue(true),
857+
MailerSubjectsEmailChangedNotification: nullable.NewNullableWithValue("email-changed-subject"),
858+
MailerNotificationsPhoneChangedEnabled: nullable.NewNullableWithValue(true),
859+
MailerTemplatesPhoneChangedNotificationContent: nullable.NewNullableWithValue("phone-changed-content"),
860+
MailerNotificationsIdentityLinkedEnabled: nullable.NewNullableWithValue(true),
861+
MailerNotificationsIdentityUnlinkedEnabled: nullable.NewNullableWithValue(true),
862+
MailerSubjectsIdentityUnlinkedNotification: nullable.NewNullableWithValue("identity-unlinked-subject"),
863+
MailerTemplatesIdentityUnlinkedNotificationContent: nullable.NewNullableWithValue("identity-unlinked-content"),
864+
MailerNotificationsMfaFactorEnrolledEnabled: nullable.NewNullableWithValue(true),
865+
MailerSubjectsMfaFactorEnrolledNotification: nullable.NewNullableWithValue("mfa-enrolled-subject"),
866+
MailerTemplatesMfaFactorEnrolledNotificationContent: nullable.NewNullableWithValue("mfa-enrolled-content"),
867+
MailerNotificationsMfaFactorUnenrolledEnabled: nullable.NewNullableWithValue(true),
868+
MailerSubjectsMfaFactorUnenrolledNotification: nullable.NewNullableWithValue("mfa-unenrolled-subject"),
869+
MailerTemplatesMfaFactorUnenrolledNotificationContent: nullable.NewNullableWithValue("mfa-unenrolled-content"),
716870
})
717871
// Check error
718872
assert.NoError(t, err)
@@ -731,6 +885,15 @@ func TestEmailDiff(t *testing.T) {
731885
"email_change": {},
732886
"reauthentication": {},
733887
},
888+
Notification: map[string]notification{
889+
"password_changed": {},
890+
"email_changed": {},
891+
"phone_changed": {},
892+
"identity_linked": {},
893+
"identity_unlinked": {},
894+
"mfa_factor_enrolled": {},
895+
"mfa_factor_unenrolled": {},
896+
},
734897
Smtp: &smtp{
735898
Enabled: false,
736899
Host: "smtp.sendgrid.net",

pkg/config/config.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ func (a *auth) Clone() auth {
259259
copy.Email.Smtp = &mailer
260260
}
261261
copy.Email.Template = maps.Clone(a.Email.Template)
262+
copy.Email.Notification = maps.Clone(a.Email.Notification)
262263
if a.Hook.MFAVerificationAttempt != nil {
263264
hook := *a.Hook.MFAVerificationAttempt
264265
copy.Hook.MFAVerificationAttempt = &hook
@@ -372,7 +373,8 @@ func NewConfig(editors ...ConfigEditor) config {
372373
Auth: auth{
373374
Image: Images.Gotrue,
374375
Email: email{
375-
Template: map[string]emailTemplate{},
376+
Template: map[string]emailTemplate{},
377+
Notification: map[string]notification{},
376378
},
377379
Sms: sms{
378380
TestOTP: map[string]string{},
@@ -673,6 +675,12 @@ func (c *baseConfig) resolve(builder pathBuilder, fsys fs.FS) error {
673675
}
674676
c.Auth.Email.Template[name] = tmpl
675677
}
678+
for name, tmpl := range c.Auth.Email.Notification {
679+
if len(tmpl.ContentPath) > 0 && !filepath.IsAbs(tmpl.ContentPath) {
680+
tmpl.ContentPath = filepath.Join(builder.SupabaseDirPath, tmpl.ContentPath)
681+
}
682+
c.Auth.Email.Notification[name] = tmpl
683+
}
676684
// Update fallback configs
677685
for name, bucket := range c.Storage.Buckets {
678686
if bucket.FileSizeLimit == 0 {
@@ -1026,17 +1034,34 @@ func (e *email) validate(fsys fs.FS) (err error) {
10261034
for name, tmpl := range e.Template {
10271035
if len(tmpl.ContentPath) == 0 {
10281036
if tmpl.Content != nil {
1029-
return errors.Errorf("Invalid config for auth.email.%s.content: please use content_path instead", name)
1037+
return errors.Errorf("Invalid config for auth.email.template.%s.content: please use content_path instead", name)
10301038
}
10311039
continue
10321040
}
10331041
if content, err := fs.ReadFile(fsys, tmpl.ContentPath); err != nil {
1034-
return errors.Errorf("Invalid config for auth.email.%s.content_path: %w", name, err)
1042+
return errors.Errorf("Invalid config for auth.email.template.%s.content_path: %w", name, err)
10351043
} else {
10361044
tmpl.Content = cast.Ptr(string(content))
10371045
}
10381046
e.Template[name] = tmpl
10391047
}
1048+
for name, tmpl := range e.Notification {
1049+
if !tmpl.Enabled {
1050+
continue
1051+
}
1052+
if len(tmpl.ContentPath) == 0 {
1053+
if tmpl.Content != nil {
1054+
return errors.Errorf("Invalid config for auth.email.notification.%s.content: please use content_path instead", name)
1055+
}
1056+
continue
1057+
}
1058+
if content, err := fs.ReadFile(fsys, tmpl.ContentPath); err != nil {
1059+
return errors.Errorf("Invalid config for auth.email.notification.%s.content_path: %w", name, err)
1060+
} else {
1061+
tmpl.Content = cast.Ptr(string(content))
1062+
}
1063+
e.Notification[name] = tmpl
1064+
}
10401065
if e.Smtp != nil && e.Smtp.Enabled {
10411066
if len(e.Smtp.Host) == 0 {
10421067
return errors.New("Missing required field in config: auth.email.smtp.host")

pkg/config/templates/Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ FROM postgrest/postgrest:v13.0.7 AS postgrest
77
FROM supabase/postgres-meta:v0.93.1 AS pgmeta
88
FROM supabase/studio:2025.11.03-sha-ddedae5 AS studio
99
FROM darthsim/imgproxy:v3.8.0 AS imgproxy
10-
FROM supabase/edge-runtime:v1.69.20 AS edgeruntime
10+
FROM supabase/edge-runtime:v1.69.21 AS edgeruntime
1111
FROM timberio/vector:0.28.1-alpine AS vector
1212
FROM supabase/supavisor:2.7.4 AS supavisor
13-
FROM supabase/gotrue:v2.180.0 AS gotrue
14-
FROM supabase/realtime:v2.60.0 AS realtime
13+
FROM supabase/gotrue:v2.181.0 AS gotrue
14+
FROM supabase/realtime:v2.61.0 AS realtime
1515
FROM supabase/storage-api:v1.28.5 AS storage
16-
FROM supabase/logflare:1.23.3 AS logflare
16+
FROM supabase/logflare:1.22.6 AS logflare
1717
# Append to JobImages when adding new dependencies below
1818
FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ
1919
FROM supabase/migra:3.0.1663481299 AS migra

pkg/config/templates/config.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,12 @@ otp_expiry = 3600
200200
# subject = "You have been invited"
201201
# content_path = "./supabase/templates/invite.html"
202202

203+
# Uncomment to customize notification email template
204+
# [auth.email.notification.password_changed]
205+
# enabled = true
206+
# subject = "Your password has been changed"
207+
# content_path = "./templates/password_changed_notification.html"
208+
203209
[auth.sms]
204210
# Allow/disallow new user signups via SMS to your project.
205211
enable_signup = false

pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
diff remote[auth] local[auth]
22
--- remote[auth]
33
+++ local[auth]
4-
@@ -44,13 +44,13 @@
4+
@@ -47,13 +47,13 @@
55
inactivity_timeout = "0s"
66

77
[email]
@@ -22,3 +22,36 @@ diff remote[auth] local[auth]
2222
[email.template]
2323
[email.template.confirmation]
2424
content_path = ""
25+
@@ -69,25 +69,25 @@
26+
content_path = ""
27+
[email.notification]
28+
[email.notification.email_changed]
29+
-enabled = true
30+
+enabled = false
31+
content_path = ""
32+
[email.notification.identity_linked]
33+
-enabled = true
34+
+enabled = false
35+
content_path = ""
36+
[email.notification.identity_unlinked]
37+
-enabled = true
38+
+enabled = false
39+
content_path = ""
40+
[email.notification.mfa_factor_enrolled]
41+
-enabled = true
42+
+enabled = false
43+
content_path = ""
44+
[email.notification.mfa_factor_unenrolled]
45+
-enabled = true
46+
+enabled = false
47+
content_path = ""
48+
[email.notification.password_changed]
49+
-enabled = true
50+
+enabled = false
51+
content_path = ""
52+
[email.notification.phone_changed]
53+
-enabled = true
54+
+enabled = false
55+
content_path = ""
56+
57+
[sms]

0 commit comments

Comments
 (0)