Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor email confirmation handling to delegate to the client #59648

Closed
Closed
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
57 changes: 48 additions & 9 deletions src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers.Text;

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: macOS x64)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: macOS arm64)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux ARM64)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux ARM)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux x64)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl x64)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl ARM64)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl ARM)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: macOS)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: Ubuntu x64)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: macOS)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: Ubuntu x64)

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)

Check failure on line 4 in src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs#L4

src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs(4,1): error IDE0005: (NETCORE_ENGINEERING_TELEMETRY=Build) Using directive is unnecessary. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005)
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Web;
using Microsoft.AspNetCore.Authentication.BearerToken;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -394,26 +396,63 @@
throw new NotSupportedException("No email confirmation endpoint was registered!");
}

var clientOptions = userManager.Options.Client;

var code = isChange
? await userManager.GenerateChangeEmailTokenAsync(user, email)
: await userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));

var userId = await userManager.GetUserIdAsync(user);
var routeValues = new RouteValueDictionary()
{
["userId"] = userId,
["code"] = code,
};

if (isChange)
string confirmEmailUrl;

// If ClientOptions are configured, use them to build the URL
if (clientOptions is not null)
{
if (string.IsNullOrEmpty(clientOptions.BaseUrl)
|| string.IsNullOrEmpty(clientOptions.EmailconfirmationRoute))
{
throw new InvalidOperationException("Client options are not correctly configured.");
}

var uriBuilder = new UriBuilder(clientOptions.BaseUrl)
{
Path = clientOptions.EmailconfirmationRoute
};

var queryParams = HttpUtility.ParseQueryString(uriBuilder.Query);

queryParams["userId"] = userId;
queryParams["code"] = code;

// This is validated by the /confirmEmail endpoint on change.
routeValues.Add("changedEmail", email);
if (isChange)
{
queryParams["changedEmail"] = HttpUtility.UrlEncode(email);
}

uriBuilder.Query = queryParams.ToString();

confirmEmailUrl = uriBuilder.ToString();
}
else
{
var routeValues = new RouteValueDictionary()
{
["userId"] = userId,
["code"] = code,
};

var confirmEmailUrl = linkGenerator.GetUriByName(context, confirmEmailEndpointName, routeValues)
?? throw new NotSupportedException($"Could not find endpoint named '{confirmEmailEndpointName}'.");
if (isChange)
{
// This is validated by the /confirmEmail endpoint on change.
routeValues.Add("changedEmail", email);
}

confirmEmailUrl = linkGenerator.GetUriByName(context, confirmEmailEndpointName, routeValues)
?? throw new NotSupportedException($"Could not find endpoint named '{confirmEmailEndpointName}'.");
}

await emailSender.SendConfirmationLinkAsync(user, email, HtmlEncoder.Default.Encode(confirmEmailUrl));
}
Expand Down
27 changes: 27 additions & 0 deletions src/Identity/Extensions.Core/src/ClientOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;

namespace Microsoft.AspNetCore.Identity;

/// <summary>
/// Options for configuring the client.
/// </summary>
public class ClientOptions
{
/// <summary>
/// Gets or sets the base URL for the client.
/// </summary>
public string? BaseUrl { get; set; }

/// <summary>
/// Gets or sets the email confirmation route for the client.
/// </summary>
public string? EmailconfirmationRoute { get; set; }

/// <summary>
/// Gets or sets the developer-defined add-ons for the client.
/// </summary>
public IDictionary<string, object>? DeveloperDefinedAddOns { get; set; }
}
8 changes: 8 additions & 0 deletions src/Identity/Extensions.Core/src/IdentityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,12 @@ public class IdentityOptions
/// The <see cref="StoreOptions"/> for the identity system.
/// </value>
public StoreOptions Stores { get; set; } = new StoreOptions();

/// <summary>
/// Gets or sets the <see cref="ClientOptions"/> for the identity system.
/// </summary>
/// <value>
/// The <see cref="ClientOptions"/> for the identity system.
/// </value>
public ClientOptions? Client { get; set; }
}
10 changes: 10 additions & 0 deletions src/Identity/Extensions.Core/src/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.UserIdClaimType.get -> strin
Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.UserIdClaimType.set -> void
Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.UserNameClaimType.get -> string!
Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.UserNameClaimType.set -> void
Microsoft.AspNetCore.Identity.ClientOptions
Microsoft.AspNetCore.Identity.ClientOptions.ClientOptions() -> void
Microsoft.AspNetCore.Identity.ClientOptions.BaseUrl.get -> string?
Microsoft.AspNetCore.Identity.ClientOptions.BaseUrl.set -> void
Microsoft.AspNetCore.Identity.ClientOptions.EmailconfirmationRoute.get -> string?
Microsoft.AspNetCore.Identity.ClientOptions.EmailconfirmationRoute.set -> void
Microsoft.AspNetCore.Identity.ClientOptions.DeveloperDefinedAddOns.get -> System.Collections.Generic.IDictionary<string!, object!>?
Microsoft.AspNetCore.Identity.ClientOptions.DeveloperDefinedAddOns.set -> void
Microsoft.AspNetCore.Identity.DefaultPersonalDataProtector
Microsoft.AspNetCore.Identity.DefaultPersonalDataProtector.DefaultPersonalDataProtector(Microsoft.AspNetCore.Identity.ILookupProtectorKeyRing! keyRing, Microsoft.AspNetCore.Identity.ILookupProtector! protector) -> void
Microsoft.AspNetCore.Identity.DefaultUserConfirmation<TUser>
Expand Down Expand Up @@ -53,6 +61,8 @@ Microsoft.AspNetCore.Identity.IdentityOptions.Tokens.get -> Microsoft.AspNetCore
Microsoft.AspNetCore.Identity.IdentityOptions.Tokens.set -> void
Microsoft.AspNetCore.Identity.IdentityOptions.User.get -> Microsoft.AspNetCore.Identity.UserOptions!
Microsoft.AspNetCore.Identity.IdentityOptions.User.set -> void
Microsoft.AspNetCore.Identity.IdentityOptions.Client.get -> Microsoft.AspNetCore.Identity.ClientOptions?
Microsoft.AspNetCore.Identity.IdentityOptions.Client.set -> void
Microsoft.AspNetCore.Identity.IdentityResult
Microsoft.AspNetCore.Identity.IdentityResult.Errors.get -> System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Identity.IdentityError!>!
Microsoft.AspNetCore.Identity.IdentityResult.IdentityResult() -> void
Expand Down
Loading