@@ -14,21 +14,26 @@ import (
1414 "github.com/supabase/auth/internal/api/apierrors"
1515 "github.com/supabase/auth/internal/api/provider"
1616 "github.com/supabase/auth/internal/conf"
17+ mail "github.com/supabase/auth/internal/mailer"
18+ "github.com/supabase/auth/internal/mailer/mockclient"
1719 "github.com/supabase/auth/internal/models"
1820)
1921
2022type IdentityTestSuite struct {
2123 suite.Suite
2224 API * API
2325 Config * conf.GlobalConfiguration
26+ Mailer mail.Mailer
2427}
2528
2629func TestIdentity (t * testing.T ) {
27- api , config , err := setupAPIForTest ()
30+ mockMailer := & mockclient.MockMailer {}
31+ api , config , err := setupAPIForTest (WithMailer (mockMailer ))
2832 require .NoError (t , err )
2933 ts := & IdentityTestSuite {
3034 API : api ,
3135 Config : config ,
36+ Mailer : mockMailer ,
3237 }
3338 defer api .db .Close ()
3439 suite .Run (t , ts )
@@ -226,3 +231,115 @@ func (ts *IdentityTestSuite) generateAccessTokenAndSession(u *models.User) strin
226231 return token
227232
228233}
234+
235+ func (ts * IdentityTestSuite ) TestLinkIdentitySendsNotificationEmailEnabled () {
236+ ts .Config .Mailer .Notifications .IdentityLinkedEnabled = true
237+
238+ u , err := models .FindUserByEmailAndAudience (ts .API .db , "one@example.com" , ts .Config .JWT .Aud )
239+ require .NoError (ts .T (), err )
240+ ctx := withTargetUser (context .Background (), u )
241+
242+ // Get the mock mailer and reset it
243+ mockMailer , ok := ts .Mailer .(* mockclient.MockMailer )
244+ require .True (ts .T (), ok , "Mailer is not of type *MockMailer" )
245+ mockMailer .Reset ()
246+
247+ // Link a new identity
248+ testValidUserData := & provider.UserProvidedData {
249+ Metadata : & provider.Claims {
250+ Subject : "test_subject" ,
251+ },
252+ }
253+ r := httptest .NewRequest (http .MethodGet , "/identities" , nil )
254+ u , err = ts .API .linkIdentityToUser (r , ctx , ts .API .db , testValidUserData , "google" )
255+ require .NoError (ts .T (), err )
256+
257+ // Assert that identity linked notification email was sent
258+ require .Len (ts .T (), mockMailer .IdentityLinkedMailCalls , 1 , "Expected 1 identity linked notification email(s) to be sent" )
259+ require .Equal (ts .T (), u .ID , mockMailer .IdentityLinkedMailCalls [0 ].User .ID , "Email should be sent to the correct user" )
260+ require .Equal (ts .T (), "google" , mockMailer .IdentityLinkedMailCalls [0 ].Provider , "Provider should match" )
261+ require .Equal (ts .T (), "one@example.com" , mockMailer .IdentityLinkedMailCalls [0 ].User .GetEmail (), "Email should be sent to the correct email address" )
262+ }
263+
264+ func (ts * IdentityTestSuite ) TestLinkIdentitySendsNotificationEmailDisabled () {
265+ ts .Config .Mailer .Notifications .IdentityLinkedEnabled = false
266+
267+ u , err := models .FindUserByEmailAndAudience (ts .API .db , "one@example.com" , ts .Config .JWT .Aud )
268+ require .NoError (ts .T (), err )
269+ ctx := withTargetUser (context .Background (), u )
270+
271+ // Get the mock mailer and reset it
272+ mockMailer , ok := ts .Mailer .(* mockclient.MockMailer )
273+ require .True (ts .T (), ok , "Mailer is not of type *MockMailer" )
274+ mockMailer .Reset ()
275+
276+ // Link a new identity
277+ testValidUserData := & provider.UserProvidedData {
278+ Metadata : & provider.Claims {
279+ Subject : "test_subject_disabled" ,
280+ },
281+ }
282+ r := httptest .NewRequest (http .MethodGet , "/identities" , nil )
283+ _ , err = ts .API .linkIdentityToUser (r , ctx , ts .API .db , testValidUserData , "facebook" )
284+ require .NoError (ts .T (), err )
285+
286+ // Assert that identity linked notification email was not sent
287+ require .Len (ts .T (), mockMailer .IdentityLinkedMailCalls , 0 , "Expected 0 identity linked notification email(s) to be sent" )
288+ }
289+
290+ func (ts * IdentityTestSuite ) TestUnlinkIdentitySendsNotificationEmailEnabled () {
291+ ts .Config .Mailer .Notifications .IdentityUnlinkedEnabled = true
292+ ts .Config .Security .ManualLinkingEnabled = true
293+
294+ u , err := models .FindUserByEmailAndAudience (ts .API .db , "two@example.com" , ts .Config .JWT .Aud )
295+ require .NoError (ts .T (), err )
296+
297+ identity , err := models .FindIdentityByIdAndProvider (ts .API .db , u .ID .String (), "phone" )
298+ require .NoError (ts .T (), err )
299+
300+ // Get the mock mailer and reset it
301+ mockMailer , ok := ts .Mailer .(* mockclient.MockMailer )
302+ require .True (ts .T (), ok , "Mailer is not of type *MockMailer" )
303+ mockMailer .Reset ()
304+
305+ token := ts .generateAccessTokenAndSession (u )
306+ req , err := http .NewRequest (http .MethodDelete , fmt .Sprintf ("/user/identities/%s" , identity .ID ), nil )
307+ require .NoError (ts .T (), err )
308+ req .Header .Set ("Authorization" , fmt .Sprintf ("Bearer %s" , token ))
309+ w := httptest .NewRecorder ()
310+ ts .API .handler .ServeHTTP (w , req )
311+ require .Equal (ts .T (), http .StatusOK , w .Code )
312+
313+ // Assert that identity unlinked notification email was sent
314+ require .Len (ts .T (), mockMailer .IdentityUnlinkedMailCalls , 1 , "Expected 1 identity unlinked notification email(s) to be sent" )
315+ require .Equal (ts .T (), u .ID , mockMailer .IdentityUnlinkedMailCalls [0 ].User .ID , "Email should be sent to the correct user" )
316+ require .Equal (ts .T (), "phone" , mockMailer .IdentityUnlinkedMailCalls [0 ].Provider , "Provider should match" )
317+ require .Equal (ts .T (), "two@example.com" , mockMailer .IdentityUnlinkedMailCalls [0 ].User .GetEmail (), "Email should be sent to the correct email address" )
318+ }
319+
320+ func (ts * IdentityTestSuite ) TestUnlinkIdentitySendsNotificationEmailDisabled () {
321+ ts .Config .Mailer .Notifications .IdentityUnlinkedEnabled = false
322+ ts .Config .Security .ManualLinkingEnabled = true
323+
324+ u , err := models .FindUserByEmailAndAudience (ts .API .db , "two@example.com" , ts .Config .JWT .Aud )
325+ require .NoError (ts .T (), err )
326+
327+ identity , err := models .FindIdentityByIdAndProvider (ts .API .db , u .ID .String (), "phone" )
328+ require .NoError (ts .T (), err )
329+
330+ // Get the mock mailer and reset it
331+ mockMailer , ok := ts .Mailer .(* mockclient.MockMailer )
332+ require .True (ts .T (), ok , "Mailer is not of type *MockMailer" )
333+ mockMailer .Reset ()
334+
335+ token := ts .generateAccessTokenAndSession (u )
336+ req , err := http .NewRequest (http .MethodDelete , fmt .Sprintf ("/user/identities/%s" , identity .ID ), nil )
337+ require .NoError (ts .T (), err )
338+ req .Header .Set ("Authorization" , fmt .Sprintf ("Bearer %s" , token ))
339+ w := httptest .NewRecorder ()
340+ ts .API .handler .ServeHTTP (w , req )
341+ require .Equal (ts .T (), http .StatusOK , w .Code )
342+
343+ // Assert that identity unlinked notification email was not sent
344+ require .Len (ts .T (), mockMailer .IdentityUnlinkedMailCalls , 0 , "Expected 0 identity unlinked notification email(s) to be sent" )
345+ }
0 commit comments