1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 23:52:50 -05:00

Fix safari sso header size (#1065)

* Safari SSO header size fix - in progress

* Cleanup of memoryCacheTicketStore

* Redis cache ticket store + registration

* Revert some unecessary changes

* temp - distributed cookie: idsrv.external

* Ticket data cached storage added

* OIDC working w/ substantially reduced cookie size

* Added distributed cache cookie manager

* Removed hybrid OIDC flow

* Enable self-hosted folks to use Redis  for SSO

* Also allow self-hosted to use Redis cont...
This commit is contained in:
Chad Scharf
2021-01-11 11:03:46 -05:00
committed by GitHub
parent 5aba9f7549
commit 99b95b5330
17 changed files with 398 additions and 36 deletions

View File

@ -204,7 +204,7 @@ namespace Bit.Sso.Controllers
{ {
// Read external identity from the temporary cookie // Read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync( var result = await HttpContext.AuthenticateAsync(
IdentityServerConstants.ExternalCookieAuthenticationScheme); Core.AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
if (result?.Succeeded != true) if (result?.Succeeded != true)
{ {
throw new Exception(_i18nService.T("ExternalAuthenticationError")); throw new Exception(_i18nService.T("ExternalAuthenticationError"));
@ -249,7 +249,7 @@ namespace Bit.Sso.Controllers
} }
// Delete temporary cookie used during external authentication // Delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); await HttpContext.SignOutAsync(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
// Retrieve return URL // Retrieve return URL
var returnUrl = result.Properties.Items["return_url"] ?? "~/"; var returnUrl = result.Properties.Items["return_url"] ?? "~/";

View File

@ -7,6 +7,7 @@
<UserSecretsId>bitwarden-Sso</UserSecretsId> <UserSecretsId>bitwarden-Sso</UserSecretsId>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(RunConfiguration)' == 'Sso' " />
<ItemGroup> <ItemGroup>
<PackageReference Include="Sustainsys.Saml2.AspNetCore2" Version="2.7.0" /> <PackageReference Include="Sustainsys.Saml2.AspNetCore2" Version="2.7.0" />
</ItemGroup> </ItemGroup>

View File

@ -58,7 +58,9 @@ namespace Bit.Sso
} }
// Authentication // Authentication
services.AddAuthentication(); services.AddDistributedIdentityServices(globalSettings);
services.AddAuthentication()
.AddCookie(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
services.AddSsoServices(globalSettings); services.AddSsoServices(globalSettings);
// IdentityServer // IdentityServer

View File

@ -14,8 +14,10 @@ using Bit.Sso.Models;
using Bit.Sso.Utilities; using Bit.Sso.Utilities;
using IdentityModel; using IdentityModel;
using IdentityServer4; using IdentityServer4;
using IdentityServer4.Infrastructure;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
@ -40,6 +42,7 @@ namespace Bit.Core.Business.Sso
private readonly Dictionary<string, DynamicAuthenticationScheme> _cachedSchemes; private readonly Dictionary<string, DynamicAuthenticationScheme> _cachedSchemes;
private readonly Dictionary<string, DynamicAuthenticationScheme> _cachedHandlerSchemes; private readonly Dictionary<string, DynamicAuthenticationScheme> _cachedHandlerSchemes;
private readonly SemaphoreSlim _semaphore; private readonly SemaphoreSlim _semaphore;
private readonly IHttpContextAccessor _httpContextAccessor;
private DateTime? _lastSchemeLoad; private DateTime? _lastSchemeLoad;
private IEnumerable<DynamicAuthenticationScheme> _schemesCopy = Array.Empty<DynamicAuthenticationScheme>(); private IEnumerable<DynamicAuthenticationScheme> _schemesCopy = Array.Empty<DynamicAuthenticationScheme>();
@ -54,7 +57,8 @@ namespace Bit.Core.Business.Sso
ISsoConfigRepository ssoConfigRepository, ISsoConfigRepository ssoConfigRepository,
ILogger<DynamicAuthenticationSchemeProvider> logger, ILogger<DynamicAuthenticationSchemeProvider> logger,
GlobalSettings globalSettings, GlobalSettings globalSettings,
SamlEnvironment samlEnvironment) SamlEnvironment samlEnvironment,
IHttpContextAccessor httpContextAccessor)
: base(options) : base(options)
{ {
_oidcPostConfigureOptions = oidcPostConfigureOptions; _oidcPostConfigureOptions = oidcPostConfigureOptions;
@ -81,6 +85,7 @@ namespace Bit.Core.Business.Sso
_cachedSchemes = new Dictionary<string, DynamicAuthenticationScheme>(); _cachedSchemes = new Dictionary<string, DynamicAuthenticationScheme>();
_cachedHandlerSchemes = new Dictionary<string, DynamicAuthenticationScheme>(); _cachedHandlerSchemes = new Dictionary<string, DynamicAuthenticationScheme>();
_semaphore = new SemaphoreSlim(1); _semaphore = new SemaphoreSlim(1);
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
} }
private bool CacheIsValid private bool CacheIsValid
@ -304,7 +309,7 @@ namespace Bit.Core.Business.Sso
ClientSecret = config.ClientSecret, ClientSecret = config.ClientSecret,
ResponseType = "code", ResponseType = "code",
ResponseMode = "form_post", ResponseMode = "form_post",
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme, SignInScheme = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme,
SignOutScheme = IdentityServerConstants.SignoutScheme, SignOutScheme = IdentityServerConstants.SignoutScheme,
SaveTokens = false, // reduce overall request size SaveTokens = false, // reduce overall request size
TokenValidationParameters = new TokenValidationParameters TokenValidationParameters = new TokenValidationParameters
@ -332,6 +337,8 @@ namespace Bit.Core.Business.Sso
oidcOptions.Scope.Add(OpenIdConnectScopes.Profile); oidcOptions.Scope.Add(OpenIdConnectScopes.Profile);
} }
oidcOptions.StateDataFormat = new DistributedCacheStateDataFormatter(_httpContextAccessor, name);
return new DynamicAuthenticationScheme(name, name, typeof(OpenIdConnectHandler), return new DynamicAuthenticationScheme(name, name, typeof(OpenIdConnectHandler),
oidcOptions, SsoType.OpenIdConnect); oidcOptions, SsoType.OpenIdConnect);
} }
@ -407,8 +414,9 @@ namespace Bit.Core.Business.Sso
var options = new Saml2Options var options = new Saml2Options
{ {
SPOptions = spOptions, SPOptions = spOptions,
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme, SignInScheme = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme,
SignOutScheme = IdentityServerConstants.DefaultCookieAuthenticationScheme, SignOutScheme = IdentityServerConstants.DefaultCookieAuthenticationScheme,
CookieManager = new IdentityServer.DistributedCacheCookieManager(),
}; };
options.IdentityProviders.Add(idp); options.IdentityProviders.Add(idp);

