Skip to content

Commit

Permalink
rpc: return an error if email already used
Browse files Browse the repository at this point in the history
  • Loading branch information
patrislav committed Apr 24, 2024
1 parent 09fccd6 commit c53abcd
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 33 deletions.
17 changes: 0 additions & 17 deletions rpc/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,23 +164,6 @@ func normalizeIssuer(iss string) string {
}

func getEmailFromToken(tok jwt.Token) string {
// TODO: we might want to relax this a bit and not depend so much on the `email_verified` claim
emailVerifiedClaim, ok := tok.Get("email_verified")
if !ok {
return ""
}

verified := false
switch v := emailVerifiedClaim.(type) {
case bool:
verified = v
case string:
verified = strings.TrimSpace(strings.ToLower(v)) == "true"
}
if !verified {
return ""
}

emailClaim, ok := tok.Get("email")
if !ok {
return ""
Expand Down
10 changes: 10 additions & 0 deletions rpc/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ func (s *RPC) RegisterSession(
}

if !accountFound {
if !intentTyped.Data.ForceCreateAccount {
accs, err := s.Accounts.ListByEmail(ctx, tntData.ProjectID, identity.Email)
if err != nil {
return nil, nil, fmt.Errorf("failed to perform email check: %w", err)
}
if len(accs) > 0 {
return nil, nil, proto.ErrEmailAlreadyInUse
}
}

accData := &proto.AccountData{
ProjectID: tntData.ProjectID,
UserID: fmt.Sprintf("%d|%s", tntData.ProjectID, sessionHash),
Expand Down
52 changes: 36 additions & 16 deletions rpc/sessions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,28 +107,15 @@ func TestRPC_RegisterSession(t *testing.T) {
},
"WithVerifiedEmail": {
tokBuilderFn: func(b *jwt.Builder, url string) {
b.Claim("email", "user@example.com").
b.Claim("email", "123@example.com").
Claim("email_verified", "true").
Claim("sequence:session_hash", sessHash)
},
assertFn: func(t *testing.T, sess *proto.Session, err error, p assertionParams) {
require.NoError(t, err)
require.NotNil(t, sess)

assert.Equal(t, "[email protected]", sess.Identity.Email)
},
},
"WithUnverifiedEmail": {
tokBuilderFn: func(b *jwt.Builder, url string) {
b.Claim("email", "[email protected]").
Claim("email_verified", "false").
Claim("sequence:session_hash", sessHash)
},
assertFn: func(t *testing.T, sess *proto.Session, err error, p assertionParams) {
require.NoError(t, err)
require.NotNil(t, sess)

assert.Equal(t, "", sess.Identity.Email)
assert.Equal(t, "[email protected]", sess.Identity.Email)
},
},
"MissingSignature": {
Expand Down Expand Up @@ -159,6 +146,36 @@ func TestRPC_RegisterSession(t *testing.T) {
assert.Equal(t, httpsIssuer, sess.Identity.Issuer)
},
},
"EmailAlreadyInUse": {
tokBuilderFn: func(b *jwt.Builder, url string) {
b.Claim("email", "[email protected]").
Claim("sequence:session_hash", sessHash)
},
assertFn: func(t *testing.T, sess *proto.Session, err error, p assertionParams) {
assert.ErrorIs(t, err, proto.ErrEmailAlreadyInUse)
assert.Nil(t, sess)
},
},
"EmailAlreadyInUseWithForceCreateAccount": {
intentBuilderFn: func(t *testing.T, data intents.IntentDataOpenSession) *proto.Intent {
data.ForceCreateAccount = true
return generateSignedIntent(t, intents.IntentName_openSession, data, signingSession)
},
tokBuilderFn: func(b *jwt.Builder, url string) {
b.Claim("email", "[email protected]").
Claim("sequence:session_hash", sessHash)
},
assertFn: func(t *testing.T, sess *proto.Session, err error, p assertionParams) {
require.NoError(t, err)
require.NotNil(t, sess)

assert.Len(t, p.dbClient.accounts[p.tenant.ProjectID], 2)
assert.Contains(t, p.dbClient.sessions, sess.ID)
assert.Contains(t, p.dbClient.accounts[p.tenant.ProjectID], sess.Identity.String())
assert.Contains(t, p.walletService.registeredSessions, sess.ID)
assert.Contains(t, p.walletService.registeredUsers, sess.UserID)
},
},
}

for label, testCase := range testCases {
Expand All @@ -180,11 +197,14 @@ func TestRPC_RegisterSession(t *testing.T) {
require.NoError(t, err)

tenant, _ := newTenant(t, enc, issuer)
account := newAccount(t, enc, "http://another-issuer", nil)

dbClient := &dbMock{
sessions: map[string]*data.Session{},
tenants: map[uint64][]*data.Tenant{tenant.ProjectID: {tenant}},
accounts: map[uint64]map[string]*data.Account{},
accounts: map[uint64]map[string]*data.Account{
tenant.ProjectID: {account.Identity.String(): account},
},
}
svc := initRPC(cfg, enc, dbClient)
walletService := newWalletServiceMock(nil)
Expand Down

0 comments on commit c53abcd

Please sign in to comment.