Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions FirebaseAdmin/FirebaseAdmin.Tests/Auth/EmailActionRequestTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ public void NoEmail()
var handler = new MockMessageHandler() { Response = GenerateEmailLinkResponse };
var auth = this.CreateFirebaseAuth(handler);

Assert.ThrowsAsync<ArgumentException>(
async () => await auth.GenerateEmailVerificationLinkAsync(null));
Assert.ThrowsAsync<ArgumentException>(
async () => await auth.GenerateEmailVerificationLinkAsync(string.Empty));

Assert.ThrowsAsync<ArgumentException>(
async () => await auth.GeneratePasswordResetLinkAsync(null));
Assert.ThrowsAsync<ArgumentException>(
Expand All @@ -110,10 +115,79 @@ public void InvalidActionCodeSettings(ActionCodeSettings settings)
var auth = this.CreateFirebaseAuth(handler);
var email = "[email protected]";

Assert.ThrowsAsync<ArgumentException>(
async () => await auth.GenerateEmailVerificationLinkAsync(email, settings));

Assert.ThrowsAsync<ArgumentException>(
async () => await auth.GeneratePasswordResetLinkAsync(email, settings));
}

[Fact]
public async Task EmailVerificationLink()
{
var handler = new MockMessageHandler() { Response = GenerateEmailLinkResponse };
var auth = this.CreateFirebaseAuth(handler);

var link = await auth.GenerateEmailVerificationLinkAsync("[email protected]");

Assert.Equal("https://mock-oob-link.for.auth.tests", link);

var request = NewtonsoftJsonSerializer.Instance.Deserialize<Dictionary<string, object>>(
handler.LastRequestBody);
Assert.Equal(3, request.Count);
Assert.Equal("[email protected]", request["email"]);
Assert.Equal("VERIFY_EMAIL", request["requestType"]);
Assert.True((bool)request["returnOobLink"]);
this.AssertRequest(handler.Requests[0]);
}

[Fact]
public async Task EmailVerificationLinkWithSettings()
{
var handler = new MockMessageHandler() { Response = GenerateEmailLinkResponse };
var auth = this.CreateFirebaseAuth(handler);

var link = await auth.GenerateEmailVerificationLinkAsync(
"[email protected]", ActionCodeSettings);

Assert.Equal("https://mock-oob-link.for.auth.tests", link);

var request = NewtonsoftJsonSerializer.Instance.Deserialize<Dictionary<string, object>>(
handler.LastRequestBody);
Assert.Equal(10, request.Count);
Assert.Equal("[email protected]", request["email"]);
Assert.Equal("VERIFY_EMAIL", request["requestType"]);
Assert.True((bool)request["returnOobLink"]);

Assert.Equal(ActionCodeSettings.Url, request["continueUrl"]);
Assert.True((bool)request["canHandleCodeInApp"]);
Assert.Equal(ActionCodeSettings.DynamicLinkDomain, request["dynamicLinkDomain"]);
Assert.Equal(ActionCodeSettings.IosBundleId, request["iOSBundleId"]);
Assert.Equal(ActionCodeSettings.AndroidPackageName, request["androidPackageName"]);
Assert.Equal(
ActionCodeSettings.AndroidMinimumVersion, request["androidMinimumVersion"]);
Assert.True((bool)request["androidInstallApp"]);
this.AssertRequest(handler.Requests[0]);
}

[Fact]
public async Task EmailVerificationLinkUnexpectedResponse()
{
var handler = new MockMessageHandler() { Response = "{}" };
var auth = this.CreateFirebaseAuth(handler);

var exception = await Assert.ThrowsAsync<FirebaseAuthException>(
async () => await auth.GenerateEmailVerificationLinkAsync("[email protected]"));

Assert.Equal(ErrorCode.Unknown, exception.ErrorCode);
Assert.Equal(AuthErrorCode.UnexpectedResponse, exception.AuthErrorCode);
Assert.Equal(
$"Failed to generate email action link for: [email protected]",
exception.Message);
Assert.NotNull(exception.HttpResponse);
Assert.Null(exception.InnerException);
}

[Fact]
public async Task PasswordResetLink()
{
Expand Down
7 changes: 7 additions & 0 deletions FirebaseAdmin/FirebaseAdmin/Auth/EmailActionLinkRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace FirebaseAdmin.Auth
{
internal sealed class EmailActionLinkRequest
{
private const string VerifyEmail = "VERIFY_EMAIL";
private const string PasswordReset = "PASSWORD_RESET";

private EmailActionLinkRequest(string type, string email, ActionCodeSettings settings)
Expand Down Expand Up @@ -74,6 +75,12 @@ private EmailActionLinkRequest(string type, string email, ActionCodeSettings set
[JsonProperty("androidInstallApp")]
internal bool? AndroidInstallApp { get; }

internal static EmailActionLinkRequest EmailVerificationLinkRequest(
string email, ActionCodeSettings settings)
{
return new EmailActionLinkRequest(VerifyEmail, email, settings);
}

internal static EmailActionLinkRequest PasswordResetLinkRequest(
string email, ActionCodeSettings settings)
{
Expand Down
51 changes: 51 additions & 0 deletions FirebaseAdmin/FirebaseAdmin/Auth/FirebaseAuth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,57 @@ public PagedAsyncEnumerable<ExportedUserRecords, ExportedUserRecord> ListUsersAs
return userManager.ListUsers(options);
}

/// <summary>
/// Generates the out-of-band email action link for email verification flows for the specified
/// email address.
/// </summary>
/// <exception cref="FirebaseAuthException">If an error occurs while generating the link.</exception>
/// <param name="email">The email of the user to be verified.</param>
/// <returns>A task that completes with the email verification link.</returns>
public async Task<string> GenerateEmailVerificationLinkAsync(string email)
{
return await this.GenerateEmailVerificationLinkAsync(email, null)
.ConfigureAwait(false);
}

/// <summary>
/// Generates the out-of-band email action link for email verification flows for the specified
/// email address.
/// </summary>
/// <exception cref="FirebaseAuthException">If an error occurs while generating the link.</exception>
/// <param name="email">The email of the user to be verifed.</param>
/// <param name="settings">The action code settings object that defines whether
/// the link is to be handled by a mobile app and the additional state information to be
/// passed in the deep link.</param>
/// <returns>A task that completes with the email verification link.</returns>
public async Task<string> GenerateEmailVerificationLinkAsync(
string email, ActionCodeSettings settings)
{
return await this.GenerateEmailVerificationLinkAsync(email, settings, default(CancellationToken))
.ConfigureAwait(false);
}

/// <summary>
/// Generates the out-of-band email action link for email verification flows for the specified
/// email address.
/// </summary>
/// <exception cref="FirebaseAuthException">If an error occurs while generating the link.</exception>
/// <param name="email">The email of the user to be verified.</param>
/// <param name="settings">The action code settings object that defines whether
/// the link is to be handled by a mobile app and the additional state information to be
/// passed in the deep link.</param>
/// <param name="cancellationToken">A cancellation token to monitor the asynchronous
/// operation.</param>
/// <returns>A task that completes with the email verification reset link.</returns>
public async Task<string> GenerateEmailVerificationLinkAsync(
string email, ActionCodeSettings settings, CancellationToken cancellationToken)
{
var userManager = this.IfNotDeleted(() => this.userManager.Value);
var request = EmailActionLinkRequest.EmailVerificationLinkRequest(email, settings);
return await userManager.GenerateEmailActionLinkAsync(request, cancellationToken)
.ConfigureAwait(false);
}

/// <summary>
/// Generates the out-of-band email action link for password reset flows for the specified
/// email address.
Expand Down