View File

@ -33,8 +33,6 @@ namespace Bit.Api
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
var provider = services.BuildServiceProvider();
// Options // Options
services.AddOptions(); services.AddOptions();

View File

@ -9,4 +9,9 @@
{ {
public const string LinkSso = "LinkSso"; public const string LinkSso = "LinkSso";
} }
public static class AuthenticationSchemes
{
public const string BitwardenExternalCookieAuthenticationScheme = "bw.external";
}
} }

View File

@ -58,6 +58,7 @@
<PackageReference Include="Otp.NET" Version="1.2.2" /> <PackageReference Include="Otp.NET" Version="1.2.2" />
<PackageReference Include="YubicoDotNetClient" Version="1.2.0" /> <PackageReference Include="YubicoDotNetClient" Version="1.2.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.1" /> <PackageReference Include="System.Data.SqlClient" Version="4.8.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -141,6 +141,7 @@ namespace Bit.Core
{ {
public string CertificateThumbprint { get; set; } public string CertificateThumbprint { get; set; }
public string CertificatePassword { get; set; } public string CertificatePassword { get; set; }
public string RedisConnectionString { get; set; }
} }
public class DataProtectionSettings public class DataProtectionSettings

View File

@ -0,0 +1,53 @@
using System;
using IdentityServer4.Configuration;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Redis;
using Microsoft.Extensions.Options;
namespace Bit.Core.IdentityServer
{
public class ConfigureOpenIdConnectDistributedOptions : IPostConfigureOptions<CookieAuthenticationOptions>
{
private readonly IdentityServerOptions _idsrv;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly GlobalSettings _globalSettings;
public ConfigureOpenIdConnectDistributedOptions(IHttpContextAccessor httpContextAccessor, GlobalSettings globalSettings,
IdentityServerOptions idsrv)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
_globalSettings = globalSettings;
_idsrv = idsrv;
}
public void PostConfigure(string name, CookieAuthenticationOptions options)
{
options.CookieManager = new DistributedCacheCookieManager();
if (name != AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme)
{
// Ignore
return;
}
options.Cookie.Name = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme;
options.Cookie.IsEssential = true;
options.Cookie.SameSite = _idsrv.Authentication.CookieSameSiteMode;
options.TicketDataFormat = new DistributedCacheTicketDataFormatter(_httpContextAccessor, name);
if (string.IsNullOrWhiteSpace(_globalSettings.IdentityServer?.RedisConnectionString))
{
options.SessionStore = new MemoryCacheTicketStore();
}
else
{
var redisOptions = new RedisCacheOptions
{
Configuration = _globalSettings.IdentityServer.RedisConnectionString,
};
options.SessionStore = new RedisCacheTicketStore(redisOptions);
}
}
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Text;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.IdentityServer
{
public class DistributedCacheCookieManager : ICookieManager
{
private readonly ChunkingCookieManager _cookieManager;
public DistributedCacheCookieManager()
{
_cookieManager = new ChunkingCookieManager();
}
private string CacheKeyPrefix => "cookie-data";
public void AppendResponseCookie(HttpContext context, string key, string value, CookieOptions options)
{
var id = Guid.NewGuid().ToString();
var cacheKey = GetKey(key, id);
var expiresUtc = options.Expires ?? DateTimeOffset.UtcNow.AddMinutes(15);
var cacheOptions = new DistributedCacheEntryOptions()
.SetAbsoluteExpiration(expiresUtc);
var data = Encoding.UTF8.GetBytes(value);
var cache = GetCache(context);
cache.Set(cacheKey, data, cacheOptions);
// Write the cookie with the identifier as the body
_cookieManager.AppendResponseCookie(context, key, id, options);
}
public void DeleteCookie(HttpContext context, string key, CookieOptions options)
{
_cookieManager.DeleteCookie(context, key, options);
var id = GetId(context, key);
if (!string.IsNullOrWhiteSpace(id))
{
var cacheKey = GetKey(key, id);
GetCache(context).Remove(cacheKey);
}
}
public string GetRequestCookie(HttpContext context, string key)
{
var id = GetId(context, key);
if (string.IsNullOrWhiteSpace(id))
{
return null;
}
var cacheKey = GetKey(key, id);
return GetCache(context).GetString(cacheKey);
}
private IDistributedCache GetCache(HttpContext context) =>
context.RequestServices.GetRequiredService<IDistributedCache>();
private string GetKey(string key, string id) => $"{CacheKeyPrefix}-{key}-{id}";
private string GetId(HttpContext context, string key) =>
context.Request.Cookies.ContainsKey(key) ?
context.Request.Cookies[key] : null;
}
}

View File

@ -0,0 +1,66 @@
using System;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.IdentityServer
{
public class DistributedCacheTicketDataFormatter : ISecureDataFormat<AuthenticationTicket>
{
private readonly IHttpContextAccessor _httpContext;
private readonly string _name;
public DistributedCacheTicketDataFormatter(IHttpContextAccessor httpContext, string name)
{
_httpContext = httpContext;
_name = name;
}
private string CacheKeyPrefix => "ticket-data";
private IDistributedCache Cache => _httpContext.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
private IDataProtector Protector => _httpContext.HttpContext.RequestServices.GetRequiredService<IDataProtectionProvider>()
.CreateProtector(CacheKeyPrefix, _name);
public string Protect(AuthenticationTicket data) => Protect(data, null);
public string Protect(AuthenticationTicket data, string purpose)
{
var key = Guid.NewGuid().ToString();
var cacheKey = $"{CacheKeyPrefix}-{_name}-{purpose}-{key}";
var expiresUtc = data.Properties.ExpiresUtc ??
DateTimeOffset.UtcNow.AddMinutes(15);
var options = new DistributedCacheEntryOptions();
options.SetAbsoluteExpiration(expiresUtc);
var ticket = TicketSerializer.Default.Serialize(data);
Cache.Set(cacheKey, ticket, options);
return Protector.Protect(key);
}
public AuthenticationTicket Unprotect(string protectedText) => Unprotect(protectedText, null);
public AuthenticationTicket Unprotect(string protectedText, string purpose)
{
if (string.IsNullOrWhiteSpace(protectedText))
{
return null;
}
// Decrypt the key and retrieve the data from the cache.
var key = Protector.Unprotect(protectedText);
var cacheKey = $"{CacheKeyPrefix}-{_name}-{purpose}-{key}";
var ticket = Cache.Get(cacheKey);
if (ticket == null)
{
return null;
}
var data = TicketSerializer.Default.Deserialize(ticket);
return data;
}
}
}

View File

@ -0,0 +1,56 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Caching.Memory;
namespace Bit.Core.IdentityServer
{
public class MemoryCacheTicketStore : ITicketStore
{
private const string _keyPrefix = "auth-";
private readonly IMemoryCache _cache;
public MemoryCacheTicketStore()
{
_cache = new MemoryCache(new MemoryCacheOptions());
}
public async Task<string> StoreAsync(AuthenticationTicket ticket)
{
var key = $"{_keyPrefix}{Guid.NewGuid()}";
await RenewAsync(key, ticket);
return key;
}
public Task RenewAsync(string key, AuthenticationTicket ticket)
{
var options = new MemoryCacheEntryOptions();
var expiresUtc = ticket.Properties.ExpiresUtc;
if (expiresUtc.HasValue)
{
options.SetAbsoluteExpiration(expiresUtc.Value);
}
else
{
options.SetSlidingExpiration(TimeSpan.FromMinutes(15));
}
_cache.Set(key, ticket, options);
return Task.FromResult(0);
}
public Task<AuthenticationTicket> RetrieveAsync(string key)
{
_cache.TryGetValue(key, out AuthenticationTicket ticket);
return Task.FromResult(ticket);
}
public Task RemoveAsync(string key)
{
_cache.Remove(key);
return Task.FromResult(0);
}
}
}

View File

@ -14,8 +14,8 @@ namespace Bit.Core.IdentityServer
ClientSecrets = new List<Secret> { new Secret(globalSettings.OidcIdentityClientKey.Sha256()) }; ClientSecrets = new List<Secret> { new Secret(globalSettings.OidcIdentityClientKey.Sha256()) };
AllowedScopes = new string[] AllowedScopes = new string[]
{ {
IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile IdentityServerConstants.StandardScopes.Profile
}; };
AllowedGrantTypes = GrantTypes.Code; AllowedGrantTypes = GrantTypes.Code;
Enabled = true; Enabled = true;

View File

@ -0,0 +1,68 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Redis;
namespace Bit.Core.IdentityServer
{
public class RedisCacheTicketStore : ITicketStore
{
private const string _keyPrefix = "auth-";
private readonly IDistributedCache _cache;
public RedisCacheTicketStore(RedisCacheOptions options)
{
_cache = new RedisCache(options);
}
public async Task<string> StoreAsync(AuthenticationTicket ticket)
{
var key = $"{_keyPrefix}{Guid.NewGuid()}";
await RenewAsync(key, ticket);
return key;
}
public Task RenewAsync(string key, AuthenticationTicket ticket)
{
var options = new DistributedCacheEntryOptions();
var expiresUtc = ticket.Properties.ExpiresUtc ??
DateTimeOffset.UtcNow.AddMinutes(15);
options.SetAbsoluteExpiration(expiresUtc);
var val = SerializeToBytes(ticket);
_cache.Set(key, val, options);
return Task.FromResult(0);
}
public Task<AuthenticationTicket> RetrieveAsync(string key)
{
AuthenticationTicket ticket;
var bytes = _cache.Get(key);
ticket = DeserializeFromBytes(bytes);
return Task.FromResult(ticket);
}
public Task RemoveAsync(string key)
{
_cache.Remove(key);
return Task.FromResult(0);
}
private static byte[] SerializeToBytes(AuthenticationTicket source)
{
return TicketSerializer.Default.Serialize(source);
}
private static AuthenticationTicket DeserializeFromBytes(byte[] source)
{
return source == null ? null : TicketSerializer.Default.Deserialize(source);
}
}
}

View File

@ -1,40 +1,43 @@
using Bit.Core.Enums; using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using AutoMapper;
using Bit.Core.Enums;
using Bit.Core.Identity; using Bit.Core.Identity;
using Bit.Core.IdentityServer; using Bit.Core.IdentityServer;
using Bit.Core.Models.Table; using Bit.Core.Models.Table;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Resources;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities;
using IdentityModel; using IdentityModel;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Configuration;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
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.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.Azure.Storage;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection.Extensions;
using System; using Microsoft.Extensions.Hosting;
using System.IO; using Microsoft.Extensions.Options;
using SqlServerRepos = Bit.Core.Repositories.SqlServer; using Serilog.Context;
using EntityFrameworkRepos = Bit.Core.Repositories.EntityFramework; using EntityFrameworkRepos = Bit.Core.Repositories.EntityFramework;
using NoopRepos = Bit.Core.Repositories.Noop; using NoopRepos = Bit.Core.Repositories.Noop;
using System.Threading.Tasks; using SqlServerRepos = Bit.Core.Repositories.SqlServer;
using TableStorageRepos = Bit.Core.Repositories.TableStorage; using TableStorageRepos = Bit.Core.Repositories.TableStorage;
using Microsoft.Extensions.DependencyInjection.Extensions;
using IdentityServer4.AccessTokenValidation;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.HttpOverrides;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Bit.Core.Utilities;
using Serilog.Context;
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.Azure.Storage;
using System.Reflection;
using Bit.Core.Resources;
using Microsoft.AspNetCore.Mvc.Localization;
namespace Bit.Core.Utilities namespace Bit.Core.Utilities
{ {
@ -502,5 +505,31 @@ namespace Bit.Core.Utilities
return factory.Create("SharedResources", assemblyName.Name); return factory.Create("SharedResources", assemblyName.Name);
}); });
} }
public static IServiceCollection AddDistributedIdentityServices(this IServiceCollection services, GlobalSettings globalSettings)
{
if (string.IsNullOrWhiteSpace(globalSettings.IdentityServer?.RedisConnectionString))
{
services.AddDistributedMemoryCache();
}
else
{
services.AddDistributedRedisCache(options =>
options.Configuration = globalSettings.IdentityServer.RedisConnectionString);
}
services.AddOidcStateDataFormatterCache();
services.AddSession();
services.ConfigureApplicationCookie(configure => configure.CookieManager = new DistributedCacheCookieManager());
services.ConfigureExternalCookie(configure => configure.CookieManager = new DistributedCacheCookieManager());
services.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>>(
svcs => new ConfigureOpenIdConnectDistributedOptions(
svcs.GetRequiredService<IHttpContextAccessor>(),
globalSettings,
svcs.GetRequiredService<IdentityServerOptions>())
);
return services;
}
} }
} }

