@@ -124,23 +124,31 @@ func runVerifyAfterUserCreatedHook(
124124 return latest
125125}
126126
127- func getAccessToken (
127+ func getEmailAccessToken (
128128 ctx context.Context ,
129129 t * testing.T ,
130130 inst * e2ehooks.Instance ,
131131 email , pass string ,
132132) * api.AccessTokenResponse {
133- req := & api.PasswordGrantParams {
133+ return getAccessToken ( ctx , t , inst , & api.PasswordGrantParams {
134134 Email : email ,
135135 Password : pass ,
136- }
136+ })
137+ }
137138
139+ func getAccessToken (
140+ ctx context.Context ,
141+ t * testing.T ,
142+ inst * e2ehooks.Instance ,
143+ req * api.PasswordGrantParams ,
144+ ) * api.AccessTokenResponse {
138145 res := new (api.AccessTokenResponse )
139146 err := e2eapi .Do (ctx , http .MethodPost , inst .APIServer .URL + "/token?grant_type=password" , req , res )
140147 require .NoError (t , err )
141148 return res
142149}
143- func signupAndConfirm (
150+
151+ func signupAndConfirmEmail (
144152 ctx context.Context ,
145153 t * testing.T ,
146154 inst * e2ehooks.Instance ,
@@ -249,21 +257,180 @@ func TestE2EHooks(t *testing.T) {
249257 })
250258
251259 t .Run ("SignupPhone" , func (t * testing.T ) {
260+ defer inst .HookRecorder .AfterUserCreated .ClearCalls ()
252261 defer inst .HookRecorder .BeforeUserCreated .ClearCalls ()
262+ defer inst .HookRecorder .SendSMS .ClearCalls ()
253263
254- phone := genPhone ()
255- req := & api.SignupParams {
256- Phone : phone ,
257- Password : defaultPassword ,
264+ var currentUser * models.User
265+ {
266+ phone := genPhone ()
267+ req := & api.SignupParams {
268+ Phone : phone ,
269+ Password : defaultPassword ,
270+ }
271+ signupUser := new (models.User )
272+ {
273+ err := e2eapi .Do (
274+ ctx , http .MethodPost , inst .APIServer .URL + "/signup" , req , signupUser )
275+ require .NoError (t , err )
276+ require .Equal (t , phone , signupUser .Phone .String ())
277+ }
278+
279+ // load the hook call
280+ calls := inst .HookRecorder .SendSMS .GetCalls ()
281+ require .Equal (t , 1 , len (calls ))
282+ call := calls [0 ]
283+
284+ hookReq := & v0hooks.SendSMSInput {}
285+ err = call .Unmarshal (hookReq )
286+ require .NoError (t , err )
287+
288+ latestUser , err := models .FindUserByID (inst .Conn , signupUser .ID )
289+ require .NoError (t , err )
290+ require .NotNil (t , latestUser )
291+
292+ otp := hookReq .SMS .OTP
293+ otpHash := crypto .GenerateTokenHash (
294+ signupUser .GetPhone (), hookReq .SMS .OTP )
295+
296+ ott , err := models .FindOneTimeToken (
297+ inst .Conn ,
298+ otpHash ,
299+ models .ConfirmationToken )
300+ require .NoError (t , err )
301+ require .Equal (t , signupUser .ID .String (), ott .UserID .String ())
302+ require .Equal (t , signupUser .Phone .String (), ott .RelatesTo )
303+
304+ {
305+ req := & api.VerifyParams {
306+ Type : "sms" ,
307+ Token : otp ,
308+ Phone : phone ,
309+ }
310+ res := new (models.User )
311+
312+ body := new (bytes.Buffer )
313+ err = json .NewEncoder (body ).Encode (req )
314+ require .NoError (t , err )
315+
316+ httpReq , err := http .NewRequestWithContext (
317+ ctx , "POST" , "/verify" , body )
318+ require .NoError (t , err )
319+
320+ httpRes , err := inst .Do (httpReq )
321+ require .NoError (t , err )
322+ require .Equal (t , 200 , httpRes .StatusCode )
323+
324+ err = json .NewDecoder (httpRes .Body ).Decode (res )
325+ require .NoError (t , err )
326+ }
327+
328+ {
329+ // setup phone change
330+ latestUser , err = models .FindUserByID (inst .Conn , signupUser .ID )
331+ require .NoError (t , err )
332+ require .NotNil (t , latestUser )
333+ currentUser = latestUser
334+ }
258335 }
259- res := new (models.User )
260- err := e2eapi .Do (ctx , http .MethodPost , inst .APIServer .URL + "/signup" , req , res )
261- require .NoError (t , err )
262- require .Equal (t , phone , res .Phone .String ())
263336
264- runVerifyBeforeUserCreatedHook (t , inst , res )
265- runVerifyAfterUserCreatedHook (t , inst , res )
337+ t .Run ("PhoneChange" , func (t * testing.T ) {
338+ inst .HookRecorder .BeforeUserCreated .ClearCalls ()
339+ inst .HookRecorder .SendSMS .ClearCalls ()
266340
341+ currentAccessToken := getAccessToken (ctx , t , inst ,
342+ & api.PasswordGrantParams {
343+ Phone : string (currentUser .Phone ),
344+ Password : defaultPassword ,
345+ })
346+
347+ curPhone := currentUser .Phone .String ()
348+ newPhone := genPhone ()
349+ {
350+ req := & api.UserUpdateParams {
351+ Phone : newPhone ,
352+ }
353+ res := new (models.User )
354+
355+ body := new (bytes.Buffer )
356+ err = json .NewEncoder (body ).Encode (req )
357+ require .NoError (t , err )
358+
359+ httpReq , err := http .NewRequestWithContext (
360+ ctx , "PUT" , "/user" , body )
361+ require .NoError (t , err )
362+
363+ httpRes , err := inst .DoAuth (httpReq , currentAccessToken .Token )
364+ require .NoError (t , err )
365+ require .Equal (t , 200 , httpRes .StatusCode )
366+
367+ err = json .NewDecoder (httpRes .Body ).Decode (res )
368+ require .NoError (t , err )
369+
370+ currentUser = res
371+ }
372+
373+ var otp string
374+ {
375+ require .Equal (t , curPhone , currentUser .Phone .String ())
376+ require .Equal (t , newPhone , currentUser .PhoneChange )
377+
378+ calls := inst .HookRecorder .SendSMS .GetCalls ()
379+ require .Equal (t , 1 , len (calls ))
380+ call := calls [0 ]
381+
382+ hookReq := & v0hooks.SendSMSInput {}
383+ err = call .Unmarshal (hookReq )
384+ require .NoError (t , err )
385+
386+ require .Equal (t , currentUser .ID , hookReq .User .ID )
387+ require .Equal (t , currentUser .Aud , hookReq .User .Aud )
388+ require .Equal (t , currentUser .Phone , hookReq .User .Phone )
389+ require .Equal (t , currentUser .AppMetaData , hookReq .User .AppMetaData )
390+
391+ otp = hookReq .SMS .OTP
392+ otpHash := crypto .GenerateTokenHash (
393+ currentUser .PhoneChange , hookReq .SMS .OTP )
394+
395+ ott , err := models .FindOneTimeToken (
396+ inst .Conn ,
397+ otpHash ,
398+ models .PhoneChangeToken )
399+ require .NoError (t , err )
400+ require .Equal (t , currentUser .ID .String (), ott .UserID .String ())
401+ require .Equal (t , currentUser .PhoneChange , ott .RelatesTo )
402+
403+ latestUser , err := models .FindUserByID (inst .Conn , currentUser .ID )
404+ require .NoError (t , err )
405+ require .NotNil (t , latestUser )
406+
407+ currentUser = latestUser
408+ }
409+
410+ {
411+ req := & api.VerifyParams {
412+ Type : "phone_change" ,
413+ Token : otp ,
414+ Phone : currentUser .PhoneChange ,
415+ }
416+ res := new (models.User )
417+
418+ body := new (bytes.Buffer )
419+ err = json .NewEncoder (body ).Encode (req )
420+ require .NoError (t , err )
421+
422+ httpReq , err := http .NewRequestWithContext (
423+ ctx , "POST" , "/verify" , body )
424+ require .NoError (t , err )
425+
426+ httpRes , err := inst .Do (httpReq )
427+ require .NoError (t , err )
428+ require .Equal (t , 200 , httpRes .StatusCode )
429+
430+ err = json .NewDecoder (httpRes .Body ).Decode (res )
431+ require .NoError (t , err )
432+ }
433+ })
267434 })
268435
269436 t .Run ("SignupAnonymously" , func (t * testing.T ) {
@@ -422,7 +589,7 @@ func TestE2EHooks(t *testing.T) {
422589 mfaUser = runVerifyBeforeUserCreatedHook (t , inst , mfaUser )
423590 runVerifyAfterUserCreatedHook (t , inst , mfaUser )
424591 require .NotNil (t , mfaUser )
425- mfaUserAccessToken = getAccessToken (
592+ mfaUserAccessToken = getEmailAccessToken (
426593 ctx , t , inst , string (mfaUser .Email ), defaultPassword )
427594
428595 phone := genPhone ()
@@ -813,7 +980,7 @@ func TestE2EHooks(t *testing.T) {
813980 require .NoError (t , err )
814981 defer inst .Close ()
815982
816- signupAndConfirm (ctx , t , inst )
983+ signupAndConfirmEmail (ctx , t , inst )
817984 })
818985
819986 t .Run ("SecureEmailChange=Enabled" , func (t * testing.T ) {
@@ -829,11 +996,11 @@ func TestE2EHooks(t *testing.T) {
829996 // test requires this flag
830997 require .True (t , inst .Config .Mailer .SecureEmailChangeEnabled )
831998
832- signupUser := signupAndConfirm (ctx , t , inst )
999+ signupUser := signupAndConfirmEmail (ctx , t , inst )
8331000 currentUser := signupUser
8341001
8351002 // get access token
836- currentAccessToken := getAccessToken (
1003+ currentAccessToken := getEmailAccessToken (
8371004 ctx , t , inst , string (currentUser .Email ), defaultPassword )
8381005
8391006 // update email
@@ -1045,11 +1212,11 @@ func TestE2EHooks(t *testing.T) {
10451212 // test requires this flag
10461213 require .False (t , inst .Config .Mailer .SecureEmailChangeEnabled )
10471214
1048- signupUser := signupAndConfirm (ctx , t , inst )
1215+ signupUser := signupAndConfirmEmail (ctx , t , inst )
10491216 currentUser := signupUser
10501217
10511218 // get access token
1052- currentAccessToken := getAccessToken (
1219+ currentAccessToken := getEmailAccessToken (
10531220 ctx , t , inst , string (currentUser .Email ), defaultPassword )
10541221
10551222 // update email
0 commit comments