/// <summary>
        /// Creates a Provider's response to an incoming association request.
        /// </summary>
        /// <param name="requestMessage">The request message.</param>
        /// <param name="associationStore">The association store.</param>
        /// <param name="securitySettings">The security settings on the Provider.</param>
        /// <returns>
        /// The appropriate association response that is ready to be sent back to the Relying Party.
        /// </returns>
        /// <remarks>
        ///   <para>If an association is created, it will be automatically be added to the provided
        /// association store.</para>
        ///   <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>.
        /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
        /// </remarks>
        internal static IProtocolMessage CreateResponse(IAssociateRequestProvider requestMessage, IProviderAssociationStore associationStore, ProviderSecuritySettings securitySettings)
        {
            Requires.NotNull(requestMessage, "requestMessage");
            Requires.NotNull(associationStore, "associationStore");
            Requires.NotNull(securitySettings, "securitySettings");

            AssociateRequest request = (AssociateRequest)requestMessage;
            IProtocolMessage response;
            var protocol = requestMessage.GetProtocol();

            if (securitySettings.IsAssociationInPermittedRange(protocol, request.AssociationType) &&
                HmacShaAssociation.IsDHSessionCompatible(protocol, request.AssociationType, request.SessionType))
            {
                response = requestMessage.CreateResponseCore();

                // Create and store the association if this is a successful response.
                var successResponse = response as IAssociateSuccessfulResponseProvider;
                if (successResponse != null)
                {
                    OpenIdProviderUtilities.CreateAssociation(request, successResponse, associationStore, securitySettings);
                }
            }
            else
            {
                response = CreateUnsuccessfulResponse(requestMessage, securitySettings);
            }

            return(response);
        }
Beispiel #2
0
        /// <summary>
        /// Creates a Provider's response to an incoming association request.
        /// </summary>
        /// <param name="associationStore">The association store where a new association (if created) will be stored.  Must not be null.</param>
        /// <param name="securitySettings">The security settings on the Provider.</param>
        /// <returns>
        /// The appropriate association response that is ready to be sent back to the Relying Party.
        /// </returns>
        /// <remarks>
        /// <para>If an association is created, it will be automatically be added to the provided
        /// association store.</para>
        /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>.
        /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
        /// </remarks>
        internal IProtocolMessage CreateResponse(IAssociationStore <AssociationRelyingPartyType> associationStore, ProviderSecuritySettings securitySettings)
        {
            Contract.Requires <ArgumentNullException>(associationStore != null);
            Contract.Requires <ArgumentNullException>(securitySettings != null);

            IProtocolMessage response;

            if (securitySettings.IsAssociationInPermittedRange(Protocol, this.AssociationType) &&
                HmacShaAssociation.IsDHSessionCompatible(Protocol, this.AssociationType, this.SessionType))
            {
                response = this.CreateResponseCore();

                // Create and store the association if this is a successful response.
                var successResponse = response as AssociateSuccessfulResponse;
                if (successResponse != null)
                {
                    Association association = successResponse.CreateAssociation(this, securitySettings);
                    associationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association);
                }
            }
            else
            {
                response = this.CreateUnsuccessfulResponse(securitySettings);
            }

            return(response);
        }
Beispiel #3
0
        /// <summary>
        /// Creates a new association with a given Provider.
        /// </summary>
        /// <param name="provider">The provider to create an association with.</param>
        /// <param name="associateRequest">The associate request.  May be <c>null</c>, which will always result in a <c>null</c> return value..</param>
        /// <param name="retriesRemaining">The number of times to try the associate request again if the Provider suggests it.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// The newly created association, or null if no association can be created with
        /// the given Provider given the current security settings.
        /// </returns>
        /// <exception cref="ProtocolException">Create if an error occurs while creating the new association.</exception>
        private async Task <Association> CreateNewAssociationAsync(IProviderEndpoint provider, AssociateRequest associateRequest, int retriesRemaining, CancellationToken cancellationToken)
        {
            Requires.NotNull(provider, "provider");

            if (associateRequest == null || retriesRemaining < 0)
            {
                // this can happen if security requirements and protocol conflict
                // to where there are no association types to choose from.
                return(null);
            }

            Exception exception = null;

            try {
                var associateResponse = await this.channel.RequestAsync(associateRequest, cancellationToken);

                var associateSuccessfulResponse   = associateResponse as IAssociateSuccessfulResponseRelyingParty;
                var associateUnsuccessfulResponse = associateResponse as AssociateUnsuccessfulResponse;
                if (associateSuccessfulResponse != null)
                {
                    Association association = associateSuccessfulResponse.CreateAssociationAtRelyingParty(associateRequest);
                    this.associationStore.StoreAssociation(provider.Uri, association);
                    return(association);
                }
                else if (associateUnsuccessfulResponse != null)
                {
                    if (string.IsNullOrEmpty(associateUnsuccessfulResponse.AssociationType))
                    {
                        Logger.OpenId.Debug("Provider rejected an association request and gave no suggestion as to an alternative association type.  Giving up.");
                        return(null);
                    }

                    if (!this.securitySettings.IsAssociationInPermittedRange(Protocol.Lookup(provider.Version), associateUnsuccessfulResponse.AssociationType))
                    {
                        Logger.OpenId.DebugFormat("Provider rejected an association request and suggested '{0}' as an association to try, which this Relying Party does not support.  Giving up.", associateUnsuccessfulResponse.AssociationType);
                        return(null);
                    }

                    if (retriesRemaining <= 0)
                    {
                        Logger.OpenId.Debug("Unable to agree on an association type with the Provider in the allowed number of retries.  Giving up.");
                        return(null);
                    }

                    // Make sure the Provider isn't suggesting an incompatible pair of association/session types.
                    Protocol protocol = Protocol.Lookup(provider.Version);
                    ErrorUtilities.VerifyProtocol(
                        HmacShaAssociation.IsDHSessionCompatible(protocol, associateUnsuccessfulResponse.AssociationType, associateUnsuccessfulResponse.SessionType),
                        OpenIdStrings.IncompatibleAssociationAndSessionTypes,
                        associateUnsuccessfulResponse.AssociationType,
                        associateUnsuccessfulResponse.SessionType);

                    associateRequest = AssociateRequestRelyingParty.Create(this.securitySettings, provider, associateUnsuccessfulResponse.AssociationType, associateUnsuccessfulResponse.SessionType);
                    return(await this.CreateNewAssociationAsync(provider, associateRequest, retriesRemaining - 1, cancellationToken));
                }
                else
                {
                    throw new ProtocolException(MessagingStrings.UnexpectedMessageReceivedOfMany);
                }
            } catch (ProtocolException ex) {
                exception = ex;
            }

            Assumes.NotNull(exception);

            // If the association failed because the remote server can't handle Expect: 100 Continue headers,
            // then our web request handler should have already accomodated for future calls.  Go ahead and
            // immediately make one of those future calls now to try to get the association to succeed.
            if (UntrustedWebRequestHandler.IsExceptionFrom417ExpectationFailed(exception))
            {
                return(await this.CreateNewAssociationAsync(provider, associateRequest, retriesRemaining - 1, cancellationToken));
            }

            // Since having associations with OPs is not totally critical, we'll log and eat
            // the exception so that auth may continue in dumb mode.
            Logger.OpenId.ErrorFormat("An error occurred while trying to create an association with {0}.  {1}", provider.Uri, exception);
            return(null);
        }
        /// <summary>
        /// Creates a new association with a given Provider.
        /// </summary>
        /// <param name="provider">The provider to create an association with.</param>
        /// <param name="associateRequest">The associate request.  May be <c>null</c>, which will always result in a <c>null</c> return value..</param>
        /// <param name="retriesRemaining">The number of times to try the associate request again if the Provider suggests it.</param>
        /// <returns>
        /// The newly created association, or null if no association can be created with
        /// the given Provider given the current security settings.
        /// </returns>
        private Association CreateNewAssociation(ProviderEndpointDescription provider, AssociateRequest associateRequest, int retriesRemaining)
        {
            ErrorUtilities.VerifyArgumentNotNull(provider, "provider");

            if (associateRequest == null || retriesRemaining < 0)
            {
                // this can happen if security requirements and protocol conflict
                // to where there are no association types to choose from.
                return(null);
            }

            try {
                var associateResponse             = this.channel.Request(associateRequest);
                var associateSuccessfulResponse   = associateResponse as AssociateSuccessfulResponse;
                var associateUnsuccessfulResponse = associateResponse as AssociateUnsuccessfulResponse;
                if (associateSuccessfulResponse != null)
                {
                    Association association = associateSuccessfulResponse.CreateAssociation(associateRequest, null);
                    this.associationStore.StoreAssociation(provider.Endpoint, association);
                    return(association);
                }
                else if (associateUnsuccessfulResponse != null)
                {
                    if (string.IsNullOrEmpty(associateUnsuccessfulResponse.AssociationType))
                    {
                        Logger.Debug("Provider rejected an association request and gave no suggestion as to an alternative association type.  Giving up.");
                        return(null);
                    }

                    if (!this.securitySettings.IsAssociationInPermittedRange(Protocol.Lookup(provider.ProtocolVersion), associateUnsuccessfulResponse.AssociationType))
                    {
                        Logger.DebugFormat("Provider rejected an association request and suggested '{0}' as an association to try, which this Relying Party does not support.  Giving up.", associateUnsuccessfulResponse.AssociationType);
                        return(null);
                    }

                    if (retriesRemaining <= 0)
                    {
                        Logger.Debug("Unable to agree on an association type with the Provider in the allowed number of retries.  Giving up.");
                        return(null);
                    }

                    // Make sure the Provider isn't suggesting an incompatible pair of association/session types.
                    Protocol protocol = Protocol.Lookup(provider.ProtocolVersion);
                    ErrorUtilities.VerifyProtocol(
                        HmacShaAssociation.IsDHSessionCompatible(protocol, associateUnsuccessfulResponse.AssociationType, associateUnsuccessfulResponse.SessionType),
                        OpenIdStrings.IncompatibleAssociationAndSessionTypes,
                        associateUnsuccessfulResponse.AssociationType,
                        associateUnsuccessfulResponse.SessionType);

                    associateRequest = AssociateRequest.Create(this.securitySettings, provider, associateUnsuccessfulResponse.AssociationType, associateUnsuccessfulResponse.SessionType);
                    return(this.CreateNewAssociation(provider, associateRequest, retriesRemaining - 1));
                }
                else
                {
                    throw new ProtocolException(MessagingStrings.UnexpectedMessageReceivedOfMany);
                }
            } catch (ProtocolException ex) {
                // Since having associations with OPs is not totally critical, we'll log and eat
                // the exception so that auth may continue in dumb mode.
                Logger.ErrorFormat("An error occurred while trying to create an association with {0}.  {1}", provider.Endpoint, ex);
                return(null);
            }
        }
Beispiel #5
0
        public static AssociateRequest Create(OpenIdRelyingParty relyingParty, ServiceEndpoint provider, string assoc_type, string session_type, bool allowNoSession)
        {
            if (relyingParty == null)
            {
                throw new ArgumentNullException("relyingParty");
            }
            if (provider == null)
            {
                throw new ArgumentNullException("provider");
            }
            if (assoc_type == null)
            {
                throw new ArgumentNullException("assoc_type");
            }
            if (session_type == null)
            {
                throw new ArgumentNullException("session_type");
            }
            Debug.Assert(Array.IndexOf(provider.Protocol.Args.SignatureAlgorithm.All, assoc_type) >= 0);
            Debug.Assert(Array.IndexOf(provider.Protocol.Args.SessionType.All, session_type) >= 0);

            if (!HmacShaAssociation.IsDHSessionCompatible(provider.Protocol, assoc_type, session_type))
            {
                throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
                                                        Strings.IncompatibleAssociationAndSessionTypes, assoc_type, session_type));
            }

            var      args     = new Dictionary <string, string>();
            Protocol protocol = provider.Protocol;

            args.Add(protocol.openid.mode, protocol.Args.Mode.associate);
            args.Add(protocol.openid.assoc_type, assoc_type);

            DiffieHellman dh = null;

            if (provider.ProviderEndpoint.Scheme == Uri.UriSchemeHttps && allowNoSession)
            {
                Logger.InfoFormat("Requesting association with {0} (assoc_type = '{1}', session_type = '{2}').",
                                  provider.ProviderEndpoint, assoc_type, protocol.Args.SessionType.NoEncryption);
                args.Add(protocol.openid.session_type, protocol.Args.SessionType.NoEncryption);
            }
            else
            {
                Logger.InfoFormat("Requesting association with {0} (assoc_type = '{1}', session_type = '{2}').",
                                  provider.ProviderEndpoint, assoc_type, session_type);

                // Initiate Diffie-Hellman Exchange
                dh = DiffieHellmanUtil.CreateDiffieHellman();

                byte[] dhPublic = dh.CreateKeyExchange();
                string cpub     = DiffieHellmanUtil.UnsignedToBase64(dhPublic);

                args.Add(protocol.openid.session_type, session_type);
                args.Add(protocol.openid.dh_consumer_public, cpub);

                DHParameters dhps = dh.ExportParameters(true);

                if (dhps.P != DiffieHellmanUtil.DEFAULT_MOD || dhps.G != DiffieHellmanUtil.DEFAULT_GEN)
                {
                    args.Add(protocol.openid.dh_modulus, DiffieHellmanUtil.UnsignedToBase64(dhps.P));
                    args.Add(protocol.openid.dh_gen, DiffieHellmanUtil.UnsignedToBase64(dhps.G));
                }
            }

            return(new AssociateRequest(relyingParty, provider, args, dh));
        }