/// <summary> /// Invoked to process incoming authentication messages. /// </summary> /// <returns></returns> protected override async Task <AuthenticationTicket> AuthenticateCoreAsync() { // Allow login to be constrained to a specific path. if (Options.CallbackPath.Any() && Options.CallbackPath.All(x => x != (Request.PathBase + Request.Path))) { return(null); } SAML2Message wsFederationMessage = null; // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small. if (string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(Request.ContentType) // May have media/type; charset=utf-8, allow partial match. && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) && Request.Body.CanRead) { if (!Request.Body.CanSeek) { _logger.WriteVerbose("Buffering request body"); // Buffer in case this body was not meant for us. MemoryStream memoryStream = new MemoryStream(); await Request.Body.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); Request.Body = memoryStream; } IFormCollection form = await Request.ReadFormAsync(); Request.Body.Seek(0, SeekOrigin.Begin); // TODO: a delegate on SAMLAuthenticationOptions would allow for users to hook their own custom message. wsFederationMessage = new SAML2Message(form); } //if (wsFederationMessage == null || !wsFederationMessage.IsSignInMessage) //{ // return null; //} // wsFederationMessage.SigningKeys = Options.SigningKeys; ExceptionDispatchInfo authFailedEx = null; try { var messageReceivedNotification = new MessageReceivedNotification <SAML2Message, SAML2AuthenticationOptions>(Context, Options) { ProtocolMessage = wsFederationMessage, }; await Options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (messageReceivedNotification.Skipped) { return(null); } if (wsFederationMessage.Wresult == null) { _logger.WriteWarning("Received a sign-in message without a WResult."); return(null); } var token = wsFederationMessage.GetToken();//Options.SecurityTokenHandlers.ReadToken(wsFederationMessage.Wresult); if (token == null) { _logger.WriteWarning("Received a sign-in message without a token."); return(null); } var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <SAML2Message, SAML2AuthenticationOptions>(Context, Options) { ProtocolMessage = wsFederationMessage }; await Options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (securityTokenReceivedNotification.Skipped) { return(null); } if (_configuration == null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled); } // Copy and augment to avoid cross request race conditions for updated configurations. TokenValidationParameters tvp = Options.TokenValidationParameters.Clone(); IEnumerable <string> issuers = new[] { _configuration.Issuer }; tvp.ValidIssuers = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers)); tvp.IssuerSigningKeys = (tvp.IssuerSigningKeys == null ? _configuration.SigningKeys : tvp.IssuerSigningKeys.Concat(_configuration.SigningKeys)); var principal = new ClaimsPrincipal(token.GetClaims(Options)); // principal = Options.SystemIdentityModelIdentityConfiguration // .ClaimsAuthenticationManager.Authenticate(null, principal); var requestState = token.GetRequestState(Options); if (requestState == null && Options.CallbackPath == null) { throw new Exception("MissingReturnUrlMessage"); } // requestState.RelayData var identities = principal.Identities.Select(i => new ClaimsIdentity(i, null, Options.SignInAsAuthenticationType, i.NameClaimType, i.RoleClaimType)).SingleOrDefault(); var authProperties = requestState != null && requestState.RelayData != null ? (AuthenticationProperties)requestState.RelayData : new AuthenticationProperties(); authProperties.RedirectUri = string.IsNullOrEmpty(wsFederationMessage.RelayState) ? Options.LoginBackUrl : wsFederationMessage.RelayState; return(new AuthenticationTicket(identities, authProperties)); // SecurityToken parsedToken; // ClaimsPrincipal principal = Options.SecurityTokenHandlers.ValidateToken(token, tvp, out parsedToken); // ClaimsIdentity claimsIdentity = principal.Identity as ClaimsIdentity; // Retrieve our cached redirect uri // string state = wsFederationMessage.state; // WsFed allows for uninitiated logins, state may be missing. //AuthenticationProperties properties = GetPropertiesFromWctx(state); //AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, properties); //if (Options.UseTokenLifetime) //{ // // Override any session persistence to match the token lifetime. // DateTime issued = parsedToken.ValidFrom; // if (issued != DateTime.MinValue) // { // ticket.Properties.IssuedUtc = issued.ToUniversalTime(); // } // DateTime expires = parsedToken.ValidTo; // if (expires != DateTime.MinValue) // { // ticket.Properties.ExpiresUtc = expires.ToUniversalTime(); // } // ticket.Properties.AllowRefresh = false; //} //var securityTokenValidatedNotification = new SecurityTokenValidatedNotification<SAML2Message, SAML2AuthenticationOptions>(Context, Options) //{ // AuthenticationTicket = ticket, // ProtocolMessage = wsFederationMessage, //}; //await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); //if (securityTokenValidatedNotification.HandledResponse) //{ // return GetHandledResponseTicket(); //} //if (securityTokenValidatedNotification.Skipped) //{ // return null; //} //// Flow possible changes //ticket = securityTokenValidatedNotification.AuthenticationTicket; //return ticket; } catch (Exception exception) { // We can't await inside a catch block, capture and handle outside. authFailedEx = ExceptionDispatchInfo.Capture(exception); } if (authFailedEx != null) { _logger.WriteError("Exception occurred while processing message: ", authFailedEx.SourceException); // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification. if (Options.RefreshOnIssuerKeyNotFound && authFailedEx.SourceException.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) { Options.ConfigurationManager.RequestRefresh(); } var authenticationFailedNotification = new AuthenticationFailedNotification <SAML2Message, SAML2AuthenticationOptions>(Context, Options) { ProtocolMessage = wsFederationMessage, Exception = authFailedEx.SourceException }; await Options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(GetHandledResponseTicket()); } if (authenticationFailedNotification.Skipped) { return(null); } authFailedEx.Throw(); } return(null); }
///// <summary> ///// Handles Signout ///// </summary> ///// <returns></returns> //protected override async Task ApplyResponseGrantAsync() //{ // AuthenticationResponseRevoke signout = Helper.LookupSignOut(Options.AuthenticationType, Options.AuthenticationMode); // if (signout == null) // { // return; // } // if (_configuration == null) // { // _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled); // } // SAML2Message wsFederationMessage = new SAML2Message() // { // IssuerAddress = _configuration.TokenEndpoint ?? string.Empty, // // Wtrealm = Options.Wtrealm, // //Wa = SAMLActions.SignOut, // }; // // Set Wreply in order: // // 1. properties.Redirect // // 2. Options.SignOutWreply // // 3. Options.Wreply // AuthenticationProperties properties = signout.Properties; // if (properties != null && !string.IsNullOrEmpty(properties.RedirectUri)) // { // // wsFederationMessage.Wreply = properties.RedirectUri; // } // else if (!string.IsNullOrWhiteSpace(Options.SignOutWreply)) // { // // wsFederationMessage.Wreply = Options.SignOutWreply; // } // else if (!string.IsNullOrWhiteSpace(Options.Wreply)) // { // // wsFederationMessage.Wreply = Options.Wreply; // } // var notification = new RedirectToIdentityProviderNotification<SAML2Message, SAML2AuthenticationOptions>(Context, Options) // { // ProtocolMessage = wsFederationMessage // }; // await Options.Notifications.RedirectToIdentityProvider(notification); // if (!notification.HandledResponse) // { // string redirectUri = notification.ProtocolMessage.CreateSignOutUrl(); // if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute)) // { // _logger.WriteWarning("The sign-out redirect URI is malformed: " + redirectUri); // } // Response.Redirect(redirectUri); // } //} /// <summary> /// Handles Challenge /// </summary> /// <returns></returns> protected override async Task ApplyResponseChallengeAsync() { if (Response.StatusCode != 401) { return; } AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode); if (challenge == null) { return; } if (_configuration == null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.Request.CallCancelled); } string baseUri = Request.Scheme + Uri.SchemeDelimiter + Request.Host + Request.PathBase; string currentUri = baseUri + Request.Path + Request.QueryString; // Save the original challenge URI so we can redirect back to it when we're done. AuthenticationProperties properties = challenge.Properties; if (string.IsNullOrEmpty(properties.RedirectUri)) { properties.RedirectUri = currentUri; } SAML2Message wsFederationMessage = new SAML2Message() { IssuerAddress = _configuration.TokenEndpoint ?? string.Empty, // Wtrealm = Options.Wtrealm, // Wctx = SAML2AuthenticationDefaults.WctxKey + "=" + Uri.EscapeDataString(Options.StateDataFormat.Protect(properties)), //Wa = SAMLActions.SignIn, // PartnerSpId = "CN_WeChat_Ent", // IdpAdapterId = "LoginForm", TargetResource = properties.RedirectUri }; if (!string.IsNullOrWhiteSpace(Options.Wreply)) { //wsFederationMessage.Wreply = Options.Wreply; } var notification = new RedirectToIdentityProviderNotification <SAML2Message, SAML2AuthenticationOptions>(Context, Options) { ProtocolMessage = wsFederationMessage }; await Options.Notifications.RedirectToIdentityProvider(notification); if (!notification.HandledResponse) { string redirectUri = notification.ProtocolMessage.CreateSignInUrl(); if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute)) { _logger.WriteWarning("The sign-in redirect URI is malformed: " + redirectUri); } Response.Redirect(redirectUri); } }