Skip to content

Commit e3ef8da

Browse files
committed
Implement getUserByProviderUid.
1 parent d672e23 commit e3ef8da

File tree

4 files changed

+226
-25
lines changed

4 files changed

+226
-25
lines changed

FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAuthTest.cs

+18-1
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,15 @@ public async Task UserLifecycle()
225225
user = await FirebaseAuth.DefaultInstance.GetUserByEmailAsync(randomUser.Email);
226226
Assert.Equal(uid, user.Uid);
227227

228-
// Disable user and remove properties
228+
// Get user by phone provider uid
229+
user = await FirebaseAuth.DefaultInstance.GetUserByProviderUidAsync("phone", randomUser.PhoneNumber);
230+
Assert.Equal(uid, user.Uid);
231+
232+
// Get user by email provider uid
233+
user = await FirebaseAuth.DefaultInstance.GetUserByProviderUidAsync("email", randomUser.Email);
234+
Assert.Equal(uid, user.Uid);
235+
236+
// Disable user and remove properties
229237
var disableArgs = new UserRecordArgs()
230238
{
231239
Uid = uid,
@@ -276,6 +284,15 @@ public async Task GetUserNonExistingEmail()
276284
Assert.Equal(AuthErrorCode.UserNotFound, exception.AuthErrorCode);
277285
}
278286

287+
[Fact]
288+
public async Task GetUserNonExistingProviderUid()
289+
{
290+
var exception = await Assert.ThrowsAsync<FirebaseAuthException>(
291+
async () => await FirebaseAuth.DefaultInstance.GetUserByProviderUidAsync("google.com", "non_existing_user"));
292+
293+
Assert.Equal(AuthErrorCode.UserNotFound, exception.AuthErrorCode);
294+
}
295+
279296
[Fact]
280297
public async Task UpdateUserNonExistingUid()
281298
{

FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseUserManagerTest.cs

+106
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,112 @@ public async Task GetUserByPhoneNumberEmpty()
326326
await Assert.ThrowsAsync<ArgumentException>(() => auth.GetUserByPhoneNumberAsync(string.Empty));
327327
}
328328

