Browse Source

Merge pull request #25648 from abpframework/maliming/openiddict-shared-user-host-lookup

Resolve shared users from Host in password and token-exchange grants
pull/25649/head
İsmail ÇAĞDAŞ 2 weeks ago
committed by GitHub
parent
commit
f9e1481659
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 129
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs
  2. 106
      modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs

129
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.Password.cs

@ -57,24 +57,30 @@ public partial class TokenController
if (await externalLoginProvider.TryAuthenticateAsync(request.Username, request.Password))
{
user = await UserManager.FindByNameAsync(request.Username);
user = await UserManager.FindSharedUserByNameAsync(request.Username);
if (user == null)
{
user = await externalLoginProvider.CreateUserAsync(request.Username, externalLoginProviderInfo.Name);
}
else
{
await externalLoginProvider.UpdateUserAsync(user, externalLoginProviderInfo.Name);
using (CurrentTenant.Change(user.TenantId))
{
await externalLoginProvider.UpdateUserAsync(user, externalLoginProviderInfo.Name);
}
}
return await SetSuccessResultAsync(request, user);
using (CurrentTenant.Change(user.TenantId))
{
return await SetSuccessResultAsync(request, user);
}
}
}
}
await IdentityOptions.SetAsync();
user = await UserManager.FindByNameAsync(request.Username);
user = await UserManager.FindSharedUserByNameAsync(request.Username);
if (user == null)
{
Logger.LogInformation("No user found matching username: {username}", request.Username);
@ -96,77 +102,82 @@ public partial class TokenController
return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
var result = await SignInManager.CheckPasswordSignInAsync(user, request.Password, true);
if (!result.Succeeded)
using (CurrentTenant.Change(user.TenantId))
{
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
await IdentityOptions.SetAsync();
var result = await SignInManager.CheckPasswordSignInAsync(user, request.Password, true);
if (!result.Succeeded)
{
Identity = OpenIddictSecurityLogIdentityConsts.OpenIddict,
Action = result.ToIdentitySecurityLogAction(),
UserName = request.Username,
ClientId = request.ClientId
});
await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
{
Identity = OpenIddictSecurityLogIdentityConsts.OpenIddict,
Action = result.ToIdentitySecurityLogAction(),
UserName = request.Username,
ClientId = request.ClientId
});
var errorCode = OpenIddictConstants.Errors.InvalidGrant;
string errorDescription;
var errorCode = OpenIddictConstants.Errors.InvalidGrant;
string errorDescription;
if (result.IsLockedOut)
{
Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", request.Username);
errorCode = AbpOpenIddictErrors.AccountLocked;
errorDescription = "The user account has been locked out due to invalid login attempts. Please wait a while and try again.";
}
else if (result.IsNotAllowed)
{
if (!await UserManager.CheckPasswordAsync(user, request.Password))
if (result.IsLockedOut)
{
Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", request.Username);
errorDescription = "Invalid username or password!";
Logger.LogInformation("Authentication failed for username: {username}, reason: locked out", request.Username);
errorCode = AbpOpenIddictErrors.AccountLocked;
errorDescription = "The user account has been locked out due to invalid login attempts. Please wait a while and try again.";
}
else
else if (result.IsNotAllowed)
{
Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", request.Username);
if (user.ShouldChangePasswordOnNextLogin)
if (!await UserManager.CheckPasswordAsync(user, request.Password))
{
return await HandleShouldChangePasswordOnNextLoginAsync(request, user, request.Password);
Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", request.Username);
errorDescription = "Invalid username or password!";
}
if (await UserManager.ShouldPeriodicallyChangePasswordAsync(user))
else
{
return await HandlePeriodicallyChangePasswordAsync(request, user, request.Password);
}
Logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", request.Username);
if (user.IsActive)
{
return await HandleConfirmUserAsync(request, user);
}
if (user.ShouldChangePasswordOnNextLogin)
{
return await HandleShouldChangePasswordOnNextLoginAsync(request, user, request.Password);
}
if (await UserManager.ShouldPeriodicallyChangePasswordAsync(user))
{
return await HandlePeriodicallyChangePasswordAsync(request, user, request.Password);
}
errorCode = AbpOpenIddictErrors.AccountInactive;
errorDescription = "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number.";
if (user.IsActive)
{
return await HandleConfirmUserAsync(request, user);
}
errorCode = AbpOpenIddictErrors.AccountInactive;
errorDescription = "You are not allowed to login! Your account is inactive or needs to confirm your email/phone number.";
}
}
}
else
{
Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", request.Username);
errorDescription = "Invalid username or password!";
else
{
Logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", request.Username);
errorDescription = "Invalid username or password!";
}
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = errorCode,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = errorDescription
});
return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
var properties = new AuthenticationProperties(new Dictionary<string, string>
if (await IsTfaEnabledAsync(user))
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = errorCode,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = errorDescription
});
return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
return await HandleTwoFactorLoginAsync(request, user);
}
if (await IsTfaEnabledAsync(user))
{
return await HandleTwoFactorLoginAsync(request, user);
return await SetSuccessResultAsync(request, user);
}
return await SetSuccessResultAsync(request, user);
}
}
}
@ -178,13 +189,13 @@ public partial class TokenController
return;
}
var userByUsername = await UserManager.FindByNameAsync(request.Username);
var userByUsername = await UserManager.FindSharedUserByNameAsync(request.Username);
if (userByUsername != null)
{
return;
}
var userByEmail = await UserManager.FindByEmailAsync(request.Username);
var userByEmail = await UserManager.FindSharedUserByEmailAsync(request.Username);
if (userByEmail == null)
{
return;

106
modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.TokenExchange.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Security.Principal;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
@ -27,66 +28,69 @@ public partial class TokenController
// If available, retrieve the claims principal stored in the actor token.
var actor = result.Properties?.GetParameter<ClaimsPrincipal>(OpenIddictServerAspNetCoreConstants.Properties.ActorTokenPrincipal);
// Retrieve the user profile corresponding to the subject token.
var user = await UserManager.FindByIdAsync(result.Principal!.GetClaim(OpenIddictConstants.Claims.Subject)!);
if (user is null)
using (CurrentTenant.Change(result.Principal!.FindTenantId()))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid."
}));
}
// Retrieve the user profile corresponding to the subject token.
var user = await UserManager.FindByIdAsync(result.Principal!.GetClaim(OpenIddictConstants.Claims.Subject)!);
if (user is null)
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid."
}));
}
// Ensure the user is still allowed to sign in.
if (!await PreSignInCheckAsync(user))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string?>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in."
}));
}
// Ensure the user is still allowed to sign in.
if (!await PreSignInCheckAsync(user))
{
return Forbid(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: new AuthenticationProperties(new Dictionary<string, string?>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant,
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in."
}));
}
// Note: whether the identity represents a delegated or impersonated access (or any other
// model) is entirely up to the implementer: to support all scenarios, OpenIddict doesn't
// enforce any specific constraint on the identity used for the sign-in operation and only
// requires that the standard "act" and "may_act" claims be valid JSON objects if present.
// Note: whether the identity represents a delegated or impersonated access (or any other
// model) is entirely up to the implementer: to support all scenarios, OpenIddict doesn't
// enforce any specific constraint on the identity used for the sign-in operation and only
// requires that the standard "act" and "may_act" claims be valid JSON objects if present.
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
// Create a new ClaimsPrincipal containing the claims that
// will be used to create an id_token, a token or a code.
var principal = await SignInManager.CreateUserPrincipalAsync(user);
// Create a new ClaimsPrincipal containing the claims that
// will be used to create an id_token, a token or a code.
var principal = await SignInManager.CreateUserPrincipalAsync(user);
// Note: IdentityModel doesn't support serializing ClaimsIdentity.Actor to the
// standard "act" claim yet, which requires adding the "act" claim manually.
//
// For more information, see
// https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3219.
if (!string.IsNullOrEmpty(actor?.GetClaim(OpenIddictConstants.Claims.Subject)) &&
!string.Equals(principal.GetClaim(OpenIddictConstants.Claims.Subject), actor.GetClaim(OpenIddictConstants.Claims.Subject), StringComparison.Ordinal))
{
principal.SetClaim(OpenIddictConstants.Claims.Actor, new JsonObject
// Note: IdentityModel doesn't support serializing ClaimsIdentity.Actor to the
// standard "act" claim yet, which requires adding the "act" claim manually.
//
// For more information, see
// https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/3219.
if (!string.IsNullOrEmpty(actor?.GetClaim(OpenIddictConstants.Claims.Subject)) &&
!string.Equals(principal.GetClaim(OpenIddictConstants.Claims.Subject), actor.GetClaim(OpenIddictConstants.Claims.Subject), StringComparison.Ordinal))
{
[OpenIddictConstants.Claims.Subject] = actor.GetClaim(OpenIddictConstants.Claims.Subject)
});
}
principal.SetClaim(OpenIddictConstants.Claims.Actor, new JsonObject
{
[OpenIddictConstants.Claims.Subject] = actor.GetClaim(OpenIddictConstants.Claims.Subject)
});
}
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
principal.SetScopes(request.GetScopes());
principal.SetResources(await GetResourcesAsync(request.GetScopes()));
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
principal.SetScopes(request.GetScopes());
principal.SetResources(await GetResourcesAsync(request.GetScopes()));
await OpenIddictClaimsPrincipalManager.HandleAsync(request, principal);
await OpenIddictClaimsPrincipalManager.HandleAsync(request, principal);
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
}
}

Loading…
Cancel
Save