Skip to content

Commit bc75c65

Browse files
authored
Remove (base64) 'REDACTED' passwords from user records. (#119)
These values *look* like password 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 e0473fa commit bc75c65

File tree

4 files changed

+56
-5
lines changed

4 files changed

+56
-5
lines changed

CONTRIBUTING.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,16 @@ test suite makes a large number of writes to the Firebase realtime database. Dow
144144
account key file from the "Settings > Service Accounts" page of the project, and copy it to
145145
`FirebaseAdmin/FirebaseAdmin.IntegrationTests/resources/integration_cert.json`. Also obtain the
146146
API key for the same project from "Settings > General", and save it to
147-
`FirebaseAdmin/FirebaseAdmin.IntegrationTests/resources/integration_apikey.txt`. Finally, to run
148-
the integration test suite:
147+
`FirebaseAdmin/FirebaseAdmin.IntegrationTests/resources/integration_apikey.txt`.
148+
149+
You'll also need to grant your service account the 'Firebase Authentication Admin' role. This is
150+
required to ensure that exported user records contain the password hashes of the user accounts:
151+
1. Go to [Google Cloud Platform Console / IAM & admin](https://console.cloud.google.com/iam-admin).
152+
2. Find your service account in the list, and click the 'pencil' icon to edit it's permissions.
153+
3. Click 'ADD ANOTHER ROLE' and choose 'Firebase Authentication Admin'.
154+
4. Click 'SAVE'.
155+
156+
Finally, to run the integration test suite:
149157

150158
```bash
151159
$ dotnet test FirebaseAdmin.IntegrationTests

FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAuthTest.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,15 @@ public async Task ListUsers()
324324
if (users.Contains(uid) && !listedUsers.Contains(uid))
325325
{
326326
listedUsers.Add(uid);
327-
Assert.NotNull(enumerator.Current.PasswordHash);
328-
Assert.NotNull(enumerator.Current.PasswordSalt);
327+
var errMsgTemplate = "Missing {0} field. A common cause would be "
328+
+ "forgetting to add the 'Firebase Authentication Admin' permission. "
329+
+ "See instructions in CONTRIBUTING.md";
330+
AssertWithMessage.NotNull(
331+
enumerator.Current.PasswordHash,
332+
string.Format(errMsgTemplate, "PasswordHash"));
333+
AssertWithMessage.NotNull(
334+
enumerator.Current.PasswordSalt,
335+
string.Format(errMsgTemplate, "PasswordSalt"));
329336
}
330337
}
331338

@@ -365,6 +372,20 @@ private static async Task<string> SignInWithCustomTokenAsync(string customToken)
365372
}
366373
}
367374

375+
/**
376+
* Additional Xunit style asserts that allow specifying an error message upon failure.
377+
*/
378+
internal static class AssertWithMessage
379+
{
380+
internal static void NotNull(object obj, string msg)
381+
{
382+
if (obj == null)
383+
{
384+
throw new Xunit.Sdk.XunitException("Assert.NotNull() Failure: " + msg);
385+
}
386+
}
387+
}
388+
368389
internal class SignInRequest
369390
{
370391
[Newtonsoft.Json.JsonProperty("token")]

FirebaseAdmin/FirebaseAdmin.Tests/Auth/ExportedUserRecordTest.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
using System;
1616
using System.Collections.Generic;
17+
using System.Text;
1718
using Xunit;
1819

1920
namespace FirebaseAdmin.Auth.Tests
@@ -151,5 +152,17 @@ public void AllProperties()
151152
Assert.Equal(UserRecord.UnixEpoch.AddMilliseconds(100), metadata.CreationTimestamp);
152153
Assert.Equal(UserRecord.UnixEpoch.AddMilliseconds(150), metadata.LastSignInTimestamp);
153154
}
155+
156+
[Fact]
157+
public void RedactedPasswordCleared()
158+
{
159+
var user = new ExportedUserRecord(new GetAccountInfoResponse.User()
160+
{
161+
UserId = "user1",
162+
PasswordHash = Convert.ToBase64String(Encoding.UTF8.GetBytes("REDACTED")),
163+
});
164+
165+
Assert.Null(user.PasswordHash);
166+
}
154167
}
155168
}

FirebaseAdmin/FirebaseAdmin/Auth/ExportedUserRecord.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using System;
16+
using System.Text;
17+
1518
namespace FirebaseAdmin.Auth
1619
{
1720
/// <summary>
@@ -20,10 +23,16 @@ namespace FirebaseAdmin.Auth
2023
/// </summary>
2124
public sealed class ExportedUserRecord : UserRecord
2225
{
26+
private static readonly string B64Redacted =
27+
Convert.ToBase64String(Encoding.UTF8.GetBytes("REDACTED"));
28+
2329
internal ExportedUserRecord(GetAccountInfoResponse.User user)
2430
: base(user)
2531
{
26-
this.PasswordHash = user.PasswordHash;
32+
// If the password hash is redacted (probably due to missing permissions) then clear it
33+
// out, similar to how the salt is returned. (Otherwise, it *looks* like a b64-encoded
34+
// hash is present, which is confusing.)
35+
this.PasswordHash = user.PasswordHash == B64Redacted ? null : user.PasswordHash;
2736
this.PasswordSalt = user.PasswordSalt;
2837
}
2938

0 commit comments

Comments
 (0)