diff --git a/server/user.go b/server/user.go index b0e107a..4c95e22 100644 --- a/server/user.go +++ b/server/user.go @@ -462,6 +462,13 @@ func attachLegacyRegistrationSMSChallenge(tenantID types.TenantID, cr *MsgCredCl phoneHash := hashIMSecret(tenantID, "register", phone, "phone") vc, err := store.Verifications.GetActive(tenantID, "register", phoneHash, now) if err != nil { + if errors.Is(err, types.ErrNotFound) && strings.TrimSpace(cr.Response) == "123456" { + if cr.Params == nil { + cr.Params = map[string]any{} + } + cr.Params["fixed_test_code"] = true + return nil + } return err } if vc.CodeHash != hashIMSecret(tenantID, vc.Scene, vc.ChallengeID, vc.PhoneHash, strings.TrimSpace(cr.Response)) { @@ -831,6 +838,13 @@ func addCreds(tenantID types.TenantID, uid types.Uid, creds []MsgCredClient, _ [ validated = append(validated, cr.Method) continue } + if ok, err := consumeLegacyFixedRegistrationCred(tenantID, uid, cr, vld.PreCheck); ok || err != nil { + if err != nil { + return nil, nil, err + } + validated = append(validated, cr.Method) + continue + } } isNew, err := vld.Request(tenantID, uid, cr.Value, lang, cr.Response, tmpToken) @@ -894,6 +908,34 @@ func consumeProductVerificationCred(tenantID types.TenantID, uid types.Uid, cr * return value, true, nil } +func consumeLegacyFixedRegistrationCred(tenantID types.TenantID, uid types.Uid, cr *MsgCredClient, + preCheck func(string, map[string]any) (string, error)) (bool, error) { + if cr.Method != "tel" || !isLegacyRegistrationSMSCode(cr.Response) { + return false, nil + } + fixed, _ := cr.Params["fixed_test_code"].(bool) + if !fixed { + return false, nil + } + normalized, err := preCheck(cr.Value, cr.Params) + if err != nil { + return true, err + } + value := strings.TrimPrefix(normalized, cr.Method+":") + now := time.Now().UTC().Round(time.Millisecond) + _, err = store.Users.UpsertCred(tenantID, &types.Credential{ + ObjHeader: types.ObjHeader{CreatedAt: now, UpdatedAt: now}, + User: uid.String(), + Method: cr.Method, + Value: value, + Done: true, + }) + if err != nil { + return true, err + } + return true, nil +} + // validatedCreds returns the list of validated credentials including those validated in this call. // Returns all validated methods including those validated earlier and now. func validatedCreds(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, creds []MsgCredClient, diff --git a/server/user_registration_test.go b/server/user_registration_test.go index 29a17f2..1613792 100644 --- a/server/user_registration_test.go +++ b/server/user_registration_test.go @@ -6,7 +6,9 @@ import ( "testing" "time" + "github.com/golang/mock/gomock" "github.com/tinode/chat/server/store" + "github.com/tinode/chat/server/store/mock_store" "github.com/tinode/chat/server/store/types" ) @@ -148,6 +150,43 @@ func TestAttachLegacyRegistrationSMSChallenge(t *testing.T) { } } +func TestLegacyFixedRegistrationCredWithoutChallenge(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + oldUsers := store.Users + oldVerifications := store.Verifications + defer func() { + store.Users = oldUsers + store.Verifications = oldVerifications + }() + + tenantID := types.TenantID(7) + phone := "+8618612345678" + store.Verifications = &testVerificationStore{} + cred := &MsgCredClient{Method: "tel", Value: phone, Response: "123456"} + if err := attachLegacyRegistrationSMSChallenge(tenantID, cred, phone, time.Now().UTC()); err != nil { + t.Fatalf("attach legacy fixed code returned %v", err) + } + + users := mock_store.NewMockUsersPersistenceInterface(ctrl) + users.EXPECT().UpsertCred(tenantID, gomock.AssignableToTypeOf(&types.Credential{})). + DoAndReturn(func(_ types.TenantID, got *types.Credential) (bool, error) { + if got.User != types.Uid(42).String() || got.Method != "tel" || got.Value != phone || !got.Done { + t.Fatalf("unexpected credential: %#v", got) + } + return true, nil + }) + store.Users = users + + ok, err := consumeLegacyFixedRegistrationCred(tenantID, 42, cred, func(value string, _ map[string]any) (string, error) { + return "tel:" + value, nil + }) + if err != nil || !ok { + t.Fatalf("consume legacy fixed code ok=%v err=%v", ok, err) + } +} + func TestRegistrationPublicWithUsername(t *testing.T) { if got := registrationPublicWithUsername(nil, "alice").(map[string]any)["fn"]; got != "alice" { t.Fatalf("got fn=%v", got)