329+
[Fact]
330+
public async Task GetUserByProviderUid()
331+
{
332+
var handler = new MockMessageHandler()
333+
{
334+
Response = GetUserResponse,
335+
};
336+
var auth = this.CreateFirebaseAuth(handler);
337+
338+
var userRecord = await auth.GetUserByProviderUidAsync("google.com", "google_uid");
339+
340+
Assert.Equal("user1", userRecord.Uid);
341+
Assert.Null(userRecord.DisplayName);
342+
Assert.Null(userRecord.Email);
343+
Assert.Null(userRecord.PhoneNumber);
344+
Assert.Null(userRecord.PhotoUrl);
345+
Assert.Equal("firebase", userRecord.ProviderId);
346+
Assert.False(userRecord.Disabled);
347+
Assert.False(userRecord.EmailVerified);
348+
Assert.Equal(UserRecord.UnixEpoch, userRecord.TokensValidAfterTimestamp);
349+
Assert.Empty(userRecord.CustomClaims);
350+
Assert.Empty(userRecord.ProviderData);
351+
Assert.Null(userRecord.UserMetaData.CreationTimestamp);
352+
Assert.Null(userRecord.UserMetaData.LastSignInTimestamp);
353+
354+
var request = NewtonsoftJsonSerializer.Instance.Deserialize<Dictionary<string, object>>(handler.LastRequestBody);
355+
JObject expectedFederatedUserId = new JObject();
356+
expectedFederatedUserId.Add("rawId", "google_uid");
357+
expectedFederatedUserId.Add("providerId", "google.com");
358+
Assert.Equal(new JArray(expectedFederatedUserId), request["federatedUserId"]);
359+
this.AssertClientVersion(handler.LastRequestHeaders);
360+
}
361+
362+
[Fact]
363+
public async Task GetUserByProviderUidWithPhoneProvider()
364+
{
365+
var handler = new MockMessageHandler()
366+
{
367+
Response = GetUserResponse,
368+
};
369+
var auth = this.CreateFirebaseAuth(handler);
370+
371+
var userRecord = await auth.GetUserByProviderUidAsync("phone", "+1234567890");
372+
373+
Assert.Equal("user1", userRecord.Uid);
374+
375+
var request = NewtonsoftJsonSerializer.Instance.Deserialize<Dictionary<string, object>>(handler.LastRequestBody);
376+
Assert.Equal(new JArray("+1234567890"), request["phoneNumber"]);
377+
Assert.False(request.ContainsKey("federatedUserId"));
378+
this.AssertClientVersion(handler.LastRequestHeaders);
379+
}
380+
381+
[Fact]
382+
public async Task GetUserByProviderUidWithEmailProvider()
383+
{
384+
var handler = new MockMessageHandler()
385+
{
386+
Response = GetUserResponse,
387+
};
388+
var auth = this.CreateFirebaseAuth(handler);
389+
390+
var userRecord = await auth.GetUserByProviderUidAsync("email", "[email protected]");
391+
392+
Assert.Equal("user1", userRecord.Uid);
393+
394+
var request = NewtonsoftJsonSerializer.Instance.Deserialize<Dictionary<string, object>>(handler.LastRequestBody);
395+
Assert.Equal(new JArray("[email protected]"), request["email"]);
396+
Assert.False(request.ContainsKey("federatedUserId"));
397+
this.AssertClientVersion(handler.LastRequestHeaders);
398+
}
399+
400+
[Fact]
401+
public async Task GetUserByProviderUidUserNotFound()
402+
{
403+
var handler = new MockMessageHandler()
404+
{
405+
Response = @"{""users"": []}",
406+
};
407+
var auth = this.CreateFirebaseAuth(handler);
408+
409+
var exception = await Assert.ThrowsAsync<FirebaseAuthException>(
410+
async () => await auth.GetUserByProviderUidAsync("google.com", "google_uid"));
411+
412+
Assert.Equal(ErrorCode.NotFound, exception.ErrorCode);
413+
Assert.Equal(AuthErrorCode.UserNotFound, exception.AuthErrorCode);
414+
Assert.Equal("Failed to get user with providerId: google.com, providerUid: google_uid", exception.Message);
415+
Assert.NotNull(exception.HttpResponse);
416+
Assert.Null(exception.InnerException);
417+
}
418+
419+
[Fact]
420+
public async Task GetUserByProviderUidNull()
421+
{
422+
var auth = this.CreateFirebaseAuth(new MockMessageHandler());
423+
await Assert.ThrowsAsync<ArgumentException>(() => auth.GetUserByProviderUidAsync("google.com", null));
424+
await Assert.ThrowsAsync<ArgumentException>(() => auth.GetUserByProviderUidAsync(null, "google_uid"));
425+
}
426+
427+
[Fact]
428+
public async Task GetUserByProviderUidEmpty()
429+
{
430+
var auth = this.CreateFirebaseAuth(new MockMessageHandler());
431+
await Assert.ThrowsAsync<ArgumentException>(() => auth.GetUserByProviderUidAsync("google.com", string.Empty));
432+
await Assert.ThrowsAsync<ArgumentException>(() => auth.GetUserByProviderUidAsync(string.Empty, "google_uid"));
433+
}
434+
329435
[Fact]
330436
public async Task ListUsers()
331437
{

FirebaseAdmin/FirebaseAdmin/Auth/FirebaseAuth.cs

+43
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,49 @@ public async Task<UserRecord> GetUserByPhoneNumberAsync(
413413
.ConfigureAwait(false);
414414
}
415415

416+
/// <summary>
417+
/// Gets a <see cref="UserRecord"/> object containing information about the user identified by
418+
/// <paramref name="providerId"/> and <paramref name="providerUid"/>.
419+
/// </summary>
420+
/// <param name="providerId">Identifier for the given provider, for example,
421+
/// "google.com" for the Google provider.</param>
422+
/// <param name="providerUid">The user identifier with the given provider.</param>
423+
/// <returns>A task that completes with a <see cref="UserRecord"/> representing
424+
/// a user with the specified provider user identifier.</returns>
425+
/// <exception cref="ArgumentException">If the provider identifier is null or empty,
426+
/// or if the provider user identifier is empty.</exception>
427+
/// <exception cref="FirebaseAuthException">If a user cannot be found with the specified
428+
/// provider user identifier.</exception>
429+
public async Task<UserRecord> GetUserByProviderUidAsync(string providerId, string providerUid)
430+
{
431+
return await this.GetUserByProviderUidAsync(providerId, providerUid, default(CancellationToken))
432+
.ConfigureAwait(false);
433+
}
434+
435+
/// <summary>
436+
/// Gets a <see cref="UserRecord"/> object containing information about the user identified by
437+
/// <paramref name="providerId"/> and <paramref name="providerUid"/>.
438+
/// </summary>
439+
/// <param name="providerId">Identifier for the given provider, for example,
440+
/// "google.com" for the Google provider.</param>
441+
/// <param name="providerUid">The user identifier with the given provider.</param>
442+
/// <param name="cancellationToken">A cancellation token to monitor the asynchronous
443+
/// operation.</param>
444+
/// <returns>A task that completes with a <see cref="UserRecord"/> representing
445+
/// a user with the specified provider user identifier.</returns>
446+
/// <exception cref="ArgumentException">If the provider identifier is null or empty,
447+
/// or if the provider user identifier is empty.</exception>
448+
/// <exception cref="FirebaseAuthException">If a user cannot be found with the specified
449+
/// provider user identifier.</exception>
450+
public async Task<UserRecord> GetUserByProviderUidAsync(
451+
string providerId, string providerUid, CancellationToken cancellationToken)
452+
{
453+
var userManager = this.IfNotDeleted(() => this.userManager.Value);
454+
455+
return await userManager.GetUserByProviderUidAsync(providerId, providerUid, cancellationToken)
456+
.ConfigureAwait(false);
457+
}
458+
416459
/// <summary>
417460
/// Updates an existing user account with the attributes contained in the specified <see cref="UserRecordArgs"/>.
418461
/// The <see cref="UserRecordArgs.Uid"/> property must be specified.

FirebaseAdmin/FirebaseAdmin/Auth/FirebaseUserManager.cs

+59-24
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ internal async Task<UserRecord> GetUserByIdAsync(
102102
var query = new UserQuery()
103103
{
104104
Field = "localId",
105-
Value = uid,
106-
Label = "uid",
105+
Value = new string[] { uid },
106+
Description = "uid: " + uid,
107107
};
108108
return await this.GetUserAsync(query, cancellationToken)
109109
.ConfigureAwait(false);
@@ -127,7 +127,56 @@ internal async Task<UserRecord> GetUserByEmailAsync(
127127
var query = new UserQuery()
128128
{
129129
Field = "email",
130-
Value = email,
130+
Value = new string[] { email },
131+
Description = "email: " + email,
132+
};
133+
return await this.GetUserAsync(query, cancellationToken)
134+
.ConfigureAwait(false);
135+
}
136+
137+
/// <summary>
138+
/// Gets the user data corresponding to the given provider user identifer.
139+
/// </summary>
140+
/// <param name="providerId">Identifier for the given provider, for example,
141+
/// "google.com" for the Google provider.</param>
142+
/// <param name="providerUid">The user identifier with the given provider.</param>
143+
/// <param name="cancellationToken">A cancellation token to monitor the asynchronous
144+
/// operation.</param>
145+
/// <returns>A record of user with the queried provider user identifier if one exists.</returns>
146+
internal async Task<UserRecord> GetUserByProviderUidAsync(
147+
string providerId, string providerUid, CancellationToken cancellationToken = default(CancellationToken))
148+
{
149+
if (string.IsNullOrEmpty(providerId))
150+
{
151+
throw new ArgumentException("providerId cannot be null or empty.");
152+
}
153+
154+
if (string.IsNullOrEmpty(providerUid))
155+
{
156+
throw new ArgumentException("providerUid cannot be null or empty.");
157+
}
158+
159+
if (providerId.Equals("phone"))
160+
{
161+
return await this.GetUserByPhoneNumberAsync(providerUid, cancellationToken);
162+
}
163+
164+
if (providerId.Equals("email"))
165+
{
166+
return await this.GetUserByEmailAsync(providerUid, cancellationToken);
167+
}
168+
169+
var federatedUserId = new Dictionary<string, object>()
170+
{
171+
{ "rawId", providerUid },
172+
{ "providerId", providerId },
173+
};
174+
175+
var query = new UserQuery()
176+
{
177+
Field = "federatedUserId",
178+
Value = new Dictionary<string, object>[] { federatedUserId },
179+
Description = "providerId: " + providerId + ", providerUid: " + providerUid,
131180
};
132181
return await this.GetUserAsync(query, cancellationToken)
133182
.ConfigureAwait(false);
@@ -151,8 +200,8 @@ internal async Task<UserRecord> GetUserByPhoneNumberAsync(
151200
var query = new UserQuery()
152201
{
153202
Field = "phoneNumber",
154-
Value = phoneNumber,
155-
Label = "phone number",
203+
Value = new string[] { phoneNumber },
204+
Description = "phone number: " + phoneNumber,
156205
};
157206
return await this.GetUserAsync(query, cancellationToken)
158207
.ConfigureAwait(false);
@@ -301,37 +350,23 @@ internal sealed class Args
301350
/// <summary>
302351
/// Represents a query that can be executed against the Firebase Auth service to retrieve user records.
303352
/// A query mainly consists of a <see cref="UserQuery.Field"/> and a <see cref="UserQuery.Value"/> (e.g.
304-
/// <c>Field = localId</c> and <c>Value = alice</c>). Additionally, a query may also specify a more
305-
/// human-readable <see cref="UserQuery.Label"/> for the field, which will appear on any error messages
353+
/// <c>Field = localId</c> and <c>Value = alice</c>). Additionally, a query also specifies a more
354+
/// human-readable <see cref="UserQuery.Description"/> for the key-value, which will appear on any error messages
306355
/// produced by the query.
307356
/// </summary>
308357
private class UserQuery
309358
{
310359
internal string Field { get; set; }
311360

312-
internal string Value { get; set; }
313-
314-
internal string Label { get; set; }
361+
internal object Value { get; set; }
315362

316-
internal string Description
317-
{
318-
get
319-
{
320-
var label = this.Label;
321-
if (string.IsNullOrEmpty(label))
322-
{
323-
label = this.Field;
324-
}
325-
326-
return $"{label}: {this.Value}";
327-
}
328-
}
363+
internal string Description { get; set; }
329364

330365
internal Dictionary<string, object> Build()
331366
{
332367
return new Dictionary<string, object>()
333368
{
334-
{ this.Field, new string[] { this.Value } },
369+
{ this.Field, this.Value },
335370
};
336371
}
337372
}

0 commit comments

Comments
 (0)