View File

@ -1,4 +1,4 @@
using Bit.Core.Models.Api; using Bit.Core.Models.Api;
using Bit.Core.Models.Table; using Bit.Core.Models.Table;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
@ -153,7 +153,7 @@ namespace Bit.Identity.Controllers
{ {
// Read external identity from the temporary cookie // Read external identity from the temporary cookie
var result = await HttpContext.AuthenticateAsync( var result = await HttpContext.AuthenticateAsync(
IdentityServerConstants.ExternalCookieAuthenticationScheme); Core.AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
if (result?.Succeeded != true) if (result?.Succeeded != true)
{ {
throw new Exception("External authentication error"); throw new Exception("External authentication error");
@ -190,7 +190,7 @@ namespace Bit.Identity.Controllers
}, localSignInProps); }, localSignInProps);
// Delete temporary cookie used during external authentication // Delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); await HttpContext.SignOutAsync(Core.AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
// Retrieve return URL // Retrieve return URL
var returnUrl = result.Properties.Items["return_url"] ?? "~/"; var returnUrl = result.Properties.Items["return_url"] ?? "~/";

View File

@ -81,7 +81,9 @@ namespace Bit.Identity
// Authentication // Authentication
services services
.AddDistributedIdentityServices(globalSettings)
.AddAuthentication() .AddAuthentication()
.AddCookie(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme)
.AddOpenIdConnect("sso", "Single Sign On", options => .AddOpenIdConnect("sso", "Single Sign On", options =>
{ {
options.Authority = globalSettings.BaseServiceUri.InternalSso; options.Authority = globalSettings.BaseServiceUri.InternalSso;
@ -91,8 +93,10 @@ namespace Bit.Identity
options.ClientSecret = globalSettings.OidcIdentityClientKey; options.ClientSecret = globalSettings.OidcIdentityClientKey;
options.ResponseMode = "form_post"; options.ResponseMode = "form_post";
options.SignInScheme = IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignInScheme = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme;
options.ResponseType = "code"; options.ResponseType = "code";
options.SaveTokens = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.Events = new Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents options.Events = new Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents
{ {