From 38728143d8cb5d948e168d8c5ba56e688e26314c Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Fri, 28 Aug 2020 13:32:15 -0400 Subject: [PATCH] Added static client store (#899) --- src/Core/IdentityServer/ApiClient.cs | 79 ++++++++++++ src/Core/IdentityServer/ClientStore.cs | 26 ++-- src/Core/IdentityServer/OidcIdentityClient.cs | 26 ++++ src/Core/IdentityServer/StaticClientStore.cs | 24 ++++ src/Core/IdentityServer/StaticClients.cs | 115 ------------------ src/Identity/Startup.cs | 1 + 6 files changed, 148 insertions(+), 123 deletions(-) create mode 100644 src/Core/IdentityServer/ApiClient.cs create mode 100644 src/Core/IdentityServer/OidcIdentityClient.cs create mode 100644 src/Core/IdentityServer/StaticClientStore.cs delete mode 100644 src/Core/IdentityServer/StaticClients.cs diff --git a/src/Core/IdentityServer/ApiClient.cs b/src/Core/IdentityServer/ApiClient.cs new file mode 100644 index 000000000..db859de4b --- /dev/null +++ b/src/Core/IdentityServer/ApiClient.cs @@ -0,0 +1,79 @@ +using IdentityServer4.Models; +using System.Collections.Generic; +using System.Linq; + +namespace Bit.Core.IdentityServer +{ + public class ApiClient : Client + { + public ApiClient( + GlobalSettings globalSettings, + string id, + int refreshTokenSlidingDays, + int accessTokenLifetimeHours, + string[] scopes = null) + { + ClientId = id; + AllowedGrantTypes = new[] { GrantType.ResourceOwnerPassword, GrantType.AuthorizationCode }; + RefreshTokenExpiration = TokenExpiration.Sliding; + RefreshTokenUsage = TokenUsage.ReUse; + SlidingRefreshTokenLifetime = 86400 * refreshTokenSlidingDays; + AbsoluteRefreshTokenLifetime = 0; // forever + UpdateAccessTokenClaimsOnRefresh = true; + AccessTokenLifetime = 3600 * accessTokenLifetimeHours; + AllowOfflineAccess = true; + + RequireConsent = false; + RequirePkce = true; + RequireClientSecret = false; + if (id == "web") + { + RedirectUris = new[] { $"{globalSettings.BaseServiceUri.Vault}/sso-connector.html" }; + PostLogoutRedirectUris = new[] { globalSettings.BaseServiceUri.Vault }; + AllowedCorsOrigins = new[] { globalSettings.BaseServiceUri.Vault }; + } + else if (id == "desktop") + { + RedirectUris = new[] { "bitwarden://sso-callback" }; + PostLogoutRedirectUris = new[] { "bitwarden://logged-out" }; + } + else if (id == "connector") + { + var connectorUris = new List(); + for (var port = 8065; port <= 8070; port++) + { + connectorUris.Add(string.Format("http://localhost:{0}", port)); + } + RedirectUris = connectorUris.Append("bwdc://sso-callback").ToList(); + PostLogoutRedirectUris = connectorUris.Append("bwdc://logged-out").ToList(); + } + else if (id == "browser") + { + RedirectUris = new[] { $"{globalSettings.BaseServiceUri.Vault}/sso-connector.html" }; + PostLogoutRedirectUris = new[] { globalSettings.BaseServiceUri.Vault }; + AllowedCorsOrigins = new[] { globalSettings.BaseServiceUri.Vault }; + } + else if (id == "cli") + { + var cliUris = new List(); + for (var port = 8065; port <= 8070; port++) + { + cliUris.Add(string.Format("http://localhost:{0}", port)); + } + RedirectUris = cliUris; + PostLogoutRedirectUris = cliUris; + } + else if (id == "mobile") + { + RedirectUris = new[] { "bitwarden://sso-callback" }; + PostLogoutRedirectUris = new[] { "bitwarden://logged-out" }; + } + + if (scopes == null) + { + scopes = new string[] { "api" }; + } + AllowedScopes = scopes; + } + } +} diff --git a/src/Core/IdentityServer/ClientStore.cs b/src/Core/IdentityServer/ClientStore.cs index 2005cebf7..bc58d73b2 100644 --- a/src/Core/IdentityServer/ClientStore.cs +++ b/src/Core/IdentityServer/ClientStore.cs @@ -4,7 +4,6 @@ using IdentityServer4.Models; using System.Collections.Generic; using Bit.Core.Repositories; using System; -using System.Security.Claims; using IdentityModel; using Bit.Core.Utilities; @@ -12,20 +11,21 @@ namespace Bit.Core.IdentityServer { public class ClientStore : IClientStore { - private static IDictionary _apiClients = StaticClients.GetApiClients(); - private readonly IInstallationRepository _installationRepository; private readonly IOrganizationRepository _organizationRepository; private readonly GlobalSettings _globalSettings; + private readonly StaticClientStore _staticClientStore; public ClientStore( IInstallationRepository installationRepository, IOrganizationRepository organizationRepository, - GlobalSettings globalSettings) + GlobalSettings globalSettings, + StaticClientStore staticClientStore) { _installationRepository = installationRepository; _organizationRepository = organizationRepository; _globalSettings = globalSettings; + _staticClientStore = staticClientStore; } public async Task FindClientByIdAsync(string clientId) @@ -47,7 +47,10 @@ namespace Bit.Core.IdentityServer AllowedGrantTypes = GrantTypes.ClientCredentials, AccessTokenLifetime = 3600 * 24, Enabled = installation.Enabled, - Claims = new List { new ClientClaim(JwtClaimTypes.Subject, installation.Id.ToString()) } + Claims = new List + { + new ClientClaim(JwtClaimTypes.Subject, installation.Id.ToString()) + } }; } } @@ -70,7 +73,10 @@ namespace Bit.Core.IdentityServer AllowedGrantTypes = GrantTypes.ClientCredentials, AccessTokenLifetime = 3600 * 24, Enabled = true, - Claims = new List { new ClientClaim(JwtClaimTypes.Subject, id) } + Claims = new List + { + new ClientClaim(JwtClaimTypes.Subject, id) + } }; } } @@ -92,13 +98,17 @@ namespace Bit.Core.IdentityServer AllowedGrantTypes = GrantTypes.ClientCredentials, AccessTokenLifetime = 3600 * 1, Enabled = org.Enabled && org.UseApi, - Claims = new List { new ClientClaim(JwtClaimTypes.Subject, org.Id.ToString()) } + Claims = new List + { + new ClientClaim(JwtClaimTypes.Subject, org.Id.ToString()) + } }; } } } - return _apiClients.ContainsKey(clientId) ? _apiClients[clientId] : null; + return _staticClientStore.ApiClients.ContainsKey(clientId) ? + _staticClientStore.ApiClients[clientId] : null; } } } diff --git a/src/Core/IdentityServer/OidcIdentityClient.cs b/src/Core/IdentityServer/OidcIdentityClient.cs new file mode 100644 index 000000000..464b33df8 --- /dev/null +++ b/src/Core/IdentityServer/OidcIdentityClient.cs @@ -0,0 +1,26 @@ +using IdentityServer4; +using IdentityServer4.Models; +using System.Collections.Generic; + +namespace Bit.Core.IdentityServer +{ + public class OidcIdentityClient : Client + { + public OidcIdentityClient(GlobalSettings globalSettings) + { + ClientId = "oidc-identity"; + RequireClientSecret = true; + RequirePkce = true; + ClientSecrets = new List { new Secret(globalSettings.OidcIdentityClientKey.Sha256()) }; + AllowedScopes = new string[] + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile + }; + AllowedGrantTypes = GrantTypes.Code; + Enabled = true; + RedirectUris = new List { $"{globalSettings.BaseServiceUri.Identity}/signin-oidc" }; + RequireConsent = false; + } + } +} diff --git a/src/Core/IdentityServer/StaticClientStore.cs b/src/Core/IdentityServer/StaticClientStore.cs new file mode 100644 index 000000000..28c554f4f --- /dev/null +++ b/src/Core/IdentityServer/StaticClientStore.cs @@ -0,0 +1,24 @@ +using IdentityServer4.Models; +using System.Collections.Generic; +using System.Linq; + +namespace Bit.Core.IdentityServer +{ + public class StaticClientStore + { + public StaticClientStore(GlobalSettings globalSettings) + { + ApiClients = new List + { + new ApiClient(globalSettings, "mobile", 90, 1), + new ApiClient(globalSettings, "web", 30, 1), + new ApiClient(globalSettings, "browser", 30, 1), + new ApiClient(globalSettings, "desktop", 30, 1), + new ApiClient(globalSettings, "cli", 30, 1), + new ApiClient(globalSettings, "connector", 30, 24) + }.ToDictionary(c => c.ClientId); + } + + public IDictionary ApiClients { get; private set; } + } +} diff --git a/src/Core/IdentityServer/StaticClients.cs b/src/Core/IdentityServer/StaticClients.cs deleted file mode 100644 index 12b536210..000000000 --- a/src/Core/IdentityServer/StaticClients.cs +++ /dev/null @@ -1,115 +0,0 @@ -using IdentityServer4; -using IdentityServer4.Models; -using System.Collections.Generic; -using System.Linq; - -namespace Bit.Core.IdentityServer -{ - public class StaticClients - { - public static IDictionary GetApiClients() - { - return new List - { - new ApiClient("mobile", 90, 1), - new ApiClient("web", 30, 1), - new ApiClient("browser", 30, 1), - new ApiClient("desktop", 30, 1), - new ApiClient("cli", 30, 1), - new ApiClient("connector", 30, 24) - }.ToDictionary(c => c.ClientId); - } - - public class ApiClient : Client - { - public ApiClient( - string id, - int refreshTokenSlidingDays, - int accessTokenLifetimeHours, - string[] scopes = null) - { - ClientId = id; - AllowedGrantTypes = new[] { GrantType.ResourceOwnerPassword, GrantType.AuthorizationCode }; - RefreshTokenExpiration = TokenExpiration.Sliding; - RefreshTokenUsage = TokenUsage.ReUse; - SlidingRefreshTokenLifetime = 86400 * refreshTokenSlidingDays; - AbsoluteRefreshTokenLifetime = 0; // forever - UpdateAccessTokenClaimsOnRefresh = true; - AccessTokenLifetime = 3600 * accessTokenLifetimeHours; - AllowOfflineAccess = true; - - RequireConsent = false; - RequirePkce = true; - RequireClientSecret = false; - if (id == "web") - { - RedirectUris = new[] { "https://localhost:8080/sso-connector.html" }; - PostLogoutRedirectUris = new[] { "https://localhost:8080" }; - AllowedCorsOrigins = new[] { "https://localhost:8080" }; - } - else if (id == "desktop") - { - RedirectUris = new[] { "bitwarden://sso-callback" }; - PostLogoutRedirectUris = new[] { "bitwarden://logged-out" }; - } - else if (id == "connector") - { - var connectorUris = new List(); - for (var port = 8065; port <= 8070; port++) - { - connectorUris.Add(string.Format("http://localhost:{0}", port)); - } - RedirectUris = connectorUris.Append("bwdc://sso-callback").ToList(); - PostLogoutRedirectUris = connectorUris.Append("bwdc://logged-out").ToList(); - } - else if (id == "browser") - { - RedirectUris = new[] { "https://localhost:8080/sso-connector.html" }; - PostLogoutRedirectUris = new[] { "https://localhost:8080" }; - AllowedCorsOrigins = new[] { "https://localhost:8080" }; - } - else if (id == "cli") - { - var cliUris = new List(); - for (var port = 8065; port <= 8070; port++) - { - cliUris.Add(string.Format("http://localhost:{0}", port)); - } - RedirectUris = cliUris; - PostLogoutRedirectUris = cliUris; - } - else if (id == "mobile") - { - RedirectUris = new[] { "bitwarden://sso-callback" }; - PostLogoutRedirectUris = new[] { "bitwarden://logged-out" }; - } - - if (scopes == null) - { - scopes = new string[] { "api" }; - } - AllowedScopes = scopes; - } - } - - public class OidcIdentityClient : Client - { - public OidcIdentityClient(GlobalSettings globalSettings) - { - ClientId = "oidc-identity"; - RequireClientSecret = true; - RequirePkce = true; - ClientSecrets = new List { new Secret(globalSettings.OidcIdentityClientKey.Sha256()) }; - AllowedScopes = new string[] - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile - }; - AllowedGrantTypes = GrantTypes.Code; - Enabled = true; - RedirectUris = new List { $"{globalSettings.BaseServiceUri.Identity}/signin-oidc" }; - RequireConsent = false; - } - } - } -} diff --git a/src/Identity/Startup.cs b/src/Identity/Startup.cs index 8d303ff31..46dffc0b8 100644 --- a/src/Identity/Startup.cs +++ b/src/Identity/Startup.cs @@ -192,6 +192,7 @@ namespace Bit.Identity public static IIdentityServerBuilder AddCustomIdentityServerServices(IServiceCollection services, IWebHostEnvironment env, GlobalSettings globalSettings) { + services.AddSingleton(); services.AddTransient(); var issuerUri = new Uri(globalSettings.BaseServiceUri.InternalIdentity);