private byte[] GetOrCreateSessionId(DotvvmRequestContext context) { if (context == null) throw new ArgumentNullException("context"); if (string.IsNullOrWhiteSpace(context.Configuration.Security.SessionIdCookieName)) throw new FormatException("Configured SessionIdCookieName is missing or empty."); // Get cookie manager var mgr = new ChunkingCookieManager(); // TODO: Make this configurable // Get application key helper var keyHelper = new ApplicationKeyHelper(context.Configuration.Security); // Get cookie value var sidCookieValue = mgr.GetRequestCookie(context.OwinContext, context.Configuration.Security.SessionIdCookieName); if (string.IsNullOrWhiteSpace(sidCookieValue)) { // No SID - generate and protect new one var rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); var sid = new byte[SID_LENGTH]; rng.GetBytes(sid); var protectedSid = keyHelper.ProtectData(sid, KDF_LABEL_SID); // Save to cookie sidCookieValue = Convert.ToBase64String(protectedSid); mgr.AppendResponseCookie( context.OwinContext, context.Configuration.Security.SessionIdCookieName, // Configured cookie name sidCookieValue, // Base64-encoded SID value new Microsoft.Owin.CookieOptions { HttpOnly = true, // Don't allow client script access Secure = context.OwinContext.Request.IsSecure // If request goes trough HTTPS, mark as secure only }); // Return newly generated SID return sid; } else { // Try to read from cookie try { var protectedSid = Convert.FromBase64String(sidCookieValue); var sid = keyHelper.UnprotectData(protectedSid, KDF_LABEL_SID); return sid; } catch (Exception ex) { // Incorrect Base64 formatting of crypto protection error throw new SecurityException("Value of the SessionID cookie is corrupted or has been tampered with.", ex); } } }
public CookieTokenCache(HttpContext context) { this.context = context.Request.GetOwinContext(); this.cookieManager = new ChunkingCookieManager(); this.AfterAccess = AfterAccessNotification; var cookie = this.cookieManager.GetRequestCookie(this.context, CookieName); if (cookie != null) { var state = Convert.FromBase64String(cookie); this.Deserialize(state); } }
public void GetLargeChunkedCookie_Reassembled() { IOwinContext context = new OwinContext(); context.Request.Headers.AppendValues("Cookie", "TestCookie=chunks:7", "TestCookieC1=abcdefghi", "TestCookieC2=jklmnopqr", "TestCookieC3=stuvwxyz0", "TestCookieC4=123456789", "TestCookieC5=ABCDEFGHI", "TestCookieC6=JKLMNOPQR", "TestCookieC7=STUVWXYZ"); string result = new ChunkingCookieManager().GetRequestCookie(context, "TestCookie"); string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; Assert.Equal(testString, result); }
// If the javascript issues an OIDC authorize reuest, and it succeeds, the user is already logged // into AAD. Since the AAD session cookie has changed, we need to check if the same use is still // logged in. public static Task AuthorizationCodeRecieved(AuthorizationCodeReceivedNotification notification) { // If the successful authorize request was issued by the SingleSignOut javascript if (notification.AuthenticationTicket.Properties.RedirectUri.Contains("SessionChanged")) { // Clear the SingleSignOut Cookie ICookieManager cookieManager = new ChunkingCookieManager(); string cookie = cookieManager.GetRequestCookie(notification.OwinContext, CookieName); AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookie); if (ticket.Properties.Dictionary != null) ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.AuthenticationType + "SingleSignOut"] = ""; cookieManager.AppendResponseCookie(notification.OwinContext, CookieName, ticketDataFormat.Protect(ticket), new CookieOptions()); Claim existingUserObjectId = notification.OwinContext.Authentication.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"); Claim incomingUserObjectId = notification.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"); if (existingUserObjectId.Value != null && incomingUserObjectId != null) { // If a different user is logged into AAD if(existingUserObjectId.Value != incomingUserObjectId.Value) { // No need to clear the session state here. It has already been // updated with the new user's session state in SecurityTokenValidated. notification.Response.Redirect("Account/SingleSignOut"); notification.HandleResponse(); } // If the same user is logged into AAD else if (existingUserObjectId.Value == incomingUserObjectId.Value) { // No need to clear the session state, SecurityTokenValidated will do so. // Simply redirect the iframe to a page other than SingleSignOut to reset // the timer in the javascript. notification.Response.Redirect("/"); notification.HandleResponse(); } } } return Task.FromResult<object>(null); }
// If the javascript issues an OIDC authorize request, and it fails (meaning the user needs to login) // this notification will be triggered with the error message 'login_required' public static Task AuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification) { string cookieStateValue = null; ICookieManager cookieManager = new ChunkingCookieManager(); string cookie = cookieManager.GetRequestCookie(notification.OwinContext, CookieName); AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookie); if (ticket.Properties.Dictionary != null) ticket.Properties.Dictionary.TryGetValue(OpenIdConnectAuthenticationDefaults.AuthenticationType + "SingleSignOut", out cookieStateValue); // If the failed authentication was a result of a request by the SingleSignOut javascript if (cookieStateValue != null && cookieStateValue.Contains(notification.ProtocolMessage.State) && notification.Exception.Message == "login_required"); { // Clear the SingleSignOut cookie, and clear the OIDC session state so //that we don't see any further "Session Changed" messages from the iframe. ticket.Properties.Dictionary[OpenIdConnectSessionProperties.SessionState] = ""; ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.AuthenticationType + "SingleSignOut"] = ""; cookieManager.AppendResponseCookie(notification.OwinContext, CookieName, ticketDataFormat.Protect(ticket), new CookieOptions()); notification.Response.Redirect("Account/SingleSignOut"); notification.HandleResponse(); } return Task.FromResult<object>(null); }
public static Task RedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification) { // If a challenge was issued by the SingleSignOut javascript UrlHelper url = new UrlHelper(HttpContext.Current.Request.RequestContext); if (notification.Request.Uri.AbsolutePath == url.Action("SessionChanged", "Account")) { // Store the state in the cookie so we can distinguish OIDC messages that occurred // as a result of the SingleSignOut javascript. ICookieManager cookieManager = new ChunkingCookieManager(); string cookie = cookieManager.GetRequestCookie(notification.OwinContext, CookieName); AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookie); if (ticket.Properties.Dictionary != null) ticket.Properties.Dictionary[OpenIdConnectAuthenticationDefaults.AuthenticationType + "SingleSignOut"] = notification.ProtocolMessage.State; cookieManager.AppendResponseCookie(notification.OwinContext, CookieName, ticketDataFormat.Protect(ticket), new CookieOptions()); // Return prompt=none request (to tenant specific endpoint) to SessionChanged controller. notification.ProtocolMessage.Prompt = "none"; notification.ProtocolMessage.IssuerAddress = notification.OwinContext.Authentication.User.FindFirst("issEndpoint").Value; string redirectUrl = notification.ProtocolMessage.BuildRedirectUrl(); notification.Response.Redirect(url.Action("SessionChanged", "Account") + "?" + redirectUrl); notification.HandleResponse(); } return Task.FromResult<object>(null); }
public void GetLargeChunkedCookieWithQuotes_Reassembled() { IOwinContext context = new OwinContext(); context.Request.Headers.AppendValues("Cookie", "TestCookie=chunks:7", "TestCookieC1=\"abcdefghi\"", "TestCookieC2=\"jklmnopqr\"", "TestCookieC3=\"stuvwxyz0\"", "TestCookieC4=\"123456789\"", "TestCookieC5=\"ABCDEFGHI\"", "TestCookieC6=\"JKLMNOPQR\"", "TestCookieC7=\"STUVWXYZ\""); string result = new ChunkingCookieManager().GetRequestCookie(context, "TestCookie"); string testString = "\"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\""; Assert.Equal(testString, result); }
public void GetLargeChunkedCookieWithMissingChunk_ThrowingDisabled_NotReassembled() { IOwinContext context = new OwinContext(); context.Request.Headers.AppendValues("Cookie", "TestCookie=chunks:7", "TestCookieC1=abcdefghi", // Missing chunk "TestCookieC2=jklmnopqr", "TestCookieC3=stuvwxyz0", "TestCookieC4=123456789", "TestCookieC5=ABCDEFGHI", "TestCookieC6=JKLMNOPQR", "TestCookieC7=STUVWXYZ"); string result = new ChunkingCookieManager() { ThrowForPartialCookies = false }.GetRequestCookie(context, "TestCookie"); string testString = "chunks:7"; Assert.Equal(testString, result); }
private byte[] GetOrCreateSessionId(IDotvvmRequestContext context, bool canGenerate = true) { if (context == null) throw new ArgumentNullException(nameof(context)); var sessionIdCookieName = GetSessionIdCookieName(context); if (string.IsNullOrWhiteSpace(sessionIdCookieName)) throw new FormatException("Configured SessionIdCookieName is missing or empty."); // Get cookie manager var mgr = new ChunkingCookieManager(); // TODO: Make this configurable // Construct protector with purposes var userIdentity = ProtectionHelpers.GetUserIdentity(context); var requestIdentity = ProtectionHelpers.GetRequestIdentity(context); var protector = this.protectionProvider.Create(PURPOSE_SID); // Get cookie value var sidCookieValue = mgr.GetRequestCookie(context.OwinContext, sessionIdCookieName); if (!string.IsNullOrWhiteSpace(sidCookieValue)) { // Try to read from cookie try { var protectedSid = Convert.FromBase64String(sidCookieValue); var sid = protector.Unprotect(protectedSid); return sid; } catch (Exception ex) { // Incorrect Base64 formatting of crypto protection error // Generate new one or thow error if can't if (!canGenerate) throw new SecurityException("Value of the SessionID cookie is corrupted or has been tampered with.", ex); // else suppress error and generate new SID } } // No SID - generate and protect new one if(canGenerate) { var rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); var sid = new byte[SID_LENGTH]; rng.GetBytes(sid); var protectedSid = protector.Protect(sid); // Save to cookie sidCookieValue = Convert.ToBase64String(protectedSid); mgr.AppendResponseCookie( context.OwinContext, sessionIdCookieName, // Configured cookie name sidCookieValue, // Base64-encoded SID value new Microsoft.Owin.CookieOptions { HttpOnly = true, // Don't allow client script access Secure = context.OwinContext.Request.IsSecure // If request goes trough HTTPS, mark as secure only }); // Return newly generated SID return sid; } else { throw new SecurityException("SessionID cookie is missing, so can't verify CSRF token."); } }