diff --git a/Configuration/IdentityServerConfig.cs b/Configuration/IdentityServerConfig.cs index b8dfb64..ff9577c 100644 --- a/Configuration/IdentityServerConfig.cs +++ b/Configuration/IdentityServerConfig.cs @@ -12,6 +12,7 @@ namespace Teknik.Configuration public string ClientSecret { get; set; } public List RedirectUris { get; set; } public List PostLogoutRedirectUris { get; set; } + public List AllowedCorsOrigins { get; set; } public string APIName { get; set; } public string APISecret { get; set; } @@ -23,6 +24,7 @@ namespace Teknik.Configuration ClientSecret = "mysecret"; RedirectUris = new List(); PostLogoutRedirectUris = new List(); + AllowedCorsOrigins = new List(); APIName = "api"; APISecret = "secret"; } diff --git a/IdentityServer/Controllers/AccountController.cs b/IdentityServer/Controllers/AccountController.cs index 03594f5..2b95890 100644 --- a/IdentityServer/Controllers/AccountController.cs +++ b/IdentityServer/Controllers/AccountController.cs @@ -295,7 +295,18 @@ namespace Teknik.IdentityServer.Controllers } return View("LoggedOut", vm); - return RedirectToLocal(model.ReturnURL); + } + + [HttpOptions] + public async Task Logout() + { + if (User?.Identity.IsAuthenticated == true) + { + await _signInManager.SignOutAsync(); + + // raise the logout event + await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); + } } private IActionResult RedirectToLocal(string returnUrl) diff --git a/IdentityServer/Controllers/ManageController.cs b/IdentityServer/Controllers/ManageController.cs index 0aef175..29a8b3a 100644 --- a/IdentityServer/Controllers/ManageController.cs +++ b/IdentityServer/Controllers/ManageController.cs @@ -69,7 +69,7 @@ namespace Teknik.IdentityServer.Controllers } [HttpPost] - public async Task DeleteUser(DeleteUserModel model) + public async Task DeleteUser(DeleteUserModel model, [FromServices] ConfigurationDbContext configContext) { if (string.IsNullOrEmpty(model.Username)) return new JsonResult(new { success = false, message = "Username is required" }); @@ -77,6 +77,18 @@ namespace Teknik.IdentityServer.Controllers var foundUser = await _userManager.FindByNameAsync(model.Username); if (foundUser != null) { + // Find this user's clients + var foundClients = configContext.Clients.Where(c => + c.Properties.Exists(p => + p.Key == "username" && + p.Value.ToLower() == model.Username.ToLower()) + ).ToList(); + if (foundClients != null) + { + configContext.Clients.RemoveRange(foundClients); + configContext.SaveChanges(); + } + var result = await _userManager.DeleteAsync(foundUser); if (result.Succeeded) return new JsonResult(new { success = true }); @@ -467,6 +479,10 @@ namespace Teknik.IdentityServer.Controllers var clientSecret = StringHelper.RandomString(40, "abcdefghjkmnpqrstuvwxyz1234567890"); + // Generate the origin for the callback + Uri redirect = new Uri(model.CallbackUrl); + string origin = redirect.Scheme + "://" + redirect.Host; + var client = new IdentityServer4.Models.Client { Properties = new Dictionary() @@ -495,6 +511,11 @@ namespace Teknik.IdentityServer.Controllers model.CallbackUrl }, + AllowedCorsOrigins = + { + origin + }, + AllowedScopes = model.AllowedScopes, AllowOfflineAccess = true @@ -530,6 +551,22 @@ namespace Teknik.IdentityServer.Controllers newUri.RedirectUri = model.CallbackUrl; configContext.Add(newUri); + // Generate the origin for the callback + Uri redirect = new Uri(model.CallbackUrl); + string origin = redirect.Scheme + "://" + redirect.Host; + + // Update the allowed origin for this client + var corsOrigins = configContext.Set().Where(c => c.ClientId == foundClient.Id).ToList(); + if (corsOrigins != null) + { + configContext.RemoveRange(corsOrigins); + } + var newOrigin = new ClientCorsOrigin(); + newOrigin.Client = foundClient; + newOrigin.ClientId = foundClient.Id; + newOrigin.Origin = origin; + configContext.Add(newUri); + // Save all the changed configContext.SaveChanges(); diff --git a/IdentityServer/IdentityServer.csproj b/IdentityServer/IdentityServer.csproj index 2273dbb..b209ac9 100644 --- a/IdentityServer/IdentityServer.csproj +++ b/IdentityServer/IdentityServer.csproj @@ -32,6 +32,13 @@ + + + Never + PreserveNewest + + + PreserveNewest diff --git a/IdentityServer/Middleware/CSPMiddleware.cs b/IdentityServer/Middleware/CSPMiddleware.cs index fb22ebd..b49e83c 100644 --- a/IdentityServer/Middleware/CSPMiddleware.cs +++ b/IdentityServer/Middleware/CSPMiddleware.cs @@ -31,23 +31,24 @@ namespace Teknik.IdentityServer.Middleware if (!string.IsNullOrEmpty(host)) { - allowedDomain = host; - } + string domain = host.GetDomain(); + allowedDomain = string.Format("*.{0} {0}", domain); + } + var csp = string.Format( - "default-src 'none'; " + - "script-src blob: 'unsafe-eval' 'nonce-{1}' {0}; " + + "default-src 'self'; " + + "script-src blob: 'unsafe-eval' 'unsafe-inline' {0}; " + "style-src 'unsafe-inline' {0}; " + "img-src data: *; " + "font-src data: {0}; " + "connect-src wss: blob: data: {0}; " + "media-src *; " + "worker-src blob: mediastream: {0}; " + - "form-action {0}; " + + "form-action *; " + "base-uri {0}; " + "frame-ancestors {0};", - allowedDomain, - httpContext.Items[Constants.NONCE_KEY]); + allowedDomain); if (!httpContext.Response.Headers.ContainsKey("Content-Security-Policy")) { diff --git a/IdentityServer/Middleware/SecurityHeadersMiddleware.cs b/IdentityServer/Middleware/SecurityHeadersMiddleware.cs index dd88b3a..5b305f5 100644 --- a/IdentityServer/Middleware/SecurityHeadersMiddleware.cs +++ b/IdentityServer/Middleware/SecurityHeadersMiddleware.cs @@ -35,14 +35,6 @@ namespace Teknik.IdentityServer.Middleware // Content Type Options headers.Append("X-Content-Type-Options", "nosniff"); - // Public Key Pinning - string keys = string.Empty; - foreach (string key in config.PublicKeys) - { - keys += $"pin-sha256=\"{key}\";"; - } - headers.Append("Public-Key-Pins", $"max-age=300; includeSubDomains; {keys}"); - // Referrer Policy headers.Append("Referrer-Policy", "no-referrer, strict-origin-when-cross-origin"); diff --git a/IdentityServer/Properties/PublishProfiles/Teknik Identity Development.pubxml b/IdentityServer/Properties/PublishProfiles/Teknik Identity Development.pubxml index 57af2fd..991d392 100644 --- a/IdentityServer/Properties/PublishProfiles/Teknik Identity Development.pubxml +++ b/IdentityServer/Properties/PublishProfiles/Teknik Identity Development.pubxml @@ -10,7 +10,7 @@ by editing this MSBuild file. In order to learn more about this please visit htt Any CPU https://authdev.teknik.io True - False + True netcoreapp2.1 05842e03-223a-4f43-9e81-d968a9475a97 false diff --git a/IdentityServer/Startup.cs b/IdentityServer/Startup.cs index 44248fc..47d2064 100644 --- a/IdentityServer/Startup.cs +++ b/IdentityServer/Startup.cs @@ -23,6 +23,7 @@ using Teknik.Logging; using Microsoft.AspNetCore.Authorization; using Teknik.IdentityServer.Models; using IdentityServer4.Services; +using System.Collections.Generic; namespace Teknik.IdentityServer { @@ -108,6 +109,10 @@ namespace Teknik.IdentityServer options.Events.RaiseSuccessEvents = true; options.UserInteraction.ErrorUrl = "/Error/IdentityError"; options.UserInteraction.ErrorIdParameter = "errorId"; + options.Cors.CorsPaths.Add(new PathString("/connect/authorize")); + options.Cors.CorsPaths.Add(new PathString("/connect/endsession")); + options.Cors.CorsPaths.Add(new PathString("/connect/checksession")); + options.Cors.CorsPaths.Add(new PathString("/connect/introspect")); }) .AddOperationalStore(options => options.ConfigureDbContext = builder => diff --git a/Teknik/Areas/User/Controllers/UserController.cs b/Teknik/Areas/User/Controllers/UserController.cs index f7271db..9ed5084 100644 --- a/Teknik/Areas/User/Controllers/UserController.cs +++ b/Teknik/Areas/User/Controllers/UserController.cs @@ -1208,6 +1208,8 @@ namespace Teknik.Areas.Users.Controllers return Json(new { error = "You must enter a client name" }); if (string.IsNullOrEmpty(callbackUrl)) return Json(new { error = "You must enter an authorization callback URL" }); + if (!callbackUrl.IsValidUrl()) + return Json(new { error = "Invalid callback URL" }); // Validate the code with the identity server var result = await IdentityHelper.CreateClient(_config, User.Identity.Name, name, homepageUrl, logoUrl, callbackUrl, "openid", "role", "account-info", "security-info", "teknik-api.read", "teknik-api.write"); @@ -1267,6 +1269,8 @@ namespace Teknik.Areas.Users.Controllers return Json(new { error = "You must enter a client name" }); if (string.IsNullOrEmpty(callbackUrl)) return Json(new { error = "You must enter an authorization callback URL" }); + if (!callbackUrl.IsValidUrl()) + return Json(new { error = "Invalid callback URL" }); Client foundClient = await IdentityHelper.GetClient(_config, User.Identity.Name, clientId); diff --git a/Teknik/Areas/User/Views/User/Settings/Settings.cshtml b/Teknik/Areas/User/Views/User/Settings/Settings.cshtml index 16819aa..ffe6b07 100644 --- a/Teknik/Areas/User/Views/User/Settings/Settings.cshtml +++ b/Teknik/Areas/User/Views/User/Settings/Settings.cshtml @@ -42,6 +42,4 @@ } - - - \ No newline at end of file + \ No newline at end of file diff --git a/Teknik/Middleware/SecurityHeadersMiddleware.cs b/Teknik/Middleware/SecurityHeadersMiddleware.cs index 29a666c..3e7706b 100644 --- a/Teknik/Middleware/SecurityHeadersMiddleware.cs +++ b/Teknik/Middleware/SecurityHeadersMiddleware.cs @@ -35,14 +35,6 @@ namespace Teknik.Middleware // Content Type Options headers.Append("X-Content-Type-Options", "nosniff"); - // Public Key Pinning - string keys = string.Empty; - foreach (string key in config.PublicKeys) - { - keys += $"pin-sha256=\"{key}\";"; - } - headers.Append("Public-Key-Pins", $"max-age=300; includeSubDomains; {keys}"); - // Referrer Policy headers.Append("Referrer-Policy", "no-referrer, strict-origin-when-cross-origin"); diff --git a/Teknik/Properties/launchSettings.json b/Teknik/Properties/launchSettings.json index 8f26730..9f1fe79 100644 --- a/Teknik/Properties/launchSettings.json +++ b/Teknik/Properties/launchSettings.json @@ -3,7 +3,7 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:23818", + "applicationUrl": "https://localhost:23818", "sslPort": 44362 } }, @@ -21,7 +21,7 @@ "launchBrowser": true, "launchUrl": "?sub=www", "environmentVariables": { - "ASPNETCORE_URLS": "http://localhost:5050", + "ASPNETCORE_URLS": "https://localhost:5050", "ASPNETCORE_ENVIRONMENT": "Development" } }, @@ -30,7 +30,7 @@ "launchBrowser": true, "launchUrl": "?sub=www", "environmentVariables": { - "ASPNETCORE_URLS": "http://localhost:5050", + "ASPNETCORE_URLS": "https://localhost:5050", "ASPNETCORE_ENVIRONMENT": "Production" } } diff --git a/Teknik/Scripts/User/AccountSettings.js b/Teknik/Scripts/User/AccountSettings.js index 13ac379..99a0c02 100644 --- a/Teknik/Scripts/User/AccountSettings.js +++ b/Teknik/Scripts/User/AccountSettings.js @@ -44,14 +44,8 @@ $(document).ready(function () { type: "POST", url: deleteUserURL, data: AddAntiForgeryToken({}), - success: function (response) { - if (response.result) { - window.location.replace(homeUrl); - } - else { - $("#top_msg").css('display', 'inline', 'important'); - $("#top_msg").html('
' + parseErrorMessage(response) + '
'); - } + success: function () { + window.location.replace(homeUrl); }, error: function (response) { $("#top_msg").css('display', 'inline', 'important'); diff --git a/Teknik/Startup.cs b/Teknik/Startup.cs index b917eaf..81126c5 100644 --- a/Teknik/Startup.cs +++ b/Teknik/Startup.cs @@ -77,6 +77,11 @@ namespace Teknik // Resolve the services from the service provider var config = sp.GetService(); + if (config.DevEnvironment) + { + Environment.EnvironmentName = EnvironmentName.Development; + } + // Add Tracking Filter scopes //services.AddScoped(); //services.AddScoped(); @@ -264,13 +269,6 @@ namespace Teknik // Use Exception Handling app.UseErrorHandler(config); - if (env.IsDevelopment()) - { - app.UseBrowserLink(); - //app.UseDeveloperExceptionPage(); - app.UseDatabaseErrorPage(); - } - // Performance Monitor the entire request app.UsePerformanceMonitor(); diff --git a/Utilities/UrlExtensions.cs b/Utilities/UrlExtensions.cs index dd379f9..8a4e419 100644 --- a/Utilities/UrlExtensions.cs +++ b/Utilities/UrlExtensions.cs @@ -49,12 +49,19 @@ namespace Teknik.Utilities // If the param is not being used, we will use the curSub if (string.IsNullOrEmpty(subParam)) { - // If we are on dev and no subparam, we need to set the subparam to the specified sub - subParam = (curSub == "dev") ? sub : string.Empty; - string firstSub = (curSub == "dev") ? "dev" : sub; - if (!string.IsNullOrEmpty(firstSub)) + if (url.ActionContext.HttpContext.Request.IsLocal()) { - domain = firstSub + "." + domain; + subParam = sub; + } + else + { + // If we are on dev and no subparam, we need to set the subparam to the specified sub + subParam = (curSub == "dev") ? sub : string.Empty; + string firstSub = (curSub == "dev") ? "dev" : sub; + if (!string.IsNullOrEmpty(firstSub)) + { + domain = firstSub + "." + domain; + } } } else