Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add 'VERIFY_AND_CHANGE_EMAIL' linkType for generateEmailActionLink #498

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
36 changes: 29 additions & 7 deletions auth/email_action_links.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ func (settings *ActionCodeSettings) toMap() (map[string]interface{}, error) {
type linkType string

const (
emailLinkSignIn linkType = "EMAIL_SIGNIN"
emailVerification linkType = "VERIFY_EMAIL"
passwordReset linkType = "PASSWORD_RESET"
emailLinkSignIn linkType = "EMAIL_SIGNIN"
emailVerification linkType = "VERIFY_EMAIL"
passwordReset linkType = "PASSWORD_RESET"
verifyAndChangeEmail linkType = "VERIFY_AND_CHANGE_EMAIL"
)

// EmailVerificationLink generates the out-of-band email action link for email verification flows for the specified
Expand All @@ -79,7 +80,7 @@ func (c *baseClient) EmailVerificationLink(ctx context.Context, email string) (s
// specified email address, using the action code settings provided.
func (c *baseClient) EmailVerificationLinkWithSettings(
ctx context.Context, email string, settings *ActionCodeSettings) (string, error) {
return c.generateEmailActionLink(ctx, emailVerification, email, settings)
return c.generateEmailActionLink(ctx, emailVerification, email, settings, nil)
}

// PasswordResetLink generates the out-of-band email action link for password reset flows for the specified email
Expand All @@ -92,18 +93,31 @@ func (c *baseClient) PasswordResetLink(ctx context.Context, email string) (strin
// specified email address, using the action code settings provided.
func (c *baseClient) PasswordResetLinkWithSettings(
ctx context.Context, email string, settings *ActionCodeSettings) (string, error) {
return c.generateEmailActionLink(ctx, passwordReset, email, settings)
return c.generateEmailActionLink(ctx, passwordReset, email, settings, nil)
}

// EmailSignInLink generates the out-of-band email action link for email link sign-in flows, using the action
// code settings provided.
func (c *baseClient) EmailSignInLink(
ctx context.Context, email string, settings *ActionCodeSettings) (string, error) {
return c.generateEmailActionLink(ctx, emailLinkSignIn, email, settings)
return c.generateEmailActionLink(ctx, emailLinkSignIn, email, settings, nil)
}

// VerifyAndChangeEmailLink generates the out-of-band email action link for email verification and change flows for the
// specified email address.
func (c *baseClient) VerifyAndChangeEmailLink(ctx context.Context, email string, newEmail string) (string, error) {
return c.VerifyAndChangeEmailLinkWithSettings(ctx, email, newEmail, nil)
}

// VerifyAndChangeEmailLinkWithSettings generates the out-of-band email action link for email verification and change
// flows for the specified email address, using the action code settings provided.
func (c *baseClient) VerifyAndChangeEmailLinkWithSettings(
ctx context.Context, email string, newEmail string, settings *ActionCodeSettings) (string, error) {
return c.generateEmailActionLink(ctx, verifyAndChangeEmail, email, settings, &newEmail)
}

func (c *baseClient) generateEmailActionLink(
ctx context.Context, linkType linkType, email string, settings *ActionCodeSettings) (string, error) {
ctx context.Context, linkType linkType, email string, settings *ActionCodeSettings, newEmail *string) (string, error) {

if email == "" {
return "", errors.New("email must not be empty")
Expand All @@ -118,6 +132,14 @@ func (c *baseClient) generateEmailActionLink(
"email": email,
"returnOobLink": true,
}

if linkType == verifyAndChangeEmail {
if newEmail == nil {
return "", errors.New("newEmail must not be nil when linkType is verifyAndChangeEmail")
}
payload["newEmail"] = *newEmail
}

if settings != nil {
settingsMap, err := settings.toMap()
if err != nil {
Expand Down
50 changes: 50 additions & 0 deletions auth/email_action_links_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
testActionLink = "https://test.link"
testActionLinkFormat = `{"oobLink": %q}`
testEmail = "user@domain.com"
testNewEmail = "user-new@domain.com"
)

var testActionLinkResponse = []byte(fmt.Sprintf(testActionLinkFormat, testActionLink))
Expand Down Expand Up @@ -309,6 +310,55 @@ func TestEmailVerificationLinkError(t *testing.T) {
}
}

func TestVerifyAndChangeEmailLink(t *testing.T) {
s := echoServer(testActionLinkResponse, t)
defer s.Close()

link, err := s.Client.VerifyAndChangeEmailLink(context.Background(), testEmail, testNewEmail)
if err != nil {
t.Fatal(err)
}
if link != testActionLink {
t.Errorf("TestVerifyAndChangeEmailLink() = %q; want = %q", link, testActionLink)
}

want := map[string]interface{}{
"requestType": "VERIFY_AND_CHANGE_EMAIL",
"email": testEmail,
"returnOobLink": true,
"newEmail": testNewEmail,
}
if err := checkActionLinkRequest(want, s); err != nil {
t.Fatalf("TestVerifyAndChangeEmailLink() %v", err)
}
}

func TestVerifyAndChangeEmailLinkWithSettings(t *testing.T) {
s := echoServer(testActionLinkResponse, t)
defer s.Close()

link, err := s.Client.VerifyAndChangeEmailLinkWithSettings(context.Background(), testEmail, testNewEmail, testActionCodeSettings)
if err != nil {
t.Fatal(err)
}
if link != testActionLink {
t.Errorf("VerifyAndChangeEmailLinkWithSettings() = %q; want = %q", link, testActionLink)
}

want := map[string]interface{}{
"requestType": "VERIFY_AND_CHANGE_EMAIL",
"email": testEmail,
"returnOobLink": true,
"newEmail": testNewEmail,
}
for k, v := range testActionCodeSettingsMap {
want[k] = v
}
if err := checkActionLinkRequest(want, s); err != nil {
t.Fatalf("VerifyAndChangeEmailLinkWithSettings() %v", err)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: t.Fatalf(checkActionLinkRequest() = %v", err)

}
}

func checkActionLinkRequest(want map[string]interface{}, s *mockAuthServer) error {
wantURL := "/projects/mock-project-id/accounts:sendOobCode"
return checkActionLinkRequestWithURL(want, wantURL, s)
Expand Down
28 changes: 28 additions & 0 deletions auth/tenant_mgt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,34 @@ func TestTenantEmailSignInLink(t *testing.T) {
}
}

func TestTenantVerifyAndChangeEmail(t *testing.T) {
s := echoServer(testActionLinkResponse, t)
defer s.Close()

client, err := s.Client.TenantManager.AuthForTenant("tenantID")
if err != nil {
t.Fatalf("AuthForTenant() = %v", err)
}

link, err := client.VerifyAndChangeEmailLink(context.Background(), testEmail, testNewEmail)
if err != nil {
t.Fatal(err)
}
if link != testActionLink {
t.Errorf("VerifyAndChangeEmailLink() = %q; want = %q", link, testActionLink)
}

want := map[string]interface{}{
"requestType": "VERIFY_AND_CHANGE_EMAIL",
"email": testEmail,
"returnOobLink": true,
"newEmail": testNewEmail,
}
if err := checkActionLinkRequestWithURL(want, wantEmailActionURL, s); err != nil {
t.Fatalf("VerifyAndChangeEmailLink() %v", err)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: t.Fatalf("checkActionLInkREquestWithURL() = %v", err)

}
}

func TestTenantOIDCProviderConfig(t *testing.T) {
s := echoServer([]byte(oidcConfigResponse), t)
defer s.Close()
Expand Down
17 changes: 17 additions & 0 deletions integration/auth/tenant_mgt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,23 @@ func testTenantAwareUserManagement(t *testing.T, id string) {
}
})

t.Run("VerifyAndChangeEmailLink()", func(t *testing.T) {
newEmail := "new-" + want.Email
link, err := tenantClient.VerifyAndChangeEmailLink(context.Background(), want.Email, newEmail)
if err != nil {
t.Fatalf("VerifyAndChangeEmailLink() = %v", err)
}

tenant, err := extractTenantID(link)
if err != nil {
t.Fatalf("VerifyAndChangeEmailLink() = %v", err)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: t.Fatalf("extractTenantID(%s) = %v", link, err)

}

if id != tenant {
t.Fatalf("VerifyAndChangeEmailLink() TenantID = %q; want = %q", tenant, id)
}
})

t.Run("RevokeRefreshTokens()", func(t *testing.T) {
validSinceMillis := time.Now().Unix() * 1000
time.Sleep(1 * time.Second)
Expand Down