public override void OnAuthorization(AuthorizationContext filterContext) { Roles = CalculateRoleNameStringFromFeature(); // MR #321 - force reload of user roles onto IClaimsIdentity KeystoneOpenIDUtilities.AddLocalUserAccountRolesToClaims(HttpRequestStorage.Person, HttpRequestStorage.GetHttpContextUserThroughOwin().Identity); // This ends up making the calls into the RoleProvider base.OnAuthorization(filterContext); }
/// <summary> /// Function required by <see cref="OwinStartupAttribute"/> /// </summary> public void Configuration(IAppBuilder app) { SitkaHttpApplication.Logger.Info("Owin Startup"); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = "Cookies", CookieDomain = CookieDomain, CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager(), CookieName = $"{NeptuneWebConfiguration.KeystoneOpenIDClientId}_{NeptuneWebConfiguration.NeptuneEnvironment.NeptuneEnvironmentType}" }); //Needed (at least in development) to allow Neptune to talk to upgraded keystone ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; //Most of the new openID for mvc pieces came from here https://www.scottbrady91.com/ASPNET/Refreshing-your-Legacy-ASPNET-IdentityServer-Client-Applications app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions { ClientId = NeptuneWebConfiguration.KeystoneOpenIDClientId, Authority = NeptuneWebConfiguration.KeystoneOpenIDUrl, RedirectUri = SitkaRoute <AccountController> .BuildAbsoluteUrlHttpsFromExpression(c => c.LogOn(), NeptuneWebConfiguration.CanonicalHostNameRoot), // this has to match the keystone client redirect uri PostLogoutRedirectUri = $"https://{NeptuneWebConfiguration.CanonicalHostNameRoot}/", // OpenID is super picky about this; url must match what Keystone has EXACTLY (Trailing slash and all) ResponseType = "code", Scope = "openid profile offline_access keystone", UseTokenLifetime = false, SignInAsAuthenticationType = "Cookies", //ClientSecret = NeptuneWebConfiguration.KeystoneOpenIDClientSecret, CallbackPath = new PathString("/Account/LogOn"), RequireHttpsMetadata = false, RedeemCode = true, SaveTokens = true, ResponseMode = "query", Notifications = new OpenIdConnectAuthenticationNotifications { AuthenticationFailed = (context) => { if ((context.Exception.Message.StartsWith("OICE_20004") || context.Exception.Message.Contains("IDX10311"))) { context.SkipToNextMiddleware(); return(Task.FromResult(0)); } return(Task.FromResult(0)); }, SecurityTokenValidated = n => { var claimsIdentity = n.AuthenticationTicket.Identity; claimsIdentity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken)); if (n.ProtocolMessage.Code != null) { claimsIdentity.AddClaim(new Claim("code", n.ProtocolMessage.Code)); } if (n.ProtocolMessage.AccessToken != null) { claimsIdentity.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken)); } //map name claim to default name type claimsIdentity.AddClaim(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", claimsIdentity.FindFirst(KeystoneOpenIDClaimTypes.Name).Value.ToString())); if (claimsIdentity.IsAuthenticated) // we have a token and we can determine the person. { KeystoneOpenIDUtilities.OpenIDClaimHandler(SyncLocalAccountStore, claimsIdentity); } return(Task.FromResult(0)); }, RedirectToIdentityProvider = n => { if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Authentication) { // generate code verifier and code challenge var codeVerifier = CryptoRandom.CreateUniqueId(32); string codeChallenge; using (var sha256 = SHA256.Create()) { var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier)); codeChallenge = Base64Url.Encode(challengeBytes); } // set code_challenge parameter on authorization request n.ProtocolMessage.SetParameter("code_challenge", codeChallenge); n.ProtocolMessage.SetParameter("code_challenge_method", "S256"); // remember code verifier in cookie (adapted from OWIN nonce cookie) // see: https://github.com/scottbrady91/Blog-Example-Classes/blob/master/AspNetFrameworkPkce/ScottBrady91.BlogExampleCode.AspNetPkce/Startup.cs#L85 RememberCodeVerifier(n, codeVerifier); } //https://identityserver.github.io/Documentation/docsv2/overview/mvcGettingStarted.html#adding-logout if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect .OpenIdConnectRequestType.Logout) { var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token"); if (idTokenHint != null) { n.ProtocolMessage.IdTokenHint = idTokenHint.Value; } } return(Task.CompletedTask); }, AuthorizationCodeReceived = n => { // get code verifier from cookie // see: https://github.com/scottbrady91/Blog-Example-Classes/blob/master/AspNetFrameworkPkce/ScottBrady91.BlogExampleCode.AspNetPkce/Startup.cs#L102 var codeVerifier = RetrieveCodeVerifier(n); // attach code_verifier on token request n.TokenEndpointRequest.SetParameter("code_verifier", codeVerifier); return(Task.CompletedTask); } } }); ScheduledBackgroundJobBootstrapper.ConfigureHangfireAndScheduledBackgroundJobs(app); }
/// <summary> /// Function required by <see cref="OwinStartupAttribute" /> /// </summary> public void Configuration(IAppBuilder app) { SitkaHttpApplication.Logger.Info("Owin Startup - Start Configuration"); app.Use((ctx, next) => { // Trying a lock here to prevent sporadic "Collection was modified; enumeration operation may not execute." error. lock (BranchBuildLock) { var branch = app.New(); JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary <string, string>(); // Specify TLS 1.2 for Rest API calls ServicePointManager.Expect100Continue = true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var tenant = GetTenantFromUrl(ctx.Request); HttpRequestStorage.Tenant = tenant; if (!tenant.TenantEnabled) { throw new SitkaDisplayErrorException($"Tenant {tenant.TenantName} is disabled; cannot show site"); } var canonicalHostNameForEnvironment = FirmaWebConfiguration.FirmaEnvironment.GetCanonicalHostNameForEnvironment(tenant); var tenantAttributes = MultiTenantHelpers.GetTenantAttributeFromCache(); var cookieSecureOption = FirmaWebConfiguration.RedirectToHttps ? CookieSecureOption.Always : CookieSecureOption.Never; branch.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = CookieAuthenticationType, CookieManager = new SystemWebChunkingCookieManager(), CookieName = ClaimsIdentityHelper.GetAuthenticationApplicationCookieName(tenant), CookieSecure = cookieSecureOption }); switch (FirmaWebConfiguration.AuthenticationType) { case AuthenticationType.KeystoneAuth: branch.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions { Authority = FirmaWebConfiguration.KeystoneOpenIDUrl, ResponseType = "id_token token", Scope = "openid all_claims keystone", UseTokenLifetime = false, SignInAsAuthenticationType = CookieAuthenticationType, CallbackPath = new PathString("/Account/LogOn"), ClientId = tenantAttributes.KeystoneOpenIDClientIdentifier, ClientSecret = tenantAttributes.KeystoneOpenIDClientSecret, RedirectUri = $"https://{canonicalHostNameForEnvironment}/Account/LogOn", // this has to match the keystone client redirect uri PostLogoutRedirectUri = $"https://{canonicalHostNameForEnvironment}/", // OpenID is super picky about this; url must match what Keystone has EXACTLY (Trailing slash and all) Notifications = new OpenIdConnectAuthenticationNotifications { AuthenticationFailed = (context) => { SitkaHttpApplication.Logger.Info($"Owin Startup - Configuration - AuthenticationFailed AuthType:{FirmaWebConfiguration.AuthenticationType}"); if ((context.Exception.Message.StartsWith("OICE_20004") || context.Exception.Message.Contains("IDX10311"))) { context.SkipToNextMiddleware(); return(Task.FromResult(0)); } return(Task.FromResult(0)); }, SecurityTokenValidated = n => { HttpRequestStorage.Tenant = GetTenantFromUrl(n.Request); SitkaHttpApplication.Logger.Info( $"In SecurityTokenValidated: TenantID {HttpRequestStorage.Tenant.TenantID}, Url: {n.Request.Uri.ToString()}, AuthType:{FirmaWebConfiguration.AuthenticationType}"); var claimsIdentity = n.AuthenticationTicket.Identity; claimsIdentity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken)); if (n.ProtocolMessage.Code != null) { claimsIdentity.AddClaim(new Claim("code", n.ProtocolMessage.Code)); } if (n.ProtocolMessage.AccessToken != null) { claimsIdentity.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken)); } //map name claim to default name type claimsIdentity.AddClaim(new Claim( "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", claimsIdentity.FindFirst(KeystoneOpenIDClaimTypes.Name).Value.ToString())); if (claimsIdentity.IsAuthenticated) // we have a token and we can determine the person. { KeystoneOpenIDUtilities.OpenIDClaimHandler(SyncLocalAccountStore, claimsIdentity); } return(Task.FromResult(0)); }, RedirectToIdentityProvider = n => { SitkaHttpApplication.Logger.Info($"Owin Startup - Configuration - RedirectToIdentityProvider AuthType:{FirmaWebConfiguration.AuthenticationType}, RequestType:{n.ProtocolMessage.RequestType}"); //n.ProtocolMessage.RedirectUri = GetHomePage(); // dynamic home page for multiple subdomains //n.ProtocolMessage.PostLogoutRedirectUri = GetOuterPage(); // dynamic landing page for multiple subdomains if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest) { var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token"); if (idTokenHint != null) { n.ProtocolMessage.IdTokenHint = idTokenHint.Value; } } else if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.AuthenticationRequest) { var httpContextBase = GetHttpContext(n); var referrer = httpContextBase.Request.UrlReferrer; if (referrer != null && referrer.Host == canonicalHostNameForEnvironment) { n.Response.Cookies.Append("ReturnURL", referrer.PathAndQuery); } } return(Task.FromResult(0)); } } }); break; case AuthenticationType.LocalAuth: break; default: throw new ArgumentOutOfRangeException(); } // we have to do this per tenant so needs to belong here branch.UseHangfireDashboard("/hangfire", new DashboardOptions { Authorization = new[] { new HangfireFirmaWebAuthorizationFilter() } }); return(branch.Build()(ctx.Environment)); } }); ScheduledBackgroundJobBootstrapper.ConfigureHangfireAndScheduledBackgroundJobs(app); }