/// <summary> /// Called when the Provider is preparing to send a response to an authentication request. /// </summary> /// <param name="request">The request that is configured to generate the outgoing response.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// <c>true</c> if this behavior owns this request and wants to stop other behaviors /// from handling it; <c>false</c> to allow other behaviors to process this request. /// </returns> async Task<bool> IProviderBehavior.OnOutgoingResponseAsync(Provider.IAuthenticationRequest request, CancellationToken cancellationToken) { bool result = false; // Nothing to do for negative assertions. if (!request.IsAuthenticated.Value) { return result; } var requestInternal = (Provider.AuthenticationRequest)request; var responseMessage = (IProtocolMessageWithExtensions)await requestInternal.GetResponseAsync(cancellationToken); // Only apply our special policies if the RP requested it. var papeRequest = request.GetExtension<PolicyRequest>(); if (papeRequest != null) { var papeResponse = responseMessage.Extensions.OfType<PolicyResponse>().SingleOrDefault(); if (papeResponse == null) { request.AddResponseExtension(papeResponse = new PolicyResponse()); } if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) { result = true; if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.USGovernmentTrustLevel1)) { papeResponse.ActualPolicies.Add(AuthenticationPolicies.USGovernmentTrustLevel1); } // The spec requires that the OP perform discovery and if that fails, it must either sternly // warn the user of a potential threat or just abort the authentication. // We can't verify that the OP displayed anything to the user at this level, but we can // at least verify that the OP performed the discovery on the realm and halt things if it didn't. ErrorUtilities.VerifyHost(requestInternal.HasRealmDiscoveryBeenPerformed, BehaviorStrings.RealmDiscoveryNotPerformed); } if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) { ErrorUtilities.VerifyProtocol(request.ClaimedIdentifier == request.LocalIdentifier, OpenIdStrings.DelegatingIdentifiersNotAllowed); // Mask the user's identity with a PPID. ErrorUtilities.VerifyHost(PpidIdentifierProvider != null, BehaviorStrings.PpidProviderNotGiven); Identifier ppidIdentifier = PpidIdentifierProvider.GetIdentifier(request.LocalIdentifier, request.Realm); requestInternal.ResetClaimedAndLocalIdentifiers(ppidIdentifier); // Indicate that the RP is receiving a PPID claimed_id if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) { papeResponse.ActualPolicies.Add(AuthenticationPolicies.PrivatePersonalIdentifier); } } if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) { ErrorUtilities.VerifyProtocol( !responseMessage.Extensions.OfType<ClaimsResponse>().Any() && !responseMessage.Extensions.OfType<FetchResponse>().Any(), BehaviorStrings.PiiIncludedWithNoPiiPolicy); // If no PII is given in extensions, and the claimed_id is a PPID, then we can state we issue no PII. if (papeResponse.ActualPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) { if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.NoPersonallyIdentifiableInformation)) { papeResponse.ActualPolicies.Add(AuthenticationPolicies.NoPersonallyIdentifiableInformation); } } } Reporting.RecordEventOccurrence(this, "OP"); } return result; }