mirror of
https://github.com/bitwarden/server.git
synced 2024-11-26 12:55:17 +01:00
passwordless sign in for admin
This commit is contained in:
parent
1be7701da0
commit
d35d8185ed
@ -27,4 +27,10 @@
|
|||||||
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
|
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="appsettings.json">
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
7
src/Admin/AdminSettings.cs
Normal file
7
src/Admin/AdminSettings.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Bit.Admin
|
||||||
|
{
|
||||||
|
public class AdminSettings
|
||||||
|
{
|
||||||
|
public virtual string Admins { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Bit.Admin.Models;
|
using Bit.Admin.Models;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
||||||
namespace Bit.Admin.Controllers
|
namespace Bit.Admin.Controllers
|
||||||
{
|
{
|
||||||
public class HomeController : Controller
|
public class HomeController : Controller
|
||||||
{
|
{
|
||||||
|
[Authorize]
|
||||||
public IActionResult Index()
|
public IActionResult Index()
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
|
48
src/Admin/Controllers/LoginController.cs
Normal file
48
src/Admin/Controllers/LoginController.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Admin.Models;
|
||||||
|
using Bit.Core.Identity;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Bit.Admin.Controllers
|
||||||
|
{
|
||||||
|
public class LoginController : Controller
|
||||||
|
{
|
||||||
|
private readonly PasswordlessSignInManager<IdentityUser> _signInManager;
|
||||||
|
|
||||||
|
public LoginController(
|
||||||
|
PasswordlessSignInManager<IdentityUser> signInManager)
|
||||||
|
{
|
||||||
|
_signInManager = signInManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> Index(LoginModel model)
|
||||||
|
{
|
||||||
|
if(ModelState.IsValid)
|
||||||
|
{
|
||||||
|
await _signInManager.PasswordlessSignInAsync(model.Email);
|
||||||
|
return RedirectToAction("Index", "Home");
|
||||||
|
}
|
||||||
|
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IActionResult> Confirm(string email, string token)
|
||||||
|
{
|
||||||
|
var result = await _signInManager.PasswordlessSignInAsync(email, token, false);
|
||||||
|
if(!result.Succeeded)
|
||||||
|
{
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
return RedirectToAction("Index", "Home");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/Admin/Models/LoginModel.cs
Normal file
11
src/Admin/Models/LoginModel.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Admin.Models
|
||||||
|
{
|
||||||
|
public class LoginModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
public string Email { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Bit.Core;
|
||||||
|
using Bit.Core.Identity;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Serilog.Events;
|
||||||
|
using Stripe;
|
||||||
|
|
||||||
namespace Bit.Admin
|
namespace Bit.Admin
|
||||||
{
|
{
|
||||||
@ -18,22 +26,52 @@ namespace Bit.Admin
|
|||||||
|
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddMvc();
|
// Options
|
||||||
|
services.AddOptions();
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
var globalSettings = services.AddGlobalSettingsServices(Configuration);
|
||||||
|
services.Configure<AdminSettings>(Configuration.GetSection("AdminSettings"));
|
||||||
|
|
||||||
|
// Stripe Billing
|
||||||
|
StripeConfiguration.SetApiKey(globalSettings.StripeApiKey);
|
||||||
|
|
||||||
|
// Repositories
|
||||||
|
services.AddSqlServerRepositories(globalSettings);
|
||||||
|
|
||||||
|
// Context
|
||||||
|
services.AddScoped<CurrentContext>();
|
||||||
|
|
||||||
|
// Identity
|
||||||
|
services.AddPasswordlessIdentityServices<ReadOnlyEnvIdentityUserStore>(globalSettings);
|
||||||
|
|
||||||
|
// Services
|
||||||
|
services.AddBaseServices();
|
||||||
|
services.AddDefaultServices(globalSettings);
|
||||||
|
|
||||||
|
// Mvc
|
||||||
|
services.AddMvc(config =>
|
||||||
|
{
|
||||||
|
config.Filters.Add(new LoggingExceptionHandlerFilterAttribute());
|
||||||
|
});
|
||||||
services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
|
services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
public void Configure(
|
||||||
|
IApplicationBuilder app,
|
||||||
|
IHostingEnvironment env,
|
||||||
|
IApplicationLifetime appLifetime,
|
||||||
|
GlobalSettings globalSettings,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
|
loggerFactory.AddSerilog(env, appLifetime, globalSettings, (e) => e.Level >= LogEventLevel.Error);
|
||||||
|
|
||||||
if(env.IsDevelopment())
|
if(env.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseBrowserLink();
|
|
||||||
app.UseDeveloperExceptionPage();
|
app.UseDeveloperExceptionPage();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
app.UseExceptionHandler("/home/error");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
app.UseMvcWithDefaultRoute();
|
app.UseMvcWithDefaultRoute();
|
||||||
}
|
}
|
||||||
|
21
src/Admin/Views/Login/Index.cshtml
Normal file
21
src/Admin/Views/Login/Index.cshtml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
@model LoginModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Login";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="row justify-content-md-center">
|
||||||
|
<div class="col-4">
|
||||||
|
<p>Please enter your email address below to log in.</p>
|
||||||
|
<form asp-action="" method="post">
|
||||||
|
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Email" class="sr-only">Email Address</label>
|
||||||
|
<input asp-for="Email" type="email" class="form-control" placeholder="ex. john@example.com"
|
||||||
|
required autofocus>
|
||||||
|
<span asp-validation-for="Email" class="invalid-feedback"></span>
|
||||||
|
<small class="form-text text-muted">We'll email you a secure login link.</small>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary btn-block" type="submit">Continue</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"IncludeScopes": false,
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Debug",
|
|
||||||
"System": "Information",
|
|
||||||
"Microsoft": "Information"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
13
src/Admin/appsettings.Production.json
Normal file
13
src/Admin/appsettings.Production.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"globalSettings": {
|
||||||
|
"baseServiceUri": {
|
||||||
|
"vault": "https://vault.bitwarden.com",
|
||||||
|
"api": "https://api.bitwarden.com",
|
||||||
|
"identity": "https://identity.bitwarden.com",
|
||||||
|
"internalIdentity": "https://identity.bitwarden.com"
|
||||||
|
},
|
||||||
|
"braintree": {
|
||||||
|
"production": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,50 @@
|
|||||||
{
|
{
|
||||||
"Logging": {
|
"globalSettings": {
|
||||||
"IncludeScopes": false,
|
"selfHosted": false,
|
||||||
"LogLevel": {
|
"siteName": "Bitwarden",
|
||||||
"Default": "Warning"
|
"projectName": "Admin",
|
||||||
|
"stripeApiKey": "SECRET",
|
||||||
|
"baseServiceUri": {
|
||||||
|
"vault": "http://localhost:4001",
|
||||||
|
"api": "http://localhost:4000",
|
||||||
|
"identity": "http://localhost:33656",
|
||||||
|
"internalIdentity": "http://localhost:33656"
|
||||||
|
},
|
||||||
|
"sqlServer": {
|
||||||
|
"connectionString": "SECRET"
|
||||||
|
},
|
||||||
|
"mail": {
|
||||||
|
"sendGridApiKey": "SECRET",
|
||||||
|
"replyToEmail": "hello@bitwarden.com"
|
||||||
|
},
|
||||||
|
"identityServer": {
|
||||||
|
"certificateThumbprint": "SECRET"
|
||||||
|
},
|
||||||
|
"dataProtection": {
|
||||||
|
"certificateThumbprint": "SECRET"
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"connectionString": "SECRET"
|
||||||
|
},
|
||||||
|
"events": {
|
||||||
|
"connectionString": "SECRET"
|
||||||
|
},
|
||||||
|
"documentDb": {
|
||||||
|
"uri": "SECRET",
|
||||||
|
"key": "SECRET"
|
||||||
|
},
|
||||||
|
"notificationHub": {
|
||||||
|
"connectionString": "SECRET",
|
||||||
|
"hubName": "SECRET"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"adminSettings": {
|
||||||
|
"admins": ""
|
||||||
|
},
|
||||||
|
"braintree": {
|
||||||
|
"production": false,
|
||||||
|
"merchantId": "SECRET",
|
||||||
|
"publicKey": "SECRET",
|
||||||
|
"privateKey": "SECRET"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,7 @@ using Bit.Core.Utilities;
|
|||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Bit.Billing.Utilities;
|
|
||||||
using Bit.Core.Identity;
|
using Bit.Core.Identity;
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
||||||
namespace Bit.Billing
|
namespace Bit.Billing
|
||||||
@ -45,28 +43,7 @@ namespace Bit.Billing
|
|||||||
services.AddScoped<CurrentContext>();
|
services.AddScoped<CurrentContext>();
|
||||||
|
|
||||||
// Identity
|
// Identity
|
||||||
services.AddTransient<ILookupNormalizer, LowerInvariantLookupNormalizer>();
|
services.AddPasswordlessIdentityServices<ReadOnlyDatabaseIdentityUserStore>(globalSettings);
|
||||||
services.AddIdentity<IdentityUser, Core.Models.Table.Role>()
|
|
||||||
.AddUserStore<ReadOnlyIdentityUserStore>()
|
|
||||||
.AddRoleStore<RoleStore>()
|
|
||||||
.AddDefaultTokenProviders();
|
|
||||||
services.TryAddScoped<PasswordlessSignInManager<IdentityUser>, PasswordlessSignInManager<IdentityUser>>();
|
|
||||||
|
|
||||||
services.Configure<DataProtectionTokenProviderOptions>(options =>
|
|
||||||
{
|
|
||||||
options.TokenLifespan = TimeSpan.FromMinutes(15);
|
|
||||||
});
|
|
||||||
services.ConfigureApplicationCookie(options =>
|
|
||||||
{
|
|
||||||
options.LoginPath = "/login";
|
|
||||||
options.LogoutPath = "/";
|
|
||||||
options.AccessDeniedPath = "/login";
|
|
||||||
options.Cookie.Name = "BitwardenBilling";
|
|
||||||
options.Cookie.HttpOnly = true;
|
|
||||||
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
|
|
||||||
options.ReturnUrlParameter = "returnUrl";
|
|
||||||
options.SlidingExpiration = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
services.AddBaseServices();
|
services.AddBaseServices();
|
||||||
@ -77,7 +54,7 @@ namespace Bit.Billing
|
|||||||
// Mvc
|
// Mvc
|
||||||
services.AddMvc(config =>
|
services.AddMvc(config =>
|
||||||
{
|
{
|
||||||
config.Filters.Add(new ExceptionHandlerFilterAttribute());
|
config.Filters.Add(new LoggingExceptionHandlerFilterAttribute());
|
||||||
});
|
});
|
||||||
services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
|
services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
|
||||||
}
|
}
|
||||||
|
37
src/Core/Identity/ReadOnlyDatabaseIdentityUserStore.cs
Normal file
37
src/Core/Identity/ReadOnlyDatabaseIdentityUserStore.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Core.Identity
|
||||||
|
{
|
||||||
|
public class ReadOnlyDatabaseIdentityUserStore : ReadOnlyIdentityUserStore
|
||||||
|
{
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
|
public ReadOnlyDatabaseIdentityUserStore(IUserRepository userRepository)
|
||||||
|
{
|
||||||
|
_userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
var user = await _userRepository.GetByEmailAsync(normalizedEmail);
|
||||||
|
return user?.ToIdentityUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<IdentityUser> FindByIdAsync(string userId,
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
if(!Guid.TryParse(userId, out var userIdGuid))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await _userRepository.GetByIdAsync(userIdGuid);
|
||||||
|
return user?.ToIdentityUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
src/Core/Identity/ReadOnlyEnvIdentityUserStore.cs
Normal file
53
src/Core/Identity/ReadOnlyEnvIdentityUserStore.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace Bit.Core.Identity
|
||||||
|
{
|
||||||
|
public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
|
||||||
|
public ReadOnlyEnvIdentityUserStore(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
var usersCsv = _configuration["adminSettings:admins"];
|
||||||
|
if(string.IsNullOrWhiteSpace(usersCsv))
|
||||||
|
{
|
||||||
|
return Task.FromResult<IdentityUser>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
var users = usersCsv.ToLowerInvariant().Split(',');
|
||||||
|
var user = users.Where(a => a.Trim() == normalizedEmail).FirstOrDefault();
|
||||||
|
if(user == null || !user.Contains("@"))
|
||||||
|
{
|
||||||
|
return Task.FromResult<IdentityUser>(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
user = user.Trim();
|
||||||
|
return Task.FromResult(new IdentityUser
|
||||||
|
{
|
||||||
|
Id = user,
|
||||||
|
Email = user,
|
||||||
|
NormalizedEmail = user,
|
||||||
|
EmailConfirmed = true,
|
||||||
|
UserName = user,
|
||||||
|
NormalizedUserName = user,
|
||||||
|
SecurityStamp = user
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<IdentityUser> FindByIdAsync(string userId,
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
return FindByEmailAsync(userId, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,22 +2,14 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Bit.Core.Repositories;
|
|
||||||
|
|
||||||
namespace Bit.Core.Identity
|
namespace Bit.Core.Identity
|
||||||
{
|
{
|
||||||
public class ReadOnlyIdentityUserStore :
|
public abstract class ReadOnlyIdentityUserStore :
|
||||||
IUserStore<IdentityUser>,
|
IUserStore<IdentityUser>,
|
||||||
IUserEmailStore<IdentityUser>,
|
IUserEmailStore<IdentityUser>,
|
||||||
IUserSecurityStampStore<IdentityUser>
|
IUserSecurityStampStore<IdentityUser>
|
||||||
{
|
{
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
|
|
||||||
public ReadOnlyIdentityUserStore(IUserRepository userRepository)
|
|
||||||
{
|
|
||||||
_userRepository = userRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
|
|
||||||
public Task<IdentityResult> CreateAsync(IdentityUser user,
|
public Task<IdentityResult> CreateAsync(IdentityUser user,
|
||||||
@ -32,24 +24,11 @@ namespace Bit.Core.Identity
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
public abstract Task<IdentityUser> FindByEmailAsync(string normalizedEmail,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken));
|
||||||
{
|
|
||||||
var user = await _userRepository.GetByEmailAsync(normalizedEmail);
|
|
||||||
return user?.ToIdentityUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IdentityUser> FindByIdAsync(string userId,
|
public abstract Task<IdentityUser> FindByIdAsync(string userId,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken));
|
||||||
{
|
|
||||||
if(!Guid.TryParse(userId, out var userIdGuid))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var user = await _userRepository.GetByIdAsync(userIdGuid);
|
|
||||||
return user?.ToIdentityUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IdentityUser> FindByNameAsync(string normalizedUserName,
|
public async Task<IdentityUser> FindByNameAsync(string normalizedUserName,
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Bit.Billing.Utilities
|
namespace Bit.Core.Utilities
|
||||||
{
|
{
|
||||||
public class ExceptionHandlerFilterAttribute : ExceptionFilterAttribute
|
public class LoggingExceptionHandlerFilterAttribute : ExceptionFilterAttribute
|
||||||
{
|
{
|
||||||
public override void OnException(ExceptionContext context)
|
public override void OnException(ExceptionContext context)
|
||||||
{
|
{
|
||||||
@ -15,7 +15,8 @@ namespace Bit.Billing.Utilities
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ExceptionHandlerFilterAttribute>>();
|
var logger = context.HttpContext.RequestServices
|
||||||
|
.GetRequiredService<ILogger<LoggingExceptionHandlerFilterAttribute>>();
|
||||||
logger.LogError(0, exception, exception.Message);
|
logger.LogError(0, exception, exception.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,7 +11,6 @@ using IdentityServer4.Validation;
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@ -22,6 +21,7 @@ using System.IO;
|
|||||||
using SqlServerRepos = Bit.Core.Repositories.SqlServer;
|
using SqlServerRepos = Bit.Core.Repositories.SqlServer;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using TableStorageRepos = Bit.Core.Repositories.TableStorage;
|
using TableStorageRepos = Bit.Core.Repositories.TableStorage;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
namespace Bit.Core.Utilities
|
namespace Bit.Core.Utilities
|
||||||
{
|
{
|
||||||
@ -199,6 +199,38 @@ namespace Bit.Core.Utilities
|
|||||||
return identityBuilder;
|
return identityBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IdentityBuilder AddPasswordlessIdentityServices<TUserStore>(
|
||||||
|
this IServiceCollection services, GlobalSettings globalSettings) where TUserStore : class
|
||||||
|
{
|
||||||
|
services.AddTransient<ILookupNormalizer, LowerInvariantLookupNormalizer>();
|
||||||
|
|
||||||
|
services.Configure<DataProtectionTokenProviderOptions>(options =>
|
||||||
|
{
|
||||||
|
options.TokenLifespan = TimeSpan.FromMinutes(15);
|
||||||
|
});
|
||||||
|
|
||||||
|
var identityBuilder = services.AddIdentity<IdentityUser, Role>()
|
||||||
|
.AddUserStore<TUserStore>()
|
||||||
|
.AddRoleStore<RoleStore>()
|
||||||
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
|
services.TryAddScoped<PasswordlessSignInManager<IdentityUser>, PasswordlessSignInManager<IdentityUser>>();
|
||||||
|
|
||||||
|
services.ConfigureApplicationCookie(options =>
|
||||||
|
{
|
||||||
|
options.LoginPath = "/login";
|
||||||
|
options.LogoutPath = "/";
|
||||||
|
options.AccessDeniedPath = "/login?accessDenied=1";
|
||||||
|
options.Cookie.Name = $"Bitwarden_{globalSettings.ProjectName}";
|
||||||
|
options.Cookie.HttpOnly = true;
|
||||||
|
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
|
||||||
|
options.ReturnUrlParameter = "returnUrl";
|
||||||
|
options.SlidingExpiration = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return identityBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
public static IIdentityServerBuilder AddCustomIdentityServerServices(
|
public static IIdentityServerBuilder AddCustomIdentityServerServices(
|
||||||
this IServiceCollection services, IHostingEnvironment env, GlobalSettings globalSettings)
|
this IServiceCollection services, IHostingEnvironment env, GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user