public async Task <IActionResult> EndSessionResponseAsync(string partyId) { logger.ScopeTrace(() => "Down, End session response."); logger.SetScopeProperty(Constants.Logs.DownPartyId, partyId); var sequenceData = await sequenceLogic.GetSequenceDataAsync <OidcDownSequenceData>(false); var rpInitiatedLogoutResponse = new RpInitiatedLogoutResponse { State = sequenceData.State, }; logger.ScopeTrace(() => $"End session response '{rpInitiatedLogoutResponse.ToJsonIndented()}'.", traceType: TraceTypes.Message); var nameValueCollection = rpInitiatedLogoutResponse.ToDictionary(); logger.ScopeTrace(() => $"Redirect Uri '{sequenceData.RedirectUri}'."); logger.ScopeTrace(() => "Down, OIDC End session response.", triggerEvent: true); await sequenceLogic.RemoveSequenceDataAsync <OidcDownSequenceData>(); if (sequenceData.RestrictFormAction) { securityHeaderLogic.AddFormAction(sequenceData.RedirectUri); } else { securityHeaderLogic.AddFormActionAllowAll(); } return(await nameValueCollection.ToRedirectResultAsync(sequenceData.RedirectUri)); }
public async Task <IActionResult> EndSessionRequestAsync(string partyId) { logger.ScopeTrace("Up, OIDC End session request."); var oidcUpSequenceData = await sequenceLogic.GetSequenceDataAsync <OidcUpSequenceData>(remove : false); if (!oidcUpSequenceData.UpPartyId.Equals(partyId, StringComparison.Ordinal)) { throw new Exception("Invalid up-party id."); } logger.SetScopeProperty("upPartyId", oidcUpSequenceData.UpPartyId); var party = await tenantRepository.GetAsync <OidcUpParty>(oidcUpSequenceData.UpPartyId); logger.SetScopeProperty("upPartyClientId", party.Client.ClientId); ValidatePartyLogoutSupport(party); var postLogoutRedirectUrl = HttpContext.GetUpPartyUrl(party.Name, Constants.Routes.OAuthController, Constants.Endpoints.EndSessionResponse, partyBindingPattern: party.PartyBindingPattern); var rpInitiatedLogoutRequest = new RpInitiatedLogoutRequest { PostLogoutRedirectUri = postLogoutRedirectUrl, State = SequenceString }; var session = await sessionUpPartyLogic.GetSessionAsync(party); if (session != null) { try { if (!oidcUpSequenceData.SessionId.Equals(session.SessionId, StringComparison.Ordinal)) { throw new Exception("Requested session ID do not match up-party session ID."); } } catch (Exception ex) { logger.Warning(ex); } rpInitiatedLogoutRequest.IdTokenHint = session.IdToken; oidcUpSequenceData.SessionDownPartyLinks = session.DownPartyLinks; oidcUpSequenceData.SessionClaims = session.Claims; await sequenceLogic.SaveSequenceDataAsync(oidcUpSequenceData); } logger.ScopeTrace($"Up, End session request '{rpInitiatedLogoutRequest.ToJsonIndented()}'."); _ = await sessionUpPartyLogic.DeleteSessionAsync(session); await oauthRefreshTokenGrantLogic.DeleteRefreshTokenGrantsAsync(oidcUpSequenceData.SessionId); securityHeaderLogic.AddFormActionAllowAll(); var nameValueCollection = rpInitiatedLogoutRequest.ToDictionary(); logger.ScopeTrace($"Up, End session request URL '{party.Client.EndSessionUrl}'."); logger.ScopeTrace("Up, Sending OIDC End session request.", triggerEvent: true); return(await nameValueCollection.ToRedirectResultAsync(party.Client.EndSessionUrl)); }
public async Task <IActionResult> AuthenticationResponseAsync(string partyId, List <Claim> claims) { logger.ScopeTrace(() => "Down, OIDC Authentication response."); logger.SetScopeProperty(Constants.Logs.DownPartyId, partyId); var party = await tenantRepository.GetAsync <TParty>(partyId); if (party.Client == null) { throw new NotSupportedException("Party Client not configured."); } var sequenceData = await sequenceLogic.GetSequenceDataAsync <OidcDownSequenceData>(false); logger.ScopeTrace(() => $"Down, OIDC received JWT claims '{claims.ToFormattedString()}'", traceType: TraceTypes.Claim); claims = await claimTransformLogic.Transform(party.ClaimTransforms?.ConvertAll(t => (ClaimTransform)t), claims); logger.ScopeTrace(() => $"Down, OIDC output JWT claims '{claims.ToFormattedString()}'", traceType: TraceTypes.Claim); var nameValueCollection = await CreateAuthenticationAndSessionResponse(party, claims, sequenceData); var responseMode = GetResponseMode(sequenceData.ResponseMode, sequenceData.ResponseType); await sequenceLogic.RemoveSequenceDataAsync <OidcDownSequenceData>(); if (party.RestrictFormAction) { securityHeaderLogic.AddFormAction(sequenceData.RedirectUri); } else { securityHeaderLogic.AddFormActionAllowAll(); } switch (responseMode) { case IdentityConstants.ResponseModes.FormPost: return(await nameValueCollection.ToHtmlPostContentResultAsync(sequenceData.RedirectUri)); case IdentityConstants.ResponseModes.Query: return(await nameValueCollection.ToRedirectResultAsync(sequenceData.RedirectUri)); case IdentityConstants.ResponseModes.Fragment: return(await nameValueCollection.ToFragmentResultAsync(sequenceData.RedirectUri)); default: throw new NotSupportedException(); } }
private async Task <IActionResult> LogoutResponseAsync <T>(SamlDownParty party, Saml2Configuration samlConfig, string inResponseTo, string relayState, Saml2Binding <T> binding, Saml2StatusCodes status, string sessionIndex) { binding.RelayState = relayState; var saml2LogoutResponse = new Saml2LogoutResponse(samlConfig) { InResponseTo = new Saml2Id(inResponseTo), Status = status, Destination = new Uri(party.LoggedOutUrl), SessionIndex = sessionIndex }; binding.Bind(saml2LogoutResponse); logger.ScopeTrace(() => $"SAML Logout response '{saml2LogoutResponse.XmlDocument.OuterXml}'.", traceType: TraceTypes.Message); logger.ScopeTrace(() => $"Logged out URL '{party.LoggedOutUrl}'."); logger.ScopeTrace(() => "Down, SAML Logout response.", triggerEvent: true); await sequenceLogic.RemoveSequenceDataAsync <SamlDownSequenceData>(); if (party.RestrictFormAction) { securityHeaderLogic.AddFormAction(party.LoggedOutUrl); } else { securityHeaderLogic.AddFormActionAllowAll(); } if (binding is Saml2Binding <Saml2RedirectBinding> ) { return(await(binding as Saml2RedirectBinding).ToActionFormResultAsync()); } if (binding is Saml2Binding <Saml2PostBinding> ) { return(await(binding as Saml2PostBinding).ToActionFormResultAsync()); } else { throw new NotSupportedException(); } }
private async Task <IActionResult> AuthnRequestAsync <T>(SamlUpParty party, Saml2Binding <T> binding, SamlUpSequenceData samlUpSequenceData) { var samlConfig = await saml2ConfigurationLogic.GetSamlUpConfigAsync(party, includeSigningAndDecryptionCertificate : true); binding.RelayState = await sequenceLogic.CreateExternalSequenceIdAsync(); var saml2AuthnRequest = new Saml2AuthnRequest(samlConfig); switch (samlUpSequenceData.LoginAction) { case LoginAction.ReadSession: saml2AuthnRequest.IsPassive = true; break; case LoginAction.RequireLogin: saml2AuthnRequest.ForceAuthn = true; break; default: break; } if (party.AuthnContextClassReferences?.Count() > 0) { saml2AuthnRequest.RequestedAuthnContext = new RequestedAuthnContext { Comparison = party.AuthnContextComparison.HasValue ? (AuthnContextComparisonTypes)Enum.Parse(typeof(AuthnContextComparisonTypes), party.AuthnContextComparison.Value.ToString()) : null, AuthnContextClassRef = party.AuthnContextClassReferences, }; } binding.Bind(saml2AuthnRequest); logger.ScopeTrace(() => $"SAML Authn request '{saml2AuthnRequest.XmlDocument.OuterXml}'.", traceType: TraceTypes.Message); logger.ScopeTrace(() => $"Authn URL '{samlConfig.SingleSignOnDestination?.OriginalString}'."); logger.ScopeTrace(() => "Up, Sending SAML Authn request.", triggerEvent: true); securityHeaderLogic.AddFormActionAllowAll(); if (binding is Saml2Binding <Saml2RedirectBinding> ) { return(await(binding as Saml2RedirectBinding).ToActionFormResultAsync()); } else if (binding is Saml2Binding <Saml2PostBinding> ) { return(await(binding as Saml2PostBinding).ToActionFormResultAsync()); } else { throw new NotSupportedException(); } }
private async Task <IActionResult> AuthnRequestAsync <T>(SamlUpParty party, Saml2Binding <T> binding, SamlUpSequenceData samlUpSequenceData) { var samlConfig = saml2ConfigurationLogic.GetSamlUpConfig(party); binding.RelayState = SequenceString; var saml2AuthnRequest = new Saml2AuthnRequest(samlConfig); switch (samlUpSequenceData.LoginAction) { case LoginAction.ReadSession: saml2AuthnRequest.IsPassive = true; break; case LoginAction.RequireLogin: saml2AuthnRequest.ForceAuthn = true; break; default: break; } binding.Bind(saml2AuthnRequest); logger.ScopeTrace($"SAML Authn request '{saml2AuthnRequest.XmlDocument.OuterXml}'."); logger.ScopeTrace($"Authn URL '{samlConfig.SingleSignOnDestination?.OriginalString}'."); logger.ScopeTrace("Up, Sending SAML Authn request.", triggerEvent: true); securityHeaderLogic.AddFormActionAllowAll(); if (binding is Saml2Binding <Saml2RedirectBinding> ) { return(await(binding as Saml2RedirectBinding).ToActionFormResultAsync()); } else if (binding is Saml2Binding <Saml2PostBinding> ) { return(await(binding as Saml2PostBinding).ToActionFormResultAsync()); } else { throw new NotSupportedException(); } }
public async Task <IActionResult> AuthenticationRequestAsync(string partyId) { logger.ScopeTrace(() => "Up, OIDC Authentication request."); var oidcUpSequenceData = await sequenceLogic.GetSequenceDataAsync <OidcUpSequenceData>(remove : false); if (!oidcUpSequenceData.UpPartyId.Equals(partyId, StringComparison.Ordinal)) { throw new Exception("Invalid up-party id."); } logger.SetScopeProperty(Constants.Logs.UpPartyId, oidcUpSequenceData.UpPartyId); var party = await tenantRepository.GetAsync <TParty>(oidcUpSequenceData.UpPartyId); logger.SetScopeProperty(Constants.Logs.UpPartyClientId, party.Client.ClientId); await oidcDiscoveryReadUpLogic.CheckOidcDiscoveryAndUpdatePartyAsync(party); var nonce = RandomGenerator.GenerateNonce(); var loginCallBackUrl = HttpContext.GetUpPartyUrl(party.Name, Constants.Routes.OAuthController, Constants.Endpoints.AuthorizationResponse, partyBindingPattern: party.PartyBindingPattern); oidcUpSequenceData.ClientId = !party.Client.SpClientId.IsNullOrWhiteSpace() ? party.Client.SpClientId : party.Client.ClientId; oidcUpSequenceData.RedirectUri = loginCallBackUrl; oidcUpSequenceData.Nonce = nonce; if (party.Client.EnablePkce) { var codeVerifier = RandomGenerator.Generate(64); oidcUpSequenceData.CodeVerifier = codeVerifier; } await sequenceLogic.SaveSequenceDataAsync(oidcUpSequenceData); var authenticationRequest = new AuthenticationRequest { ClientId = oidcUpSequenceData.ClientId, ResponseMode = party.Client.ResponseMode, ResponseType = party.Client.ResponseType, RedirectUri = loginCallBackUrl, Nonce = nonce, State = await sequenceLogic.CreateExternalSequenceIdAsync() }; switch (oidcUpSequenceData.LoginAction) { case LoginAction.ReadSession: authenticationRequest.Prompt = IdentityConstants.AuthorizationServerPrompt.None; break; case LoginAction.RequireLogin: authenticationRequest.Prompt = IdentityConstants.AuthorizationServerPrompt.Login; break; default: break; } if (oidcUpSequenceData.MaxAge.HasValue) { authenticationRequest.MaxAge = oidcUpSequenceData.MaxAge; } if (!oidcUpSequenceData.UserId.IsNullOrEmpty()) { authenticationRequest.LoginHint = oidcUpSequenceData.UserId; } authenticationRequest.Scope = new[] { IdentityConstants.DefaultOidcScopes.OpenId }.ConcatOnce(party.Client.Scopes).ToSpaceList(); //TODO add AcrValues //authenticationRequest.AcrValues = "urn:federation:authentication:windows"; logger.ScopeTrace(() => $"Up, Authentication request '{authenticationRequest.ToJsonIndented()}'.", traceType: TraceTypes.Message); var nameValueCollection = authenticationRequest.ToDictionary(); if (party.Client.EnablePkce) { var codeChallengeRequest = new CodeChallengeSecret { CodeChallenge = await oidcUpSequenceData.CodeVerifier.Sha256HashBase64urlEncodedAsync(), CodeChallengeMethod = IdentityConstants.CodeChallengeMethods.S256, }; logger.ScopeTrace(() => $"Up, CodeChallengeSecret request '{codeChallengeRequest.ToJsonIndented()}'.", traceType: TraceTypes.Message); nameValueCollection = nameValueCollection.AddToDictionary(codeChallengeRequest); } securityHeaderLogic.AddFormActionAllowAll(); logger.ScopeTrace(() => $"Up, Authentication request URL '{party.Client.AuthorizeUrl}'."); logger.ScopeTrace(() => "Up, Sending OIDC Authentication request.", triggerEvent: true); return(await nameValueCollection.ToRedirectResultAsync(party.Client.AuthorizeUrl)); }
private async Task <IActionResult> LogoutRequestAsync <T>(SamlUpParty party, Saml2Binding <T> binding, SamlUpSequenceData samlUpSequenceData) { var samlConfig = await saml2ConfigurationLogic.GetSamlUpConfigAsync(party, includeSigningAndDecryptionCertificate : true); binding.RelayState = await sequenceLogic.CreateExternalSequenceIdAsync(); var saml2LogoutRequest = new Saml2LogoutRequest(samlConfig); var session = await sessionUpPartyLogic.GetSessionAsync(party); if (session == null) { return(await LogoutResponseDownAsync(samlUpSequenceData)); } try { if (!samlUpSequenceData.SessionId.Equals(session.SessionId, StringComparison.Ordinal)) { throw new Exception("Requested session ID do not match up-party session ID."); } } catch (Exception ex) { logger.Warning(ex); } saml2LogoutRequest.SessionIndex = session.ExternalSessionId; samlUpSequenceData.SessionDownPartyLinks = session.DownPartyLinks; samlUpSequenceData.SessionClaims = session.Claims; await sequenceLogic.SaveSequenceDataAsync(samlUpSequenceData); var jwtClaims = samlUpSequenceData.SessionClaims.ToClaimList(); var nameID = jwtClaims?.Where(c => c.Type == JwtClaimTypes.Subject).Select(c => c.Value).FirstOrDefault(); var nameIdFormat = jwtClaims?.Where(c => c.Type == Constants.JwtClaimTypes.SubFormat).Select(c => c.Value).FirstOrDefault(); if (!nameID.IsNullOrEmpty()) { var prePartyName = $"{party.Name}|"; if (nameID.StartsWith(prePartyName, StringComparison.Ordinal)) { nameID = nameID.Remove(0, prePartyName.Length); } if (nameIdFormat.IsNullOrEmpty()) { saml2LogoutRequest.NameId = new Saml2NameIdentifier(nameID); } else { saml2LogoutRequest.NameId = new Saml2NameIdentifier(nameID, new Uri(nameIdFormat)); } } binding.Bind(saml2LogoutRequest); logger.ScopeTrace(() => $"SAML Logout request '{saml2LogoutRequest.XmlDocument.OuterXml}'.", traceType: TraceTypes.Message); logger.ScopeTrace(() => $"Logout URL '{samlConfig.SingleLogoutDestination?.OriginalString}'."); logger.ScopeTrace(() => "Up, SAML Logout request.", triggerEvent: true); _ = await sessionUpPartyLogic.DeleteSessionAsync(party, session); await oauthRefreshTokenGrantLogic.DeleteRefreshTokenGrantsAsync(samlUpSequenceData.SessionId); securityHeaderLogic.AddFormActionAllowAll(); if (binding is Saml2Binding <Saml2RedirectBinding> ) { return(await(binding as Saml2RedirectBinding).ToActionFormResultAsync()); } else if (binding is Saml2Binding <Saml2PostBinding> ) { return(await(binding as Saml2PostBinding).ToActionFormResultAsync()); } else { throw new NotSupportedException(); } }
private async Task <IActionResult> AuthnResponseAsync <T>(SamlDownParty party, Saml2Configuration samlConfig, string inResponseTo, string relayState, string acsUrl, Saml2Binding <T> binding, Saml2StatusCodes status, IEnumerable <Claim> claims) { binding.RelayState = relayState; var saml2AuthnResponse = new FoxIDsSaml2AuthnResponse(settings, samlConfig) { InResponseTo = new Saml2Id(inResponseTo), Status = status, Destination = new Uri(acsUrl), }; if (status == Saml2StatusCodes.Success && party != null && claims != null) { logger.ScopeTrace(() => $"Down, SAML Authn received SAML claims '{claims.ToFormattedString()}'", traceType: TraceTypes.Claim); claims = await claimTransformLogic.Transform(party.ClaimTransforms?.ConvertAll(t => (ClaimTransform)t), claims); logger.ScopeTrace(() => $"Down, SAML Authn output SAML claims '{claims.ToFormattedString()}'", traceType: TraceTypes.Claim); saml2AuthnResponse.SessionIndex = samlClaimsDownLogic.GetSessionIndex(claims); saml2AuthnResponse.NameId = samlClaimsDownLogic.GetNameId(claims); var tokenIssueTime = DateTimeOffset.UtcNow; var tokenDescriptor = saml2AuthnResponse.CreateTokenDescriptor(samlClaimsDownLogic.GetSubjectClaims(party, claims), party.Issuer, tokenIssueTime, party.IssuedTokenLifetime); var authnContext = claims.FindFirstValue(c => c.Type == ClaimTypes.AuthenticationMethod); var authenticationInstant = claims.FindFirstValue(c => c.Type == ClaimTypes.AuthenticationInstant); var authenticationStatement = saml2AuthnResponse.CreateAuthenticationStatement(authnContext, DateTime.Parse(authenticationInstant)); var subjectConfirmation = saml2AuthnResponse.CreateSubjectConfirmation(tokenIssueTime, party.SubjectConfirmationLifetime); await saml2AuthnResponse.CreateSecurityTokenAsync(tokenDescriptor, authenticationStatement, subjectConfirmation); } binding.Bind(saml2AuthnResponse); logger.ScopeTrace(() => $"SAML Authn response '{saml2AuthnResponse.XmlDocument.OuterXml}'.", traceType: TraceTypes.Message); logger.ScopeTrace(() => $"ACS URL '{acsUrl}'."); logger.ScopeTrace(() => "Down, SAML Authn response.", triggerEvent: true); await sequenceLogic.RemoveSequenceDataAsync <SamlDownSequenceData>(); if (party.RestrictFormAction) { securityHeaderLogic.AddFormAction(acsUrl); } else { securityHeaderLogic.AddFormActionAllowAll(); } if (binding is Saml2Binding <Saml2RedirectBinding> ) { return(await(binding as Saml2RedirectBinding).ToActionFormResultAsync()); } else if (binding is Saml2Binding <Saml2PostBinding> ) { return(await(binding as Saml2PostBinding).ToActionFormResultAsync()); } else { throw new NotSupportedException(); } }