Skip to content

Combine email and username in MapIdentityApi #49981

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

Merged
merged 7 commits into from
Aug 11, 2023
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
9 changes: 9 additions & 0 deletions src/Identity/Core/src/DTO/ForgotPasswordRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.AspNetCore.Identity.DTO;

internal sealed class ForgotPasswordRequest
{
public required string Email { get; init; }
}
1 change: 0 additions & 1 deletion src/Identity/Core/src/DTO/InfoRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ namespace Microsoft.AspNetCore.Identity.DTO;

internal sealed class InfoRequest
{
public string? NewUsername { get; init; }
public string? NewEmail { get; init; }
public string? NewPassword { get; init; }
public string? OldPassword { get; init; }
Expand Down
2 changes: 1 addition & 1 deletion src/Identity/Core/src/DTO/InfoResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Identity.DTO;

internal sealed class InfoResponse
{
public required string Username { get; init; }
public required string Email { get; init; }
public required bool IsEmailConfirmed { get; init; }
public required IDictionary<string, string> Claims { get; init; }
}
2 changes: 1 addition & 1 deletion src/Identity/Core/src/DTO/LoginRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Identity.DTO;

internal sealed class LoginRequest
{
public required string Username { get; init; }
public required string Email { get; init; }
public required string Password { get; init; }
public string? TwoFactorCode { get; init; }
public string? TwoFactorRecoveryCode { get; init; }
Expand Down
3 changes: 1 addition & 2 deletions src/Identity/Core/src/DTO/RegisterRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ namespace Microsoft.AspNetCore.Identity.DTO;

internal sealed class RegisterRequest
{
public required string Username { get; init; }
public required string Password { get; init; }
public required string Email { get; init; }
public required string Password { get; init; }
}
4 changes: 2 additions & 2 deletions src/Identity/Core/src/DTO/ResetPasswordRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ namespace Microsoft.AspNetCore.Identity.DTO;
internal sealed class ResetPasswordRequest
{
public required string Email { get; init; }
public string? ResetCode { get; init; }
public string? NewPassword { get; init; }
public required string ResetCode { get; init; }
public required string NewPassword { get; init; }
}
275 changes: 111 additions & 164 deletions src/Identity/Core/src/IdentityApiEndpointRouteBuilderExtensions.cs

Large diffs are not rendered by default.

447 changes: 254 additions & 193 deletions src/Identity/test/Identity.FunctionalTests/MapIdentityApiTests.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,14 @@ namespace Microsoft.AspNetCore.Authentication.BearerToken;
internal sealed class BearerTokenHandler(IOptionsMonitor<BearerTokenOptions> optionsMonitor, ILoggerFactory loggerFactory, UrlEncoder urlEncoder)
: SignInAuthenticationHandler<BearerTokenOptions>(optionsMonitor, loggerFactory, urlEncoder)
{
private static readonly long OneSecondTicks = TimeSpan.FromSeconds(1).Ticks;

private static readonly AuthenticateResult FailedUnprotectingToken = AuthenticateResult.Fail("Unprotected token failed");
private static readonly AuthenticateResult TokenExpired = AuthenticateResult.Fail("Token expired");

private new BearerTokenEvents Events => (BearerTokenEvents)base.Events!;

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// Give application opportunity to find from a different location, adjust, or reject token
// Give application opportunity to find from a different location, adjust, or reject token.
var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options);

await Events.MessageReceivedAsync(messageReceivedContext);
Expand Down Expand Up @@ -66,12 +64,12 @@ protected override async Task HandleSignInAsync(ClaimsPrincipal user, Authentica
var utcNow = TimeProvider.GetUtcNow();

properties ??= new();
properties.ExpiresUtc ??= utcNow + Options.BearerTokenExpiration;
properties.ExpiresUtc = utcNow + Options.BearerTokenExpiration;

var response = new AccessTokenResponse
{
AccessToken = Options.BearerTokenProtector.Protect(CreateBearerTicket(user, properties)),
ExpiresInSeconds = CalculateExpiresInSeconds(utcNow, properties.ExpiresUtc),
ExpiresInSeconds = (long)Options.BearerTokenExpiration.TotalSeconds,
RefreshToken = Options.RefreshTokenProtector.Protect(CreateRefreshTicket(user, utcNow)),
};

Expand All @@ -92,24 +90,6 @@ protected override async Task HandleSignInAsync(ClaimsPrincipal user, Authentica
: null;
}

private long CalculateExpiresInSeconds(DateTimeOffset utcNow, DateTimeOffset? expiresUtc)
{
static DateTimeOffset FloorSeconds(DateTimeOffset dateTimeOffset)
=> new(dateTimeOffset.Ticks / OneSecondTicks * OneSecondTicks, dateTimeOffset.Offset);

// AuthenticationProperties floors ExpiresUtc. If this remains unchanged, we'll use BearerTokenExpiration directly
// to produce a consistent ExpiresInTotalSeconds values. If ExpiresUtc was overridden, we just calculate the
// the difference from utcNow and round even though this will likely result in unstable values.
var expiresTimeSpan = Options.BearerTokenExpiration;
var expectedExpiresUtc = FloorSeconds(utcNow + expiresTimeSpan);
return (long)(expiresUtc switch
{
DateTimeOffset d when d == expectedExpiresUtc => expiresTimeSpan.TotalSeconds,
DateTimeOffset d => (d - utcNow).TotalSeconds,
_ => expiresTimeSpan.TotalSeconds,
});
}

private AuthenticationTicket CreateBearerTicket(ClaimsPrincipal user, AuthenticationProperties properties)
=> new(user, properties, $"{Scheme.Name}:AccessToken");

Expand Down