private async Task <SamlMessage> SamlMessageFromRequest() { var samlMessage = new SamlMessage(null, Context, Options.Configuration); // 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; } var 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. samlMessage = new SamlMessage(form, Context, Options.Configuration); } return(samlMessage); }
/// <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; } 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 = !string.IsNullOrWhiteSpace(Options.RedirectAfterLogin) ? Options.RedirectAfterLogin : currentUri; if (_logger.IsEnabled(TraceEventType.Verbose)) { _logger.WriteVerbose(string.Format("Setting the RedirectUri to {0}.", properties.RedirectUri)); } } SamlMessage samlMessage = await SamlMessageFromRequest(); var notification = new RedirectToIdentityProviderNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options) { ProtocolMessage = samlMessage }; await Options.Notifications.RedirectToIdentityProvider(notification); if (!notification.HandledResponse) { string redirectUri = notification.ProtocolMessage.BuildRedirectUrl(); if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute)) { _logger.WriteWarning("The sign-in redirect URI is malformed: " + redirectUri); } Response.Redirect(redirectUri); } }
/// <summary> /// Handles Signout /// </summary> /// <returns></returns> protected override async Task ApplyResponseGrantAsync() { AuthenticationResponseRevoke signout = Helper.LookupSignOut(Options.AuthenticationType, Options.AuthenticationMode); if (signout == null) { return; } SamlMessage SamlMessage = await SamlMessageFromRequest(); // WS Fed was "TokenAddress". Not sure this is the right endpoint SamlMessage.IssuerAddress = Options.Configuration.ServiceProvider.Endpoints.DefaultLogoutEndpoint.RedirectUrl ?? string.Empty; SamlMessage.Reply = string.Empty; // Set Wreply in order: // 1. properties.Redirect // 2. Options.SignOutWreply // 3. Options.Wreply AuthenticationProperties properties = signout.Properties; if (properties != null && !string.IsNullOrEmpty(properties.RedirectUri)) { SamlMessage.Reply = properties.RedirectUri; } else if (!string.IsNullOrWhiteSpace(Options.Configuration.ServiceProvider.Endpoints.DefaultLogoutEndpoint.RedirectUrl)) { SamlMessage.Reply = Options.Configuration.ServiceProvider.Endpoints.DefaultLogoutEndpoint.RedirectUrl; } var notification = new RedirectToIdentityProviderNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options) { ProtocolMessage = SamlMessage }; await Options.Notifications.RedirectToIdentityProvider(notification); if (!notification.HandledResponse) { string redirectUri = notification.ProtocolMessage.BuildSignOutRedirectUrl(); if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute)) { _logger.WriteWarning("The sign-out redirect URI is malformed: " + redirectUri); } if (Context.Request.IsAjaxRequest()) { Response.Headers.Add("X-Location", new[] { redirectUri }); } else { 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; } 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; } SamlMessage SamlMessage = await SamlMessageFromRequest(); { //IssuerAddress = _configuration.TokenEndpoint ?? string.Empty, //Wtrealm = Options.Wtrealm, //Wctx = SamlAuthenticationDefaults.WctxKey + "=" + Uri.EscapeDataString(Options.StateDataFormat.Protect(properties)), //Wa = SamlActions.SignIn, }; //if (!string.IsNullOrWhiteSpace(Options.Wreply)) //{ // SamlMessage.Wreply = Options.Wreply; //} var notification = new RedirectToIdentityProviderNotification <SamlMessage, SamlAuthenticationOptions>(Context, Options) { ProtocolMessage = SamlMessage }; await Options.Notifications.RedirectToIdentityProvider(notification); if (!notification.HandledResponse) { string redirectUri = notification.ProtocolMessage.BuildRedirectUrl(); if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute)) { _logger.WriteWarning("The sign-in redirect URI is malformed: " + redirectUri); } Response.Redirect(redirectUri); } }
/// <summary> /// Invokes the login procedure (2nd leg of SP-Initiated login). Analagous to Saml20SignonHandler from ASP.Net DLL /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task <AuthenticationTicket> Invoke(IOwinContext context) { Logger.Debug(TraceMessages.SignOnHandlerCalled); ExceptionDispatchInfo authFailedEx = null; try { var messageReceivedNotification = new MessageReceivedNotification <SamlMessage, SamlAuthenticationOptions>(context, options) { ProtocolMessage = new SamlMessage(context, configuration, null) }; await options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return(null); // GetHandledResponseTicket() } if (messageReceivedNotification.Skipped) { return(null); } var requestParams = await HandleResponse(context); var assertion = context.Get <Saml20Assertion>("Saml2:assertion"); var securityTokenReceivedNotification = new SecurityTokenReceivedNotification <SamlMessage, SamlAuthenticationOptions>(context, options) { ProtocolMessage = new SamlMessage(context, configuration, assertion) }; await options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return(null); // GetHandledResponseTicket(); } if (securityTokenReceivedNotification.Skipped) { return(null); } var ticket = await GetAuthenticationTicket(context, requestParams); var securityTokenValidatedNotification = new SecurityTokenValidatedNotification <SamlMessage, SamlAuthenticationOptions>(context, options) { AuthenticationTicket = ticket, ProtocolMessage = new SamlMessage(context, configuration, assertion) }; await options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return(null); // GetHandledResponseTicket(); } if (securityTokenValidatedNotification.Skipped) { return(null); // null; } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(ticket.Identity, ticket.Properties); return(ticket); } catch (Exception ex) { var endpointException = new SamlEndpointException("Error during login request, see inner exception", ex); authFailedEx = ExceptionDispatchInfo.Capture(endpointException); } if (authFailedEx != null) { Logger.Error("Exception occurred while processing message: " + authFailedEx.SourceException); var message = new SamlMessage(context, configuration, context.Get <Saml20Assertion>("Saml2:assertion")); var authenticationFailedNotification = new AuthenticationFailedNotification <SamlMessage, SamlAuthenticationOptions>(context, options) { ProtocolMessage = message, Exception = authFailedEx.SourceException }; await options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return(null);//GetHandledResponseTicket(); } if (authenticationFailedNotification.Skipped) { return(null); //null } authFailedEx.Throw(); } return(null); }
/// <summary> /// Invokes the login procedure (2nd leg of SP-Initiated login). Analagous to Saml20SignonHandler from ASP.Net DLL /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task<AuthenticationTicket> Invoke(IOwinContext context) { Logger.Debug(TraceMessages.SignOnHandlerCalled); ExceptionDispatchInfo authFailedEx = null; try { var messageReceivedNotification = new MessageReceivedNotification<SamlMessage, SamlAuthenticationOptions>(context, options) { ProtocolMessage = new SamlMessage(context, configuration, null) }; await options.Notifications.MessageReceived(messageReceivedNotification); if (messageReceivedNotification.HandledResponse) { return null; // GetHandledResponseTicket() } if (messageReceivedNotification.Skipped) { return null; } var requestParams = await HandleResponse(context); var assertion = context.Get<Saml20Assertion>("Saml2:assertion"); var securityTokenReceivedNotification = new SecurityTokenReceivedNotification<SamlMessage, SamlAuthenticationOptions>(context, options) { ProtocolMessage = new SamlMessage(context, configuration, assertion) }; await options.Notifications.SecurityTokenReceived(securityTokenReceivedNotification); if (securityTokenReceivedNotification.HandledResponse) { return null; // GetHandledResponseTicket(); } if (securityTokenReceivedNotification.Skipped) { return null; } var ticket = await GetAuthenticationTicket(context, requestParams); var securityTokenValidatedNotification = new SecurityTokenValidatedNotification<SamlMessage, SamlAuthenticationOptions>(context, options) { AuthenticationTicket = ticket, ProtocolMessage = new SamlMessage(context, configuration, assertion) }; await options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification); if (securityTokenValidatedNotification.HandledResponse) { return null; // GetHandledResponseTicket(); } if (securityTokenValidatedNotification.Skipped) { return null; // null; } // Flow possible changes ticket = securityTokenValidatedNotification.AuthenticationTicket; context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(ticket.Identity, ticket.Properties); return ticket; } catch (Exception ex) { authFailedEx = ExceptionDispatchInfo.Capture(ex); } if (authFailedEx != null) { Logger.Error("Exception occurred while processing message: " + authFailedEx.SourceException); var message = new SamlMessage(context, configuration, context.Get<Saml20Assertion>("Saml2:assertion")); var authenticationFailedNotification = new AuthenticationFailedNotification<SamlMessage, SamlAuthenticationOptions>(context, options) { ProtocolMessage = message, Exception = authFailedEx.SourceException }; await options.Notifications.AuthenticationFailed(authenticationFailedNotification); if (authenticationFailedNotification.HandledResponse) { return null;//GetHandledResponseTicket(); } if (authenticationFailedNotification.Skipped) { return null; //null } authFailedEx.Throw(); } return null; }