Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions internal/api/anonymous_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/supabase/auth/internal/conf"
mail "github.com/supabase/auth/internal/mailer"
"github.com/supabase/auth/internal/models"
"github.com/supabase/auth/internal/storage"
)

type AnonymousTestSuite struct {
Expand All @@ -25,9 +26,14 @@ type AnonymousTestSuite struct {
}

func TestAnonymous(t *testing.T) {
api, config, err := setupAPIForTest()
require.NoError(t, err)
cb := func(cfg *conf.GlobalConfiguration, _ *storage.Connection) {
if cfg != nil {
cfg.RateLimitAnonymousUsers = 5
}
}

api, config, err := setupAPIForTestWithCallback(cb)
require.NoError(t, err)
ts := &AnonymousTestSuite{
API: api,
Config: config,
Expand Down
207 changes: 187 additions & 20 deletions internal/api/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,23 +124,31 @@ func runVerifyAfterUserCreatedHook(
return latest
}

func getAccessToken(
func getEmailAccessToken(
ctx context.Context,
t *testing.T,
inst *e2ehooks.Instance,
email, pass string,
) *api.AccessTokenResponse {
req := &api.PasswordGrantParams{
return getAccessToken(ctx, t, inst, &api.PasswordGrantParams{
Email: email,
Password: pass,
}
})
}

func getAccessToken(
ctx context.Context,
t *testing.T,
inst *e2ehooks.Instance,
req *api.PasswordGrantParams,
) *api.AccessTokenResponse {
res := new(api.AccessTokenResponse)
err := e2eapi.Do(ctx, http.MethodPost, inst.APIServer.URL+"/token?grant_type=password", req, res)
require.NoError(t, err)
return res
}
func signupAndConfirm(

func signupAndConfirmEmail(
ctx context.Context,
t *testing.T,
inst *e2ehooks.Instance,
Expand Down Expand Up @@ -249,21 +257,180 @@ func TestE2EHooks(t *testing.T) {
})

t.Run("SignupPhone", func(t *testing.T) {
defer inst.HookRecorder.AfterUserCreated.ClearCalls()
defer inst.HookRecorder.BeforeUserCreated.ClearCalls()
defer inst.HookRecorder.SendSMS.ClearCalls()

phone := genPhone()
req := &api.SignupParams{
Phone: phone,
Password: defaultPassword,
var currentUser *models.User
{
phone := genPhone()
req := &api.SignupParams{
Phone: phone,
Password: defaultPassword,
}
signupUser := new(models.User)
{
err := e2eapi.Do(
ctx, http.MethodPost, inst.APIServer.URL+"/signup", req, signupUser)
require.NoError(t, err)
require.Equal(t, phone, signupUser.Phone.String())
}

// load the hook call
calls := inst.HookRecorder.SendSMS.GetCalls()
require.Equal(t, 1, len(calls))
call := calls[0]

hookReq := &v0hooks.SendSMSInput{}
err = call.Unmarshal(hookReq)
require.NoError(t, err)

latestUser, err := models.FindUserByID(inst.Conn, signupUser.ID)
require.NoError(t, err)
require.NotNil(t, latestUser)

otp := hookReq.SMS.OTP
otpHash := crypto.GenerateTokenHash(
signupUser.GetPhone(), hookReq.SMS.OTP)

ott, err := models.FindOneTimeToken(
inst.Conn,
otpHash,
models.ConfirmationToken)
require.NoError(t, err)
require.Equal(t, signupUser.ID.String(), ott.UserID.String())
require.Equal(t, signupUser.Phone.String(), ott.RelatesTo)

{
req := &api.VerifyParams{
Type: "sms",
Token: otp,
Phone: phone,
}
res := new(models.User)

body := new(bytes.Buffer)
err = json.NewEncoder(body).Encode(req)
require.NoError(t, err)

httpReq, err := http.NewRequestWithContext(
ctx, "POST", "/verify", body)
require.NoError(t, err)

httpRes, err := inst.Do(httpReq)
require.NoError(t, err)
require.Equal(t, 200, httpRes.StatusCode)

err = json.NewDecoder(httpRes.Body).Decode(res)
require.NoError(t, err)
}

{
// setup phone change
latestUser, err = models.FindUserByID(inst.Conn, signupUser.ID)
require.NoError(t, err)
require.NotNil(t, latestUser)
currentUser = latestUser
}
}
res := new(models.User)
err := e2eapi.Do(ctx, http.MethodPost, inst.APIServer.URL+"/signup", req, res)
require.NoError(t, err)
require.Equal(t, phone, res.Phone.String())

runVerifyBeforeUserCreatedHook(t, inst, res)
runVerifyAfterUserCreatedHook(t, inst, res)
t.Run("PhoneChange", func(t *testing.T) {
inst.HookRecorder.BeforeUserCreated.ClearCalls()
inst.HookRecorder.SendSMS.ClearCalls()

currentAccessToken := getAccessToken(ctx, t, inst,
&api.PasswordGrantParams{
Phone: string(currentUser.Phone),
Password: defaultPassword,
})

curPhone := currentUser.Phone.String()
newPhone := genPhone()
{
req := &api.UserUpdateParams{
Phone: newPhone,
}
res := new(models.User)

body := new(bytes.Buffer)
err = json.NewEncoder(body).Encode(req)
require.NoError(t, err)

httpReq, err := http.NewRequestWithContext(
ctx, "PUT", "/user", body)
require.NoError(t, err)

httpRes, err := inst.DoAuth(httpReq, currentAccessToken.Token)
require.NoError(t, err)
require.Equal(t, 200, httpRes.StatusCode)

err = json.NewDecoder(httpRes.Body).Decode(res)
require.NoError(t, err)

currentUser = res
}

var otp string
{
require.Equal(t, curPhone, currentUser.Phone.String())
require.Equal(t, newPhone, currentUser.PhoneChange)

calls := inst.HookRecorder.SendSMS.GetCalls()
require.Equal(t, 1, len(calls))
call := calls[0]

hookReq := &v0hooks.SendSMSInput{}
err = call.Unmarshal(hookReq)
require.NoError(t, err)

require.Equal(t, currentUser.ID, hookReq.User.ID)
require.Equal(t, currentUser.Aud, hookReq.User.Aud)
require.Equal(t, currentUser.Phone, hookReq.User.Phone)
require.Equal(t, currentUser.AppMetaData, hookReq.User.AppMetaData)

otp = hookReq.SMS.OTP
otpHash := crypto.GenerateTokenHash(
currentUser.PhoneChange, hookReq.SMS.OTP)

ott, err := models.FindOneTimeToken(
inst.Conn,
otpHash,
models.PhoneChangeToken)
require.NoError(t, err)
require.Equal(t, currentUser.ID.String(), ott.UserID.String())
require.Equal(t, currentUser.PhoneChange, ott.RelatesTo)

latestUser, err := models.FindUserByID(inst.Conn, currentUser.ID)
require.NoError(t, err)
require.NotNil(t, latestUser)

currentUser = latestUser
}

{
req := &api.VerifyParams{
Type: "phone_change",
Token: otp,
Phone: currentUser.PhoneChange,
}
res := new(models.User)

body := new(bytes.Buffer)
err = json.NewEncoder(body).Encode(req)
require.NoError(t, err)

httpReq, err := http.NewRequestWithContext(
ctx, "POST", "/verify", body)
require.NoError(t, err)

httpRes, err := inst.Do(httpReq)
require.NoError(t, err)
require.Equal(t, 200, httpRes.StatusCode)

err = json.NewDecoder(httpRes.Body).Decode(res)
require.NoError(t, err)
}
})
})

t.Run("SignupAnonymously", func(t *testing.T) {
Expand Down Expand Up @@ -422,7 +589,7 @@ func TestE2EHooks(t *testing.T) {
mfaUser = runVerifyBeforeUserCreatedHook(t, inst, mfaUser)
runVerifyAfterUserCreatedHook(t, inst, mfaUser)
require.NotNil(t, mfaUser)
mfaUserAccessToken = getAccessToken(
mfaUserAccessToken = getEmailAccessToken(
ctx, t, inst, string(mfaUser.Email), defaultPassword)

phone := genPhone()
Expand Down Expand Up @@ -813,7 +980,7 @@ func TestE2EHooks(t *testing.T) {
require.NoError(t, err)
defer inst.Close()

signupAndConfirm(ctx, t, inst)
signupAndConfirmEmail(ctx, t, inst)
})

t.Run("SecureEmailChange=Enabled", func(t *testing.T) {
Expand All @@ -829,11 +996,11 @@ func TestE2EHooks(t *testing.T) {
// test requires this flag
require.True(t, inst.Config.Mailer.SecureEmailChangeEnabled)

signupUser := signupAndConfirm(ctx, t, inst)
signupUser := signupAndConfirmEmail(ctx, t, inst)
currentUser := signupUser

// get access token
currentAccessToken := getAccessToken(
currentAccessToken := getEmailAccessToken(
ctx, t, inst, string(currentUser.Email), defaultPassword)

// update email
Expand Down Expand Up @@ -1045,11 +1212,11 @@ func TestE2EHooks(t *testing.T) {
// test requires this flag
require.False(t, inst.Config.Mailer.SecureEmailChangeEnabled)

signupUser := signupAndConfirm(ctx, t, inst)
signupUser := signupAndConfirmEmail(ctx, t, inst)
currentUser := signupUser

// get access token
currentAccessToken := getAccessToken(
currentAccessToken := getEmailAccessToken(
ctx, t, inst, string(currentUser.Email), defaultPassword)

// update email
Expand Down