Skip to content

Commit 851c0a2

Browse files
authored
Remove (base64) 'REDACTED' passwords from user records. (#277)
These values look like passwords hashes, but aren't, leading to potential confusion. Additionally, added docs to CONTRIBUTING.md detailing how to add the permission that causes password hashes to be properly returned as well as adjusting the test failure message should the developer not add that permission. b/141189502
1 parent effa840 commit 851c0a2

File tree

4 files changed

+44
-4
lines changed

4 files changed

+44
-4
lines changed

CONTRIBUTING.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ following credentials from the project:
130130
your Go workspace as
131131
`src/firebase.google.com/go/testdata/integration_apikey.txt`.
132132

133+
You'll also need to grant your service account the 'Firebase Authentication Admin' role. This is
134+
required to ensure that exported user records contain the password hashes of the user accounts:
135+
1. Go to [Google Cloud Platform Console / IAM & admin](https://console.cloud.google.com/iam-admin).
136+
2. Find your service account in the list, and click the 'pencil' icon to edit it's permissions.
137+
3. Click 'ADD ANOTHER ROLE' and choose 'Firebase Authentication Admin'.
138+
4. Click 'SAVE'.
139+
133140
Now you can invoke the test suite as follows:
134141

135142
```bash

auth/user_mgt.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package auth
1616

1717
import (
1818
"context"
19+
"encoding/base64"
1920
"encoding/json"
2021
"errors"
2122
"fmt"
@@ -35,6 +36,9 @@ const (
3536
idToolkitV1Endpoint = "https://identitytoolkit.googleapis.com/v1"
3637
)
3738

39+
// 'REDACTED', encoded as a base64 string.
40+
var b64Redacted = base64.StdEncoding.EncodeToString([]byte("REDACTED"))
41+
3842
// UserInfo is a collection of standard profile information for a user.
3943
type UserInfo struct {
4044
DisplayName string `json:"displayName,omitempty"`
@@ -595,6 +599,14 @@ func (r *userQueryResponse) makeExportedUserRecord() (*ExportedUserRecord, error
595599
}
596600
}
597601

602+
// If the password hash is redacted (probably due to missing permissions)
603+
// then clear it out, similar to how the salt is returned. (Otherwise, it
604+
// *looks* like a b64-encoded hash is present, which is confusing.)
605+
hash := r.PasswordHash
606+
if hash == b64Redacted {
607+
hash = ""
608+
}
609+
598610
return &ExportedUserRecord{
599611
UserRecord: &UserRecord{
600612
UserInfo: &UserInfo{
@@ -615,7 +627,7 @@ func (r *userQueryResponse) makeExportedUserRecord() (*ExportedUserRecord, error
615627
CreationTimestamp: r.CreationTimestamp,
616628
},
617629
},
618-
PasswordHash: r.PasswordHash,
630+
PasswordHash: hash,
619631
PasswordSalt: r.PasswordSalt,
620632
}, nil
621633
}

auth/user_mgt_test.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,7 @@ func TestInvalidDeleteUser(t *testing.T) {
995995
}
996996

997997
func TestMakeExportedUser(t *testing.T) {
998-
rur := &userQueryResponse{
998+
queryResponse := &userQueryResponse{
999999
UID: "testuser",
10001000
10011001
PhoneNumber: "+1234567890",
@@ -1028,7 +1028,7 @@ func TestMakeExportedUser(t *testing.T) {
10281028
PasswordHash: "passwordhash",
10291029
PasswordSalt: "salt",
10301030
}
1031-
exported, err := rur.makeExportedUserRecord()
1031+
exported, err := queryResponse.makeExportedUserRecord()
10321032
if err != nil {
10331033
t.Fatal(err)
10341034
}
@@ -1045,6 +1045,21 @@ func TestMakeExportedUser(t *testing.T) {
10451045
}
10461046
}
10471047

1048+
func TestExportedUserRecordShouldClearRedacted(t *testing.T) {
1049+
queryResponse := &userQueryResponse{
1050+
UID: "uid1",
1051+
PasswordHash: base64.StdEncoding.EncodeToString([]byte("REDACTED")),
1052+
}
1053+
1054+
exported, err := queryResponse.makeExportedUserRecord()
1055+
if err != nil {
1056+
t.Fatal(err)
1057+
}
1058+
if exported.PasswordHash != "" {
1059+
t.Errorf("PasswordHash = %q; want = ''", exported.PasswordHash)
1060+
}
1061+
}
1062+
10481063
func TestSessionCookie(t *testing.T) {
10491064
resp := `{
10501065
"sessionCookie": "expectedCookie"

integration/auth/user_mgt_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ func TestDeleteNonExistingUser(t *testing.T) {
107107
}
108108

109109
func TestListUsers(t *testing.T) {
110+
errMsgTemplate := "Users() %s = empty; want = non-empty. A common cause would be " +
111+
"forgetting to add the 'Firebase Authentication Admin' permission. See " +
112+
"instructions in CONTRIBUTING.md"
110113
newUsers := map[string]bool{}
111114
user := newUserWithParams(t)
112115
defer deleteUser(user.UID)
@@ -133,7 +136,10 @@ func TestListUsers(t *testing.T) {
133136
if _, ok := newUsers[u.UID]; ok {
134137
count++
135138
if u.PasswordHash == "" {
136-
t.Errorf("Users() PasswordHash = empty; want = non-empty")
139+
t.Errorf(errMsgTemplate, "PasswordHash")
140+
}
141+
if u.PasswordSalt == "" {
142+
t.Errorf(errMsgTemplate, "PasswordSalt")
137143
}
138144
}
139145
}

0 commit comments

Comments
 (0)