mirror of
https://github.com/bitwarden/server.git
synced 2026-02-11 11:23:14 +08:00
* [PM-1188] add sso project to auth * [PM-1188] move sso api models to auth * [PM-1188] fix sso api model namespace & imports * [PM-1188] move core files to auth * [PM-1188] fix core sso namespace & models * [PM-1188] move sso repository files to auth * [PM-1188] fix sso repo files namespace & imports * [PM-1188] move sso sql files to auth folder * [PM-1188] move sso test files to auth folders * [PM-1188] fix sso tests namespace & imports * [PM-1188] move auth api files to auth folder * [PM-1188] fix auth api files namespace & imports * [PM-1188] move auth core files to auth folder * [PM-1188] fix auth core files namespace & imports * [PM-1188] move auth email templates to auth folder * [PM-1188] move auth email folder back into shared directory * [PM-1188] fix auth email names * [PM-1188] move auth core models to auth folder * [PM-1188] fix auth model namespace & imports * [PM-1188] add entire Identity project to auth codeowners * [PM-1188] fix auth orm files namespace & imports * [PM-1188] move auth orm files to auth folder * [PM-1188] move auth sql files to auth folder * [PM-1188] move auth tests to auth folder * [PM-1188] fix auth test files namespace & imports * [PM-1188] move emergency access api files to auth folder * [PM-1188] fix emergencyaccess api files namespace & imports * [PM-1188] move emergency access core files to auth folder * [PM-1188] fix emergency access core files namespace & imports * [PM-1188] move emergency access orm files to auth folder * [PM-1188] fix emergency access orm files namespace & imports * [PM-1188] move emergency access sql files to auth folder * [PM-1188] move emergencyaccess test files to auth folder * [PM-1188] fix emergency access test files namespace & imports * [PM-1188] move captcha files to auth folder * [PM-1188] fix captcha files namespace & imports * [PM-1188] move auth admin files into auth folder * [PM-1188] fix admin auth files namespace & imports - configure mvc to look in auth folders for views * [PM-1188] remove extra imports and formatting * [PM-1188] fix ef auth model imports * [PM-1188] fix DatabaseContextModelSnapshot paths * [PM-1188] fix grant import in ef * [PM-1188] update sqlproj * [PM-1188] move missed sqlproj files * [PM-1188] move auth ef models out of auth folder * [PM-1188] fix auth ef models namespace * [PM-1188] remove auth ef models unused imports * [PM-1188] fix imports for auth ef models * [PM-1188] fix more ef model imports * [PM-1188] fix file encodings
160 lines
6.5 KiB
C#
160 lines
6.5 KiB
C#
using System.Security.Claims;
|
|
using Bit.Core.Auth.Identity;
|
|
using Bit.Core.Auth.Repositories;
|
|
using Bit.Core.Context;
|
|
using Bit.Core.Entities;
|
|
using Bit.Core.IdentityServer;
|
|
using Bit.Core.Repositories;
|
|
using Bit.Core.Services;
|
|
using Bit.Core.Settings;
|
|
using IdentityModel;
|
|
using IdentityServer4.Extensions;
|
|
using IdentityServer4.Validation;
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
namespace Bit.Identity.IdentityServer;
|
|
|
|
public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenRequestValidationContext>,
|
|
ICustomTokenRequestValidator
|
|
{
|
|
private UserManager<User> _userManager;
|
|
private readonly ISsoConfigRepository _ssoConfigRepository;
|
|
|
|
public CustomTokenRequestValidator(
|
|
UserManager<User> userManager,
|
|
IDeviceRepository deviceRepository,
|
|
IDeviceService deviceService,
|
|
IUserService userService,
|
|
IEventService eventService,
|
|
IOrganizationDuoWebTokenProvider organizationDuoWebTokenProvider,
|
|
IOrganizationRepository organizationRepository,
|
|
IOrganizationUserRepository organizationUserRepository,
|
|
IApplicationCacheService applicationCacheService,
|
|
IMailService mailService,
|
|
ILogger<CustomTokenRequestValidator> logger,
|
|
ICurrentContext currentContext,
|
|
GlobalSettings globalSettings,
|
|
IPolicyRepository policyRepository,
|
|
ISsoConfigRepository ssoConfigRepository,
|
|
IUserRepository userRepository)
|
|
: base(userManager, deviceRepository, deviceService, userService, eventService,
|
|
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
|
|
applicationCacheService, mailService, logger, currentContext, globalSettings, policyRepository,
|
|
userRepository)
|
|
{
|
|
_userManager = userManager;
|
|
_ssoConfigRepository = ssoConfigRepository;
|
|
}
|
|
|
|
public async Task ValidateAsync(CustomTokenRequestValidationContext context)
|
|
{
|
|
string[] allowedGrantTypes = { "authorization_code", "client_credentials" };
|
|
if (!allowedGrantTypes.Contains(context.Result.ValidatedRequest.GrantType)
|
|
|| context.Result.ValidatedRequest.ClientId.StartsWith("organization")
|
|
|| context.Result.ValidatedRequest.ClientId.StartsWith("installation")
|
|
|| context.Result.ValidatedRequest.ClientId.StartsWith("internal")
|
|
|| context.Result.ValidatedRequest.Client.AllowedScopes.Contains(ApiScopes.ApiSecrets))
|
|
{
|
|
if (context.Result.ValidatedRequest.Client.Properties.TryGetValue("encryptedPayload", out var payload) &&
|
|
!string.IsNullOrWhiteSpace(payload))
|
|
{
|
|
context.Result.CustomResponse = new Dictionary<string, object> { { "encrypted_payload", payload } };
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
await ValidateAsync(context, context.Result.ValidatedRequest,
|
|
new CustomValidatorRequestContext { KnownDevice = true });
|
|
}
|
|
|
|
protected async override Task<bool> ValidateContextAsync(CustomTokenRequestValidationContext context,
|
|
CustomValidatorRequestContext validatorContext)
|
|
{
|
|
var email = context.Result.ValidatedRequest.Subject?.GetDisplayName()
|
|
?? context.Result.ValidatedRequest.ClientClaims?.FirstOrDefault(claim => claim.Type == JwtClaimTypes.Email)?.Value;
|
|
if (!string.IsNullOrWhiteSpace(email))
|
|
{
|
|
validatorContext.User = await _userManager.FindByEmailAsync(email);
|
|
}
|
|
return validatorContext.User != null;
|
|
}
|
|
|
|
protected override async Task SetSuccessResult(CustomTokenRequestValidationContext context, User user,
|
|
List<Claim> claims, Dictionary<string, object> customResponse)
|
|
{
|
|
context.Result.CustomResponse = customResponse;
|
|
if (claims?.Any() ?? false)
|
|
{
|
|
context.Result.ValidatedRequest.Client.AlwaysSendClientClaims = true;
|
|
context.Result.ValidatedRequest.Client.ClientClaimsPrefix = string.Empty;
|
|
foreach (var claim in claims)
|
|
{
|
|
context.Result.ValidatedRequest.ClientClaims.Add(claim);
|
|
}
|
|
}
|
|
|
|
if (context.Result.CustomResponse == null || user.MasterPassword != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// KeyConnector responses below
|
|
|
|
// Apikey login
|
|
if (context.Result.ValidatedRequest.GrantType == "client_credentials")
|
|
{
|
|
if (user.UsesKeyConnector)
|
|
{
|
|
// KeyConnectorUrl is configured in the CLI client, we just need to tell the client to use it
|
|
context.Result.CustomResponse["ApiUseKeyConnector"] = true;
|
|
context.Result.CustomResponse["ResetMasterPassword"] = false;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// SSO login
|
|
var organizationClaim = context.Result.ValidatedRequest.Subject?.FindFirst(c => c.Type == "organizationId");
|
|
if (organizationClaim?.Value != null)
|
|
{
|
|
var organizationId = new Guid(organizationClaim.Value);
|
|
|
|
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organizationId);
|
|
var ssoConfigData = ssoConfig.GetData();
|
|
|
|
if (ssoConfigData is { KeyConnectorEnabled: true } && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl))
|
|
{
|
|
context.Result.CustomResponse["KeyConnectorUrl"] = ssoConfigData.KeyConnectorUrl;
|
|
// Prevent clients redirecting to set-password
|
|
context.Result.CustomResponse["ResetMasterPassword"] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void SetTwoFactorResult(CustomTokenRequestValidationContext context,
|
|
Dictionary<string, object> customResponse)
|
|
{
|
|
context.Result.Error = "invalid_grant";
|
|
context.Result.ErrorDescription = "Two factor required.";
|
|
context.Result.IsError = true;
|
|
context.Result.CustomResponse = customResponse;
|
|
}
|
|
|
|
protected override void SetSsoResult(CustomTokenRequestValidationContext context,
|
|
Dictionary<string, object> customResponse)
|
|
{
|
|
context.Result.Error = "invalid_grant";
|
|
context.Result.ErrorDescription = "Single Sign on required.";
|
|
context.Result.IsError = true;
|
|
context.Result.CustomResponse = customResponse;
|
|
}
|
|
|
|
protected override void SetErrorResult(CustomTokenRequestValidationContext context,
|
|
Dictionary<string, object> customResponse)
|
|
{
|
|
context.Result.Error = "invalid_grant";
|
|
context.Result.IsError = true;
|
|
context.Result.CustomResponse = customResponse;
|
|
}
|
|
}
|