diff --git a/src/Api/Controllers/AuthController.cs b/src/Api/Controllers/AuthController.cs index cb1ac05a42..b5e33da6d7 100644 --- a/src/Api/Controllers/AuthController.cs +++ b/src/Api/Controllers/AuthController.cs @@ -1,11 +1,7 @@ using System; -using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Bit.Core.Identity; -using Bit.Core.Models.Api; using Microsoft.AspNetCore.Authorization; using Bit.Core.Exceptions; -using Bit.Core.Services; namespace Bit.Api.Controllers { @@ -13,54 +9,12 @@ namespace Bit.Api.Controllers [Route("auth")] public class AuthController : Controller { - private readonly JwtBearerSignInManager _signInManager; - private readonly IUserService _userService; - - public AuthController( - JwtBearerSignInManager signInManager, - IUserService userService) - { - _signInManager = signInManager; - _userService = userService; - } - [HttpPost("token")] [AllowAnonymous] - public async Task PostToken([FromBody]AuthTokenRequestModel model) + public IActionResult PostToken() { - var result = await _signInManager.PasswordSignInAsync(model.Email.ToLower(), model.MasterPasswordHash, - model.Device?.ToDevice()); - if(result == JwtBearerSignInResult.Success) - { - return new AuthTokenResponseModel(result.Token, result.User); - } - else if(result == JwtBearerSignInResult.TwoFactorRequired) - { - return new AuthTokenResponseModel(result.Token, null); - } - - await Task.Delay(2000); - throw new BadRequestException("Username or password is incorrect. Try again."); - } - - [HttpPost("token/two-factor")] - [Authorize("TwoFactor")] - public async Task PostTokenTwoFactor([FromBody]AuthTokenTwoFactorRequestModel model) - { - var user = await _userService.GetUserByPrincipalAsync(User); - if(user == null) - { - throw new UnauthorizedAccessException(); - } - - var result = await _signInManager.TwoFactorSignInAsync(user, model.Provider, model.Code, model.Device?.ToDevice()); - if(result == JwtBearerSignInResult.Success) - { - return new AuthTokenResponseModel(result.Token, result.User); - } - - await Task.Delay(2000); - throw new BadRequestException("Code is not correct. Try again."); + throw new BadRequestException("You are using an outdated version of bitwarden that is no longer supported. " + + "Please update your app first and try again."); } } } diff --git a/src/Api/Startup.cs b/src/Api/Startup.cs index a0389dc595..c156a9cde3 100644 --- a/src/Api/Startup.cs +++ b/src/Api/Startup.cs @@ -1,12 +1,10 @@ using System; using System.Security.Claims; -using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Bit.Api.Utilities; using Bit.Core; @@ -20,6 +18,7 @@ using Bit.Api.Middleware; using Serilog.Events; using Stripe; using Bit.Core.Utilities; +using IdentityModel; namespace Bit.Api { @@ -73,21 +72,13 @@ namespace Bit.Api // Identity services.AddCustomIdentityServices(globalSettings); - var jwtIdentityOptions = provider.GetRequiredService>().Value; services.AddAuthorization(config => { config.AddPolicy("Application", policy => { - policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme, "Bearer2", "Bearer3"); + policy.AddAuthenticationSchemes("Bearer2", "Bearer3"); policy.RequireAuthenticatedUser(); - policy.RequireClaim(ClaimTypes.AuthenticationMethod, jwtIdentityOptions.AuthenticationMethod); - }); - - config.AddPolicy("TwoFactor", policy => - { - policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme, "Bearer2", "Bearer3"); - policy.RequireAuthenticatedUser(); - policy.RequireClaim(ClaimTypes.AuthenticationMethod, jwtIdentityOptions.TwoFactorAuthenticationMethod); + policy.RequireClaim(JwtClaimTypes.AuthenticationMethod, "Application"); }); }); @@ -166,9 +157,6 @@ namespace Bit.Api app.UseIdentityServerAuthentication( GetIdentityOptions(env, IdentityServerAuthority(env, "api", "4000"), "2")); - // Add Jwt authentication to the request pipeline. - app.UseJwtBearerIdentity(); - // Add current context app.UseMiddleware(); @@ -208,7 +196,7 @@ namespace Bit.Api else { return $"http://localhost:{port}"; - //return $"http://192.168.1.8:{port}"; // Desktop external + //return $"http://192.168.1.6:{port}"; // Desktop external } } } diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 0ed515e975..d77118037a 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -38,6 +38,7 @@ + diff --git a/src/Core/Identity/AuthenticatorTokenProvider.cs b/src/Core/Identity/AuthenticatorTokenProvider.cs index 2062c5041f..066aec8999 100644 --- a/src/Core/Identity/AuthenticatorTokenProvider.cs +++ b/src/Core/Identity/AuthenticatorTokenProvider.cs @@ -15,7 +15,7 @@ namespace Bit.Core.Identity && user.TwoFactorProvider.HasValue && user.TwoFactorProvider.Value == TwoFactorProviderType.Authenticator && !string.IsNullOrWhiteSpace(user.AuthenticatorKey); - + return Task.FromResult(canGenerate); } diff --git a/src/Core/Identity/JwtBearerAppBuilderExtensions.cs b/src/Core/Identity/JwtBearerAppBuilderExtensions.cs deleted file mode 100644 index b200f98678..0000000000 --- a/src/Core/Identity/JwtBearerAppBuilderExtensions.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Microsoft.AspNetCore.Authentication.JwtBearer; - -namespace Bit.Core.Identity -{ - public static class JwtBearerAppBuilderExtensions - { - public static IApplicationBuilder UseJwtBearerIdentity(this IApplicationBuilder app) - { - if(app == null) - { - throw new ArgumentNullException(nameof(app)); - } - - var marker = app.ApplicationServices.GetService(); - if(marker == null) - { - throw new InvalidOperationException("Must Call AddJwtBearerIdentity"); - } - - var jwtOptions = app.ApplicationServices.GetRequiredService>().Value; - var options = BuildJwtBearerOptions(jwtOptions); - app.UseJwtBearerAuthentication(options); - - return app; - } - - public static JwtBearerOptions BuildJwtBearerOptions(JwtBearerIdentityOptions jwtOptions) - { - var options = new JwtBearerOptions(); - - // Basic settings - signing key to validate with, audience and issuer. - options.TokenValidationParameters.IssuerSigningKey = jwtOptions.SigningCredentials.Key; - options.TokenValidationParameters.ValidAudience = jwtOptions.Audience; - options.TokenValidationParameters.ValidIssuer = jwtOptions.Issuer; - - options.TokenValidationParameters.RequireExpirationTime = true; - options.TokenValidationParameters.RequireSignedTokens = false; - - // When receiving a token, check that we've signed it. - options.TokenValidationParameters.RequireSignedTokens = false; - - //// When receiving a token, check that it is still valid. - options.TokenValidationParameters.ValidateLifetime = true; - - // This defines the maximum allowable clock skew - i.e. provides a tolerance on the token expiry time - // when validating the lifetime. As we're creating the tokens locally and validating them on the same - // machines which should have synchronised time, this can be set to zero. Where external tokens are - // used, some leeway here could be useful. - options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(0); - - options.Events = new JwtBearerEvents - { - OnTokenValidated = JwtBearerEventImplementations.ValidatedTokenAsync, - OnAuthenticationFailed = JwtBearerEventImplementations.AuthenticationFailedAsync, - OnMessageReceived = JwtBearerEventImplementations.MessageReceivedAsync - }; - - return options; - } - } -} diff --git a/src/Core/Identity/JwtBearerEventImplementations.cs b/src/Core/Identity/JwtBearerEventImplementations.cs deleted file mode 100644 index a84a9529d6..0000000000 --- a/src/Core/Identity/JwtBearerEventImplementations.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http.Authentication; -using Microsoft.IdentityModel.Tokens; -using Bit.Core.Services; - -namespace Bit.Core.Identity -{ - public static class JwtBearerEventImplementations - { - public async static Task ValidatedTokenAsync(TokenValidatedContext context) - { - if(context.HttpContext.RequestServices == null) - { - throw new InvalidOperationException("RequestServices is null"); - } - - var userService = context.HttpContext.RequestServices.GetRequiredService(); - var signInManager = context.HttpContext.RequestServices.GetRequiredService(); - - var userId = userService.GetProperUserId(context.Ticket.Principal); - var user = await userService.GetUserByIdAsync(userId.Value); - - // validate security token - if(!await signInManager.ValidateSecurityStampAsync(user, context.Ticket.Principal)) - { - throw new SecurityTokenValidationException("Bad security stamp."); - } - } - - public static Task AuthenticationFailedAsync(AuthenticationFailedContext context) - { - if(!context.HttpContext.User.Identity.IsAuthenticated) - { - context.State = EventResultState.HandledResponse; - context.Ticket = new AuthenticationTicket(context.HttpContext.User, new AuthenticationProperties(), - context.Options.AuthenticationScheme); - } - - return Task.FromResult(0); - } - - public static Task MessageReceivedAsync(MessageReceivedContext context) - { - if(!context.Request.Headers.ContainsKey("Authorization")) - { - context.Token = context.Request.Query["access_token"]; - } - - return Task.FromResult(0); - } - } -} diff --git a/src/Core/Identity/JwtBearerIdentityOptions.cs b/src/Core/Identity/JwtBearerIdentityOptions.cs deleted file mode 100644 index bb6430c19b..0000000000 --- a/src/Core/Identity/JwtBearerIdentityOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Microsoft.IdentityModel.Tokens; - -namespace Bit.Core.Identity -{ - public class JwtBearerIdentityOptions - { - public string Audience { get; set; } - public string Issuer { get; set; } - public SigningCredentials SigningCredentials { get; set; } - public TimeSpan? TokenLifetime { get; set; } - public TimeSpan? TwoFactorTokenLifetime { get; set; } - public string AuthenticationMethod { get; set; } = "Application"; - public string TwoFactorAuthenticationMethod { get; set; } = "TwoFactor"; - } -} diff --git a/src/Core/Identity/JwtBearerIdentityServiceCollectionExtensions.cs b/src/Core/Identity/JwtBearerIdentityServiceCollectionExtensions.cs deleted file mode 100644 index ee67ac2d13..0000000000 --- a/src/Core/Identity/JwtBearerIdentityServiceCollectionExtensions.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Bit.Core.Models.Table; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; - -namespace Bit.Core.Identity -{ - public static class JwtBearerIdentityServiceCollectionExtensions - { - public static IdentityBuilder AddJwtBearerIdentity( - this IServiceCollection services) - { - return services.AddJwtBearerIdentity(setupAction: null, jwtBearerSetupAction: null); - } - - public static IdentityBuilder AddJwtBearerIdentity( - this IServiceCollection services, - Action setupAction, - Action jwtBearerSetupAction) - { - // Services used by identity - services.AddOptions(); - services.AddAuthentication(); - - // Hosting doesn't add IHttpContextAccessor by default - services.TryAddSingleton(); - // Identity services - services.TryAddSingleton(); - services.TryAddScoped, UserValidator>(); - services.TryAddScoped, PasswordValidator>(); - services.TryAddScoped, PasswordHasher>(); - services.TryAddScoped(); - services.TryAddScoped, RoleValidator>(); - // No interface for the error describer so we can add errors without rev'ing the interface - services.TryAddScoped(); - services.TryAddScoped>(); - services.TryAddScoped, UserClaimsPrincipalFactory>(); - services.TryAddScoped, UserManager>(); - services.TryAddScoped(); - services.TryAddScoped, RoleManager>(); - - if(setupAction != null) - { - services.Configure(setupAction); - } - - if(jwtBearerSetupAction != null) - { - services.Configure(jwtBearerSetupAction); - } - - return new IdentityBuilder(typeof(User), typeof(Role), services); - } - } -} diff --git a/src/Core/Identity/JwtBearerSignInManager.cs b/src/Core/Identity/JwtBearerSignInManager.cs deleted file mode 100644 index 1e400cfb5a..0000000000 --- a/src/Core/Identity/JwtBearerSignInManager.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Bit.Core.Models.Table; -using Microsoft.AspNetCore.Builder; -using Microsoft.IdentityModel.Tokens; -using Bit.Core.Repositories; -using Bit.Core.Services; - -namespace Bit.Core.Identity -{ - public class JwtBearerSignInManager - { - private readonly IDeviceRepository _deviceRepository; - private readonly IDeviceService _deviceService; - - public JwtBearerSignInManager( - UserManager userManager, - IHttpContextAccessor contextAccessor, - IUserClaimsPrincipalFactory claimsFactory, - IOptions optionsAccessor, - IOptions jwtIdentityOptionsAccessor, - IOptions jwtOptionsAccessor, - ILogger logger, - IDeviceRepository deviceRepository, - IDeviceService deviceService) - { - UserManager = userManager; - Context = contextAccessor.HttpContext; - ClaimsFactory = claimsFactory; - IdentityOptions = optionsAccessor?.Value ?? new IdentityOptions(); - JwtIdentityOptions = jwtIdentityOptionsAccessor?.Value ?? new JwtBearerIdentityOptions(); - JwtBearerOptions = jwtOptionsAccessor?.Value ?? new JwtBearerOptions(); - _deviceRepository = deviceRepository; - _deviceService = deviceService; - } - - internal UserManager UserManager { get; set; } - internal HttpContext Context { get; set; } - internal IUserClaimsPrincipalFactory ClaimsFactory { get; set; } - internal IdentityOptions IdentityOptions { get; set; } - internal JwtBearerIdentityOptions JwtIdentityOptions { get; set; } - internal JwtBearerOptions JwtBearerOptions { get; set; } - - public async Task CreateUserPrincipalAsync(User user) => await ClaimsFactory.CreateAsync(user); - - public Task ValidateSecurityStampAsync(User user, ClaimsPrincipal principal) - { - if(user != null && UserManager.SupportsUserSecurityStamp) - { - var securityStamp = principal.FindFirstValue(IdentityOptions.ClaimsIdentity.SecurityStampClaimType); - if(securityStamp == user.SecurityStamp) - { - return Task.FromResult(true); - } - } - - return Task.FromResult(false); - } - - public async Task PasswordSignInAsync(User user, string password, Device device = null) - { - if(user == null) - { - throw new ArgumentNullException(nameof(user)); - } - - if(await UserManager.CheckPasswordAsync(user, password)) - { - var result = await SignInOrTwoFactorAsync(user); - if(result.Succeeded && device != null) - { - var existingDevice = await _deviceRepository.GetByIdentifierAsync(device.Identifier, user.Id); - if(existingDevice == null) - { - device.UserId = user.Id; - await _deviceService.SaveAsync(device); - } - } - - return result; - } - - return JwtBearerSignInResult.Failed; - } - - public async Task PasswordSignInAsync(string userName, string password, Device device = null) - { - var user = await UserManager.FindByNameAsync(userName); - if(user == null) - { - return JwtBearerSignInResult.Failed; - } - - return await PasswordSignInAsync(user, password, device); - } - - public async Task TwoFactorSignInAsync(User user, string provider, string code, Device device = null) - { - if(user == null) - { - return JwtBearerSignInResult.Failed; - } - - if(await UserManager.VerifyTwoFactorTokenAsync(user, provider, code)) - { - var token = await SignInAsync(user, false); - - var success = JwtBearerSignInResult.Success; - success.Token = token; - success.User = user; - - if(device != null) - { - var existingDevice = await _deviceRepository.GetByIdentifierAsync(device.Identifier, user.Id); - if(existingDevice == null) - { - device.UserId = user.Id; - await _deviceService.SaveAsync(device); - } - } - - return success; - } - - return JwtBearerSignInResult.Failed; - } - - private async Task SignInAsync(User user, bool twoFactor) - { - var handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler(); - - DateTime? tokenExpiration = null; - var userPrincipal = await CreateUserPrincipalAsync(user); - if(twoFactor) - { - userPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.AuthenticationMethod, JwtIdentityOptions.TwoFactorAuthenticationMethod)); - if(JwtIdentityOptions.TwoFactorTokenLifetime.HasValue) - { - tokenExpiration = DateTime.UtcNow.Add(JwtIdentityOptions.TwoFactorTokenLifetime.Value); - } - } - else - { - userPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.AuthenticationMethod, JwtIdentityOptions.AuthenticationMethod)); - if(JwtIdentityOptions.TokenLifetime.HasValue) - { - tokenExpiration = DateTime.UtcNow.Add(JwtIdentityOptions.TokenLifetime.Value); - } - } - - var descriptor = new SecurityTokenDescriptor - { - Issuer = JwtIdentityOptions.Issuer, - SigningCredentials = JwtIdentityOptions.SigningCredentials, - Audience = JwtIdentityOptions.Audience, - Subject = userPrincipal.Identities.First(), - Expires = tokenExpiration - }; - - var securityToken = handler.CreateToken(descriptor); - - return handler.WriteToken(securityToken); - } - - private async Task SignInOrTwoFactorAsync(User user) - { - if(UserManager.SupportsUserTwoFactor && - await UserManager.GetTwoFactorEnabledAsync(user) && - (await UserManager.GetValidTwoFactorProvidersAsync(user)).Count > 0) - { - var twoFactorToken = await SignInAsync(user, true); - - var twoFactorResult = JwtBearerSignInResult.TwoFactorRequired; - twoFactorResult.Token = twoFactorToken; - twoFactorResult.User = user; - - return twoFactorResult; - } - - var token = await SignInAsync(user, false); - - var result = JwtBearerSignInResult.Success; - result.Token = token; - result.User = user; - - return result; - } - } -} diff --git a/src/Core/Identity/JwtBearerSignInResult.cs b/src/Core/Identity/JwtBearerSignInResult.cs deleted file mode 100644 index a72f4878e5..0000000000 --- a/src/Core/Identity/JwtBearerSignInResult.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Bit.Core.Models.Table; - -namespace Bit.Core.Identity -{ - public class JwtBearerSignInResult - { - private static readonly JwtBearerSignInResult _success = new JwtBearerSignInResult { Succeeded = true }; - private static readonly JwtBearerSignInResult _failed = new JwtBearerSignInResult(); - private static readonly JwtBearerSignInResult _lockedOut = new JwtBearerSignInResult { IsLockedOut = true }; - private static readonly JwtBearerSignInResult _notAllowed = new JwtBearerSignInResult { IsNotAllowed = true }; - private static readonly JwtBearerSignInResult _twoFactorRequired = new JwtBearerSignInResult { RequiresTwoFactor = true }; - - public bool Succeeded { get; protected set; } - public bool IsLockedOut { get; protected set; } - public bool IsNotAllowed { get; protected set; } - public bool RequiresTwoFactor { get; protected set; } - public string Token { get; set; } - public User User { get; set; } - - public static JwtBearerSignInResult Success => _success; - public static JwtBearerSignInResult Failed => _failed; - public static JwtBearerSignInResult LockedOut => _lockedOut; - public static JwtBearerSignInResult NotAllowed => _notAllowed; - public static JwtBearerSignInResult TwoFactorRequired => _twoFactorRequired; - - public override string ToString() - { - return IsLockedOut ? "Lockedout" : - IsNotAllowed ? "NotAllowed" : - RequiresTwoFactor ? "RequiresTwoFactor" : - Succeeded ? "Succeeded" : "Failed"; - } - } -} diff --git a/src/Core/IdentityServer/ApiResources.cs b/src/Core/IdentityServer/ApiResources.cs index caa1fabe47..f6910be1aa 100644 --- a/src/Core/IdentityServer/ApiResources.cs +++ b/src/Core/IdentityServer/ApiResources.cs @@ -1,4 +1,5 @@ -using IdentityServer4.Models; +using IdentityModel; +using IdentityServer4.Models; using System.Collections.Generic; using System.Security.Claims; @@ -11,13 +12,9 @@ namespace Bit.Core.IdentityServer return new List { new ApiResource("api", new string[] { - ClaimTypes.AuthenticationMethod, - ClaimTypes.NameIdentifier, - ClaimTypes.Email, - "securitystamp", - - "name", - "email", + JwtClaimTypes.Name, + JwtClaimTypes.Email, + JwtClaimTypes.EmailVerified, "sstamp", // security stamp "plan", "device", diff --git a/src/Core/IdentityServer/ProfileService.cs b/src/Core/IdentityServer/ProfileService.cs index 5d45932ee6..940c9adef4 100644 --- a/src/Core/IdentityServer/ProfileService.cs +++ b/src/Core/IdentityServer/ProfileService.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Builder; using System.Linq; using Microsoft.Extensions.Options; using System; +using IdentityModel; namespace Bit.Core.IdentityServer { @@ -42,43 +43,40 @@ namespace Bit.Core.IdentityServer newClaims.AddRange(new List { new Claim("plan", "0"), // free plan hard coded for now - new Claim("sstamp", user.SecurityStamp), - new Claim("email", user.Email), - - // Deprecated claims for backwards compatability - new Claim(_identityOptions.ClaimsIdentity.UserNameClaimType, user.Email), + new Claim(JwtClaimTypes.Email, user.Email), + new Claim(JwtClaimTypes.EmailVerified, user.EmailVerified ? "true" : "false"), new Claim(_identityOptions.ClaimsIdentity.SecurityStampClaimType, user.SecurityStamp) }); if(!string.IsNullOrWhiteSpace(user.Name)) { - newClaims.Add(new Claim("name", user.Name)); + newClaims.Add(new Claim(JwtClaimTypes.Name, user.Name)); } // Orgs that this user belongs to var orgs = await _organizationUserRepository.GetManyByUserAsync(user.Id); if(orgs.Any()) { - var groupedOrgs = orgs.Where(o => o.Status == Core.Enums.OrganizationUserStatusType.Confirmed) + var groupedOrgs = orgs.Where(o => o.Status == Enums.OrganizationUserStatusType.Confirmed) .GroupBy(o => o.Type); foreach(var group in groupedOrgs) { switch(group.Key) { - case Core.Enums.OrganizationUserType.Owner: + case Enums.OrganizationUserType.Owner: foreach(var org in group) { newClaims.Add(new Claim("orgowner", org.OrganizationId.ToString())); } break; - case Core.Enums.OrganizationUserType.Admin: + case Enums.OrganizationUserType.Admin: foreach(var org in group) { newClaims.Add(new Claim("orgadmin", org.OrganizationId.ToString())); } break; - case Core.Enums.OrganizationUserType.User: + case Enums.OrganizationUserType.User: foreach(var org in group) { newClaims.Add(new Claim("orguser", org.OrganizationId.ToString())); diff --git a/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs b/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs index bf1d940f2e..ab91486417 100644 --- a/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs +++ b/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs @@ -1,19 +1,12 @@ using Bit.Core.Models.Api; using Bit.Core.Models.Table; using Bit.Core.Enums; -using Bit.Core.Identity; using Bit.Core.Repositories; using IdentityServer4.Models; using IdentityServer4.Validation; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http.Authentication; using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.Options; -using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; -using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Bit.Core.Services; @@ -23,57 +16,26 @@ namespace Bit.Core.IdentityServer public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator { private UserManager _userManager; - private IdentityOptions _identityOptions; - private JwtBearerOptions _jwtBearerOptions; - private JwtBearerIdentityOptions _jwtBearerIdentityOptions; private readonly IDeviceRepository _deviceRepository; private readonly IDeviceService _deviceService; public ResourceOwnerPasswordValidator( UserManager userManager, - IOptions identityOptionsAccessor, - IOptions jwtIdentityOptionsAccessor, IDeviceRepository deviceRepository, IDeviceService deviceService) { _userManager = userManager; - _identityOptions = identityOptionsAccessor?.Value ?? new IdentityOptions(); - _jwtBearerIdentityOptions = jwtIdentityOptionsAccessor?.Value; - _jwtBearerOptions = Identity.JwtBearerAppBuilderExtensions.BuildJwtBearerOptions(_jwtBearerIdentityOptions); _deviceRepository = deviceRepository; _deviceService = deviceService; } public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { - var oldAuthBearer = context.Request.Raw["OldAuthBearer"]?.ToString(); var twoFactorToken = context.Request.Raw["TwoFactorToken"]?.ToString(); var twoFactorProvider = context.Request.Raw["TwoFactorProvider"]?.ToString(); var twoFactorRequest = !string.IsNullOrWhiteSpace(twoFactorToken) && !string.IsNullOrWhiteSpace(twoFactorProvider); - if(!string.IsNullOrWhiteSpace(oldAuthBearer) && _jwtBearerOptions != null) - { - // support transferring the old auth bearer token - var ticket = ValidateOldAuthBearer(oldAuthBearer); - if(ticket != null && ticket.Principal != null) - { - var idClaim = ticket.Principal.Claims - .FirstOrDefault(c => c.Type == _identityOptions.ClaimsIdentity.UserIdClaimType); - var securityTokenClaim = ticket.Principal.Claims - .FirstOrDefault(c => c.Type == _identityOptions.ClaimsIdentity.SecurityStampClaimType); - if(idClaim != null && securityTokenClaim != null) - { - var user = await _userManager.FindByIdAsync(idClaim.Value); - if(user != null && user.SecurityStamp == securityTokenClaim.Value) - { - var device = await SaveDeviceAsync(user, context); - BuildSuccessResult(user, context, device); - return; - } - } - } - } - else if(!string.IsNullOrWhiteSpace(context.UserName)) + if(!string.IsNullOrWhiteSpace(context.UserName)) { var user = await _userManager.FindByEmailAsync(context.UserName.ToLowerInvariant()); if(user != null) @@ -110,12 +72,7 @@ namespace Bit.Core.IdentityServer private void BuildSuccessResult(User user, ResourceOwnerPasswordValidationContext context, Device device) { - var claims = new List - { - // Deprecated claims for backwards compatibility - new Claim(ClaimTypes.AuthenticationMethod, "Application"), - new Claim(_identityOptions.ClaimsIdentity.UserIdClaimType, user.Id.ToString()) - }; + var claims = new List(); if(device != null) { @@ -164,34 +121,6 @@ namespace Bit.Core.IdentityServer }}); } - private AuthenticationTicket ValidateOldAuthBearer(string token) - { - SecurityToken validatedToken; - foreach(var validator in _jwtBearerOptions.SecurityTokenValidators) - { - if(validator.CanReadToken(token)) - { - ClaimsPrincipal principal; - try - { - principal = validator.ValidateToken(token, - _jwtBearerOptions.TokenValidationParameters, out validatedToken); - } - catch - { - continue; - } - - var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), - _jwtBearerOptions.AuthenticationScheme); - - return ticket; - } - } - - return null; - } - private async Task TwoFactorRequiredAsync(User user) { return _userManager.SupportsUserTwoFactor && diff --git a/src/Core/Models/Api/Request/AuthTokenRequestModel.cs b/src/Core/Models/Api/Request/AuthTokenRequestModel.cs deleted file mode 100644 index 499541799b..0000000000 --- a/src/Core/Models/Api/Request/AuthTokenRequestModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Bit.Core.Models.Api -{ - public class AuthTokenRequestModel - { - [Required] - [EmailAddress] - [StringLength(50)] - public string Email { get; set; } - [Required] - public string MasterPasswordHash { get; set; } - public DeviceRequestModel Device { get; set; } - } -} diff --git a/src/Core/Models/Api/Request/AuthTokenTwoFactorRequestModel.cs b/src/Core/Models/Api/Request/AuthTokenTwoFactorRequestModel.cs deleted file mode 100644 index 07a0cc72ec..0000000000 --- a/src/Core/Models/Api/Request/AuthTokenTwoFactorRequestModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Bit.Core.Models.Api -{ - public class AuthTokenTwoFactorRequestModel - { - [Required] - public string Code { get; set; } - [Required] - public string Provider { get; set; } - public DeviceRequestModel Device { get; set; } - } -} diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs index 3d88857b41..bcf6dfae72 100644 --- a/src/Core/Utilities/ServiceCollectionExtensions.cs +++ b/src/Core/Utilities/ServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ using Bit.Core.IdentityServer; using Bit.Core.Models.Table; using Bit.Core.Repositories; using Bit.Core.Services; +using IdentityModel; using IdentityServer4.Services; using IdentityServer4.Stores; using IdentityServer4.Validation; @@ -12,11 +13,8 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.IdentityModel.Tokens; using Microsoft.WindowsAzure.Storage; -using System; using System.Security.Claims; -using System.Text; using SqlServerRepos = Bit.Core.Repositories.SqlServer; namespace Bit.Core.Utilities @@ -70,7 +68,7 @@ namespace Bit.Core.Utilities { services.AddTransient(); - var identityBuilder = services.AddJwtBearerIdentity(options => + var identityBuilder = services.AddIdentity(options => { options.User = new UserOptions { @@ -87,18 +85,11 @@ namespace Bit.Core.Utilities }; options.ClaimsIdentity = new ClaimsIdentityOptions { - SecurityStampClaimType = "securitystamp", - UserNameClaimType = ClaimTypes.Email + SecurityStampClaimType = "sstamp", + UserNameClaimType = JwtClaimTypes.Email, + UserIdClaimType = JwtClaimTypes.Subject, }; options.Tokens.ChangeEmailTokenProvider = TokenOptions.DefaultEmailProvider; - }, jwtBearerOptions => - { - jwtBearerOptions.Audience = "bitwarden"; - jwtBearerOptions.Issuer = "bitwarden"; - jwtBearerOptions.TokenLifetime = TimeSpan.FromDays(10 * 365); - jwtBearerOptions.TwoFactorTokenLifetime = TimeSpan.FromMinutes(10); - var keyBytes = Encoding.ASCII.GetBytes(globalSettings.JwtSigningKey); - jwtBearerOptions.SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(keyBytes), SecurityAlgorithms.HmacSha256); }); identityBuilder