Skip to content

Commit 37edd08

Browse files
authored
Migrate Users() API to internal.HTTPClient (#242)
* Migrating DeleteUser() API to the internal.HTTPClient * Migrated ImportUsers() to internal.HTTPClient * Minor clean up * Migrated Users() API to internal.HTTPClient
1 parent 0aa3e83 commit 37edd08

File tree

4 files changed

+64
-111
lines changed

4 files changed

+64
-111
lines changed

auth/auth.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"strings"
2424

2525
"firebase.google.com/go/internal"
26-
"google.golang.org/api/identitytoolkit/v3"
2726
)
2827

2928
const (
@@ -41,12 +40,10 @@ var reservedClaims = []string{
4140
// Client facilitates generating custom JWT tokens for Firebase clients, and verifying ID tokens issued
4241
// by Firebase backend services.
4342
type Client struct {
44-
is *identitytoolkit.Service
4543
userManagementClient
4644
idTokenVerifier *tokenVerifier
4745
cookieVerifier *tokenVerifier
4846
signer cryptoSigner
49-
version string
5047
clock internal.Clock
5148
}
5249

@@ -90,11 +87,6 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
9087
return nil, err
9188
}
9289

93-
is, err := identitytoolkit.New(hc.Client)
94-
if err != nil {
95-
return nil, err
96-
}
97-
9890
idTokenVerifier, err := newIDTokenVerifier(ctx, conf.ProjectID)
9991
if err != nil {
10092
return nil, err
@@ -113,11 +105,9 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
113105
version: version,
114106
httpClient: hc,
115107
},
116-
is: is,
117108
idTokenVerifier: idTokenVerifier,
118109
cookieVerifier: cookieVerifier,
119110
signer: signer,
120-
version: version, // This can be removed when userManagementClient implements all user mgt APIs.
121111
clock: internal.SystemClock,
122112
}, nil
123113
}

auth/export_users.go

Lines changed: 34 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ package auth
1717
import (
1818
"context"
1919
"encoding/json"
20+
"fmt"
21+
"net/http"
22+
"net/url"
23+
"strconv"
2024

21-
identitytoolkit "google.golang.org/api/identitytoolkit/v3"
2225
"google.golang.org/api/iterator"
2326
)
2427

@@ -28,7 +31,7 @@ const maxReturnedResults = 1000
2831
//
2932
// If nextPageToken is empty, the iterator will start at the beginning.
3033
// If the nextPageToken is not empty, the iterator starts after the token.
31-
func (c *Client) Users(ctx context.Context, nextPageToken string) *UserIterator {
34+
func (c *userManagementClient) Users(ctx context.Context, nextPageToken string) *UserIterator {
3235
it := &UserIterator{
3336
ctx: ctx,
3437
client: c,
@@ -46,7 +49,7 @@ func (c *Client) Users(ctx context.Context, nextPageToken string) *UserIterator
4649
//
4750
// Also see: https://github.com/GoogleCloudPlatform/google-cloud-go/wiki/Iterator-Guidelines
4851
type UserIterator struct {
49-
client *Client
52+
client *userManagementClient
5053
ctx context.Context
5154
nextFunc func() error
5255
pageInfo *iterator.PageInfo
@@ -70,26 +73,43 @@ func (it *UserIterator) Next() (*ExportedUserRecord, error) {
7073
}
7174

7275
func (it *UserIterator) fetch(pageSize int, pageToken string) (string, error) {
73-
request := &identitytoolkit.IdentitytoolkitRelyingpartyDownloadAccountRequest{
74-
MaxResults: int64(pageSize),
75-
NextPageToken: pageToken,
76+
query := make(url.Values)
77+
query.Set("maxResults", strconv.Itoa(pageSize))
78+
if pageToken != "" {
79+
query.Set("nextPageToken", pageToken)
7680
}
77-
call := it.client.is.Relyingparty.DownloadAccount(request)
78-
it.client.setHeader(call)
79-
resp, err := call.Context(it.ctx).Do()
81+
82+
req, err := it.client.newRequest(http.MethodGet, fmt.Sprintf("/accounts:batchGet?%s", query.Encode()))
8083
if err != nil {
81-
return "", handleServerError(err)
84+
return "", err
85+
}
86+
87+
resp, err := it.client.httpClient.Do(it.ctx, req)
88+
if err != nil {
89+
return "", err
90+
}
91+
92+
if resp.Status != http.StatusOK {
93+
return "", handleHTTPError(resp)
8294
}
8395

84-
for _, u := range resp.Users {
85-
eu, err := makeExportedUser(u)
96+
var parsed struct {
97+
Users []userQueryResponse `json:"users"`
98+
NextPageToken string `json:"nextPageToken"`
99+
}
100+
if err := json.Unmarshal(resp.Body, &parsed); err != nil {
101+
return "", err
102+
}
103+
104+
for _, u := range parsed.Users {
105+
eu, err := u.makeExportedUserRecord()
86106
if err != nil {
87107
return "", err
88108
}
89109
it.users = append(it.users, eu)
90110
}
91-
it.pageInfo.Token = resp.NextPageToken
92-
return resp.NextPageToken, nil
111+
it.pageInfo.Token = parsed.NextPageToken
112+
return parsed.NextPageToken, nil
93113
}
94114

95115
// ExportedUserRecord is the returned user value used when listing all the users.
@@ -98,53 +118,3 @@ type ExportedUserRecord struct {
98118
PasswordHash string
99119
PasswordSalt string
100120
}
101-
102-
func makeExportedUser(r *identitytoolkit.UserInfo) (*ExportedUserRecord, error) {
103-
var cc map[string]interface{}
104-
if r.CustomAttributes != "" {
105-
if err := json.Unmarshal([]byte(r.CustomAttributes), &cc); err != nil {
106-
return nil, err
107-
}
108-
if len(cc) == 0 {
109-
cc = nil
110-
}
111-
}
112-
113-
var providerUserInfo []*UserInfo
114-
for _, u := range r.ProviderUserInfo {
115-
info := &UserInfo{
116-
DisplayName: u.DisplayName,
117-
Email: u.Email,
118-
PhoneNumber: u.PhoneNumber,
119-
PhotoURL: u.PhotoUrl,
120-
ProviderID: u.ProviderId,
121-
UID: u.RawId,
122-
}
123-
providerUserInfo = append(providerUserInfo, info)
124-
}
125-
126-
resp := &ExportedUserRecord{
127-
UserRecord: &UserRecord{
128-
UserInfo: &UserInfo{
129-
DisplayName: r.DisplayName,
130-
Email: r.Email,
131-
PhoneNumber: r.PhoneNumber,
132-
PhotoURL: r.PhotoUrl,
133-
ProviderID: defaultProviderID,
134-
UID: r.LocalId,
135-
},
136-
CustomClaims: cc,
137-
Disabled: r.Disabled,
138-
EmailVerified: r.EmailVerified,
139-
ProviderUserInfo: providerUserInfo,
140-
TokensValidAfterMillis: r.ValidSince * 1000,
141-
UserMetadata: &UserMetadata{
142-
LastLogInTimestamp: r.LastLoginAt,
143-
CreationTimestamp: r.CreatedAt,
144-
},
145-
},
146-
PasswordHash: r.PasswordHash,
147-
PasswordSalt: r.Salt,
148-
}
149-
return resp, nil
150-
}

auth/user_mgt.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -487,11 +487,6 @@ func validatePhone(phone string) error {
487487
const idToolkitEndpoint = "https://identitytoolkit.googleapis.com/v1/projects"
488488

489489
// userManagementClient is a helper for interacting with the Identity Toolkit REST API.
490-
//
491-
// Currently it is only used for some user management operations (e.g. session cookie
492-
// generation). We will gradually migrate all the user management functionality to this
493-
// type, and remove our dependency on the google.golang.org/api/identitytoolkit/v3
494-
// package.
495490
type userManagementClient struct {
496491
baseURL string
497492
projectID string

auth/user_mgt_test.go

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import (
3030
"time"
3131

3232
"firebase.google.com/go/internal"
33-
"google.golang.org/api/identitytoolkit/v3"
3433
"google.golang.org/api/iterator"
3534
)
3635

@@ -181,7 +180,7 @@ func TestListUsers(t *testing.T) {
181180
{UserRecord: testUser, PasswordHash: "passwordhash3", PasswordSalt: "salt3"},
182181
}
183182

184-
testIterator := func(iter *UserIterator, token string, req map[string]interface{}) {
183+
testIterator := func(iter *UserIterator, token string, req string) {
185184
count := 0
186185
for i := 0; i < len(want); i++ {
187186
user, err := iter.Next()
@@ -209,20 +208,20 @@ func TestListUsers(t *testing.T) {
209208
t.Errorf("Users(%q) = %v, want = %v", token, err, iterator.Done)
210209
}
211210

212-
b, err := json.Marshal(req)
213-
if err != nil {
214-
t.Fatal(err)
215-
}
216-
if string(s.Rbody) != string(b) {
217-
t.Errorf("Users(%q) = %v, want = %v", token, string(s.Rbody), string(b))
211+
// Check the query string of the last HTTP request made.
212+
gotReq := s.Req[len(s.Req)-1].URL.Query().Encode()
213+
if gotReq != req {
214+
t.Errorf("Users(%q) = %q, want = %v", token, gotReq, req)
218215
}
219216
}
220217
testIterator(
221218
s.Client.Users(context.Background(), ""),
222-
"", map[string]interface{}{"maxResults": 1000})
219+
"",
220+
"maxResults=1000")
223221
testIterator(
224222
s.Client.Users(context.Background(), "pageToken"),
225-
"pageToken", map[string]interface{}{"maxResults": 1000, "nextPageToken": "pageToken"})
223+
"pageToken",
224+
"maxResults=1000&nextPageToken=pageToken")
226225
}
227226

228227
func TestInvalidCreateUser(t *testing.T) {
@@ -996,31 +995,31 @@ func TestInvalidDeleteUser(t *testing.T) {
996995
}
997996

998997
func TestMakeExportedUser(t *testing.T) {
999-
rur := &identitytoolkit.UserInfo{
1000-
LocalId: "testuser",
1001-
Email: "testuser@example.com",
1002-
PhoneNumber: "+1234567890",
1003-
EmailVerified: true,
1004-
DisplayName: "Test User",
1005-
Salt: "salt",
1006-
PhotoUrl: "http://www.example.com/testuser/photo.png",
1007-
PasswordHash: "passwordhash",
1008-
ValidSince: 1494364393,
1009-
Disabled: false,
1010-
CreatedAt: 1234567890000,
1011-
LastLoginAt: 1233211232000,
1012-
CustomAttributes: `{"admin": true, "package": "gold"}`,
1013-
ProviderUserInfo: []*identitytoolkit.UserInfoProviderUserInfo{
998+
rur := &userQueryResponse{
999+
UID: "testuser",
1000+
Email: "testuser@example.com",
1001+
PhoneNumber: "+1234567890",
1002+
EmailVerified: true,
1003+
DisplayName: "Test User",
1004+
PasswordSalt: "salt",
1005+
PhotoURL: "http://www.example.com/testuser/photo.png",
1006+
PasswordHash: "passwordhash",
1007+
ValidSinceSeconds: 1494364393,
1008+
Disabled: false,
1009+
CreationTimestamp: 1234567890000,
1010+
LastLogInTimestamp: 1233211232000,
1011+
CustomAttributes: `{"admin": true, "package": "gold"}`,
1012+
ProviderUserInfo: []*UserInfo{
10141013
{
1015-
ProviderId: "password",
1014+
ProviderID: "password",
10161015
DisplayName: "Test User",
1017-
PhotoUrl: "http://www.example.com/testuser/photo.png",
1016+
PhotoURL: "http://www.example.com/testuser/photo.png",
10181017
Email: "testuser@example.com",
1019-
RawId: "testuid",
1018+
UID: "testuid",
10201019
}, {
1021-
ProviderId: "phone",
1020+
ProviderID: "phone",
10221021
PhoneNumber: "+1234567890",
1023-
RawId: "testuid",
1022+
UID: "testuid",
10241023
}},
10251024
}
10261025

@@ -1029,7 +1028,7 @@ func TestMakeExportedUser(t *testing.T) {
10291028
PasswordHash: "passwordhash",
10301029
PasswordSalt: "salt",
10311030
}
1032-
exported, err := makeExportedUser(rur)
1031+
exported, err := rur.makeExportedUserRecord()
10331032
if err != nil {
10341033
t.Fatal(err)
10351034
}
@@ -1271,7 +1270,6 @@ func echoServer(resp interface{}, t *testing.T) *mockAuthServer {
12711270
if err != nil {
12721271
t.Fatal(err)
12731272
}
1274-
authClient.is.BasePath = s.Srv.URL + "/"
12751273
authClient.baseURL = s.Srv.URL
12761274
s.Client = authClient
12771275
return &s

0 commit comments

Comments
 (0)