Example #1
0
        /// <summary>
        /// Generates the authentication requests that can satisfy the requirements of some OpenID Identifier.
        /// </summary>
        /// <param name="userSuppliedIdentifier">
        /// The Identifier supplied by the user.  This may be a URL, an XRI or i-name.
        /// </param>
        /// <returns>
        /// A sequence of authentication requests, any of which constitutes a valid identity assertion on the Claimed Identifier.
        /// Never null, but may be empty.
        /// </returns>
        /// <remarks>
        /// <para>Any individual generated request can satisfy the authentication.
        /// The generated requests are sorted in preferred order.
        /// Each request is generated as it is enumerated to.  Associations are created only as
        /// <see cref="IAuthenticationRequest.RedirectingResponse"/> is called.</para>
        /// <para>No exception is thrown if no OpenID endpoints were discovered.
        /// An empty enumerable is returned instead.</para>
        /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para>
        /// </remarks>
        /// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception>
        public IEnumerable <IAuthenticationRequest> CreateRequests(Identifier userSuppliedIdentifier)
        {
            Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier");
            RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);

            return(this.CreateRequests(userSuppliedIdentifier, Realm.AutoDetect));
        }
Example #2
0
        /// <summary>
        /// Gets the actual arguments to add to a querystring or other response,
        /// where type URI, alias, and actual key/values are all defined.
        /// </summary>
        /// <param name="includeOpenIdPrefix">
        /// <c>true</c> if the generated parameter names should include the 'openid.' prefix.
        /// This should be <c>true</c> for all but direct response messages.
        /// </param>
        /// <returns>A dictionary of key=value pairs to add to the message to carry the extension.</returns>
        internal IDictionary <string, string> GetArgumentsToSend(bool includeOpenIdPrefix)
        {
            RequiresEx.ValidState(!this.ReadMode);
            Dictionary <string, string> args = new Dictionary <string, string>();

            foreach (var typeUriAndExtension in this.extensions)
            {
                string typeUri       = typeUriAndExtension.Key;
                var    extensionArgs = typeUriAndExtension.Value;
                if (extensionArgs.Count == 0)
                {
                    continue;
                }
                string alias = this.aliasManager.GetAlias(typeUri);

                // send out the alias declaration
                string openidPrefix = includeOpenIdPrefix ? this.protocol.openid.Prefix : string.Empty;
                args.Add(openidPrefix + this.protocol.openidnp.ns + "." + alias, typeUri);
                string prefix = openidPrefix + alias;
                foreach (var pair in extensionArgs)
                {
                    string key = prefix;
                    if (pair.Key.Length > 0)
                    {
                        key += "." + pair.Key;
                    }
                    args.Add(key, pair.Value);
                }
            }
            return(args);
        }
Example #3
0
        /// <summary>
        /// Generates the authentication requests that can satisfy the requirements of some OpenID Identifier.
        /// </summary>
        /// <param name="userSuppliedIdentifier">The Identifier supplied by the user.  This may be a URL, an XRI or i-name.</param>
        /// <param name="realm">The shorest URL that describes this relying party web site's address.
        /// For example, if your login page is found at https://www.example.com/login.aspx,
        /// your realm would typically be https://www.example.com/.</param>
        /// <param name="requestContext">The request context.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// A sequence of authentication requests, any of which constitutes a valid identity assertion on the Claimed Identifier.
        /// Never null, but may be empty.
        /// </returns>
        /// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception>
        /// <remarks>
        ///   <para>Any individual generated request can satisfy the authentication.
        /// The generated requests are sorted in preferred order.
        /// Each request is generated as it is enumerated to.  Associations are created only as
        ///   <see cref="IAuthenticationRequest.GetRedirectingResponseAsync" /> is called.</para>
        ///   <para>No exception is thrown if no OpenID endpoints were discovered.
        /// An empty enumerable is returned instead.</para>
        ///   <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para>
        /// </remarks>
        public async Task <IEnumerable <IAuthenticationRequest> > CreateRequestsAsync(Identifier userSuppliedIdentifier, Realm realm, HttpRequestBase requestContext = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            RequiresEx.ValidState(requestContext != null || (HttpContext.Current != null && HttpContext.Current.Request != null), MessagingStrings.HttpContextRequired);
            Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier");
            Requires.NotNull(realm, "realm");

            requestContext = requestContext ?? this.channel.GetRequestFromContext();

            // This next code contract is a BAD idea, because it causes each authentication request to be generated
            // at least an extra time.
            ////
            // Build the return_to URL
            UriBuilder returnTo = new UriBuilder(requestContext.GetPublicFacingUrl());

            // Trim off any parameters with an "openid." prefix, and a few known others
            // to avoid carrying state from a prior login attempt.
            returnTo.Query = string.Empty;
            NameValueCollection queryParams = requestContext.GetQueryStringBeforeRewriting();
            var returnToParams = new Dictionary <string, string>(queryParams.Count);

            foreach (string key in queryParams)
            {
                if (!IsOpenIdSupportingParameter(key) && key != null)
                {
                    returnToParams.Add(key, queryParams[key]);
                }
            }

            returnTo.AppendQueryArgs(returnToParams);

            return(await this.CreateRequestsAsync(userSuppliedIdentifier, realm, returnTo.Uri, cancellationToken));
        }
        /// <summary>
        /// Gets the OAuth authorization request included with an OpenID authentication
        /// request, if there is one.
        /// </summary>
        /// <param name="openIdRequest">The OpenID authentication request.</param>
        /// <returns>
        /// The scope of access the relying party is requesting, or null if no OAuth request
        /// is present.
        /// </returns>
        /// <remarks>
        /// <para>Call this method rather than simply extracting the OAuth extension
        /// out from the authentication request directly to ensure that the additional
        /// security measures that are required are taken.</para>
        /// </remarks>
        public AuthorizationRequest ReadAuthorizationRequest(IHostProcessedRequest openIdRequest)
        {
            Requires.NotNull(openIdRequest, "openIdRequest");
            RequiresEx.ValidState(this.TokenManager is ICombinedOpenIdProviderTokenManager);
            var openidTokenManager = this.TokenManager as ICombinedOpenIdProviderTokenManager;

            ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName);

            var authzRequest = openIdRequest.GetExtension <AuthorizationRequest>();

            if (authzRequest == null)
            {
                return(null);
            }

            // OpenID+OAuth spec section 9:
            // The Combined Provider SHOULD verify that the consumer key passed in the
            // request is authorized to be used for the realm passed in the request.
            string expectedConsumerKey = openidTokenManager.GetConsumerKey(openIdRequest.Realm);

            ErrorUtilities.VerifyProtocol(
                string.Equals(expectedConsumerKey, authzRequest.Consumer, StringComparison.Ordinal),
                OAuthStrings.OpenIdOAuthRealmConsumerKeyDoNotMatch);

            return(authzRequest);
        }
        public void AttachAuthorizationResponse(IHostProcessedRequest openIdAuthenticationRequest, string scope)
        {
            Requires.NotNull(openIdAuthenticationRequest, "openIdAuthenticationRequest");
            RequiresEx.ValidState(this.TokenManager is ICombinedOpenIdProviderTokenManager);

            var openidTokenManager = this.TokenManager as ICombinedOpenIdProviderTokenManager;
            IOpenIdMessageExtension response;

            if (scope != null)
            {
                // Generate an authorized request token to return to the relying party.
                string consumerKey      = openidTokenManager.GetConsumerKey(openIdAuthenticationRequest.Realm);
                var    approvedResponse = new AuthorizationApprovedResponse {
                    RequestToken = this.TokenGenerator.GenerateRequestToken(consumerKey),
                    Scope        = scope,
                };
                openidTokenManager.StoreOpenIdAuthorizedRequestToken(consumerKey, approvedResponse);
                response = approvedResponse;
            }
            else
            {
                response = new AuthorizationDeclinedResponse();
            }

            openIdAuthenticationRequest.AddResponseExtension(response);
        }
Example #6
0
        /// <summary>
        /// Gets the response to send to the user agent.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The response.</returns>
        /// <exception cref="InvalidOperationException">Thrown if <see cref="IsResponseReady" /> is <c>false</c>.</exception>
        internal async Task <IProtocolMessage> GetResponseAsync(CancellationToken cancellationToken)
        {
            RequiresEx.ValidState(this.IsResponseReady, OpenIdStrings.ResponseNotReady);

            if (this.responseExtensions.Count > 0)
            {
                var responseMessage = await this.GetResponseMessageAsync(cancellationToken);

                var extensibleResponse = responseMessage as IProtocolMessageWithExtensions;
                ErrorUtilities.VerifyOperation(extensibleResponse != null, MessagingStrings.MessageNotExtensible, responseMessage.GetType().Name);
                foreach (var extension in this.responseExtensions)
                {
                    // It's possible that a prior call to this property
                    // has already added some/all of the extensions to the message.
                    // We don't have to worry about deleting old ones because
                    // this class provides no facility for removing extensions
                    // that are previously added.
                    if (!extensibleResponse.Extensions.Contains(extension))
                    {
                        extensibleResponse.Extensions.Add(extension);
                    }
                }
            }

            return(await this.GetResponseMessageAsync(cancellationToken));
        }
Example #7
0
        /// <summary>
        /// Discovers what access the client should have considering the access token in the current request.
        /// </summary>
        /// <param name="httpRequestInfo">The HTTP request info.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <param name="requiredScopes">The set of scopes required to approve this request.</param>
        /// <returns>
        /// The access token describing the authorization the client has.  Never <c>null</c>.
        /// </returns>
        /// <exception cref="ProtocolFaultResponseException">Thrown when the client is not authorized.  This exception should be caught and the
        /// <see cref="ProtocolFaultResponseException.ErrorResponseMessage" /> message should be returned to the client.</exception>
        public virtual Task <AccessToken> GetAccessTokenAsync(HttpRequestBase httpRequestInfo = null, CancellationToken cancellationToken = default(CancellationToken), params string[] requiredScopes)
        {
            Requires.NotNull(requiredScopes, "requiredScopes");
            RequiresEx.ValidState(this.ScopeSatisfiedCheck != null, Strings.RequiredPropertyNotYetPreset);

            httpRequestInfo = httpRequestInfo ?? this.Channel.GetRequestFromContext();
            return(this.GetAccessTokenAsync(httpRequestInfo.AsHttpRequestMessage(), cancellationToken, requiredScopes));
        }
        /// <summary>
        /// Generates a URL that the user's browser can be directed to in order to authorize
        /// this client to access protected data at some resource server.
        /// </summary>
        /// <param name="authorization">The authorization state that is tracking this particular request.  Optional.</param>
        /// <param name="implicitResponseType">
        /// <c>true</c> to request an access token in the fragment of the response's URL;
        /// <c>false</c> to authenticate to the authorization server and acquire the access token (and possibly a refresh token) via a private channel.
        /// </param>
        /// <param name="state">The client state that should be returned with the authorization response.</param>
        /// <returns>
        /// A fully-qualified URL suitable to initiate the authorization flow.
        /// </returns>
        public Uri RequestUserAuthorization(IAuthorizationState authorization, bool implicitResponseType = false, string state = null)
        {
            Requires.NotNull(authorization, "authorization");
            RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier));

            var request = this.PrepareRequestUserAuthorization(authorization, implicitResponseType, state);

            return(this.Channel.PrepareResponse(request).GetDirectUriRequest(this.Channel));
        }
Example #9
0
        /// <summary>
        /// Gets the fields carried by a given OpenId extension.
        /// </summary>
        /// <param name="extensionTypeUri">The type URI of the extension whose fields are being queried for.</param>
        /// <returns>
        /// The fields included in the given extension, or null if the extension is not present.
        /// </returns>
        internal IDictionary <string, string> GetExtensionArguments(string extensionTypeUri)
        {
            Requires.NotNullOrEmpty(extensionTypeUri, "extensionTypeUri");
            RequiresEx.ValidState(this.ReadMode);

            IDictionary <string, string> extensionArgs;

            this.extensions.TryGetValue(extensionTypeUri, out extensionArgs);
            return(extensionArgs);
        }
Example #10
0
        /// <summary>
        /// Generates a URL that the user's browser can be directed to in order to authorize
        /// this client to access protected data at some resource server.
        /// </summary>
        /// <param name="authorization">The authorization state that is tracking this particular request.  Optional.</param>
        /// <param name="implicitResponseType"><c>true</c> to request an access token in the fragment of the response's URL;
        /// <c>false</c> to authenticate to the authorization server and acquire the access token (and possibly a refresh token) via a private channel.</param>
        /// <param name="state">The client state that should be returned with the authorization response.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// A fully-qualified URL suitable to initiate the authorization flow.
        /// </returns>
        public async Task <Uri> RequestUserAuthorizationAsync(IAuthorizationState authorization, bool implicitResponseType = false, string state = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(authorization, "authorization");
            RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier));

            var request  = this.PrepareRequestUserAuthorization(authorization, implicitResponseType, state);
            var response = await this.Channel.PrepareResponseAsync(request, cancellationToken);

            return(response.GetDirectUriRequest());
        }
        public void AttachAuthorizationResponse(IHostProcessedRequest openIdAuthenticationRequest, string consumerKey, string scope)
        {
            Requires.NotNull(openIdAuthenticationRequest, "openIdAuthenticationRequest");
            Requires.That((consumerKey == null) == (scope == null), null, "consumerKey and scope must either be both provided or both omitted.");
            RequiresEx.ValidState(this.TokenManager is ICombinedOpenIdProviderTokenManager);
            var openidTokenManager = (ICombinedOpenIdProviderTokenManager)this.TokenManager;

            ErrorUtilities.VerifyArgument(consumerKey == null || consumerKey == openidTokenManager.GetConsumerKey(openIdAuthenticationRequest.Realm), OAuthStrings.OpenIdOAuthRealmConsumerKeyDoNotMatch);

            this.AttachAuthorizationResponse(openIdAuthenticationRequest, scope);
        }
        /// <summary>
        /// Sends an identity assertion on behalf of one of this Provider's
        /// members in order to redirect the user agent to a relying party
        /// web site and log him/her in immediately in one uninterrupted step.
        /// </summary>
        /// <param name="providerEndpoint">The absolute URL on the Provider site that receives OpenID messages.</param>
        /// <param name="relyingPartyRealm">The URL of the Relying Party web site.
        /// This will typically be the home page, but may be a longer URL if
        /// that Relying Party considers the scope of its realm to be more specific.
        /// The URL provided here must allow discovery of the Relying Party's
        /// XRDS document that advertises its OpenID RP endpoint.</param>
        /// <param name="claimedIdentifier">The Identifier you are asserting your member controls.</param>
        /// <param name="localIdentifier">The Identifier you know your user by internally.  This will typically
        /// be the same as <paramref name="claimedIdentifier"/>.</param>
        /// <param name="extensions">The extensions.</param>
        public void SendUnsolicitedAssertion(Uri providerEndpoint, Realm relyingPartyRealm, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions)
        {
            RequiresEx.ValidState(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
            Requires.NotNull(providerEndpoint, "providerEndpoint");
            Requires.That(providerEndpoint.IsAbsoluteUri, "providerEndpoint", OpenIdStrings.AbsoluteUriRequired);
            Requires.NotNull(relyingPartyRealm, "relyingPartyRealm");
            Requires.NotNull(claimedIdentifier, "claimedIdentifier");
            Requires.NotNull(localIdentifier, "localIdentifier");

            this.PrepareUnsolicitedAssertion(providerEndpoint, relyingPartyRealm, claimedIdentifier, localIdentifier, extensions).Send();
        }
        public void Respond(IRequest request)
        {
            RequiresEx.ValidState(HttpContext.Current != null, MessagingStrings.CurrentHttpContextRequired);
            Requires.NotNull(request, "request");
            Requires.That(request.IsResponseReady, "request", OpenIdStrings.ResponseNotReady);

            this.ApplyBehaviorsToResponse(request);
            Request requestInternal = (Request)request;

            this.Channel.Respond(requestInternal.Response);
        }
        /// <summary>
        /// Discovers what access the client should have considering the access token in the current request.
        /// </summary>
        /// <param name="httpRequestInfo">The HTTP request info.</param>
        /// <param name="requiredScopes">The set of scopes required to approve this request.</param>
        /// <returns>
        /// The access token describing the authorization the client has.  Never <c>null</c>.
        /// </returns>
        /// <exception cref="ProtocolFaultResponseException">
        /// Thrown when the client is not authorized.  This exception should be caught and the
        /// <see cref="ProtocolFaultResponseException.ErrorResponseMessage"/> message should be returned to the client.
        /// </exception>
        public virtual AccessToken GetAccessToken(HttpRequestBase httpRequestInfo = null, params string[] requiredScopes)
        {
            Requires.NotNull(requiredScopes, "requiredScopes");
            RequiresEx.ValidState(this.ScopeSatisfiedCheck != null, Strings.RequiredPropertyNotYetPreset);
            if (httpRequestInfo == null)
            {
                httpRequestInfo = this.Channel.GetRequestFromContext();
            }

            AccessToken accessToken;
            AccessProtectedResourceRequest request = null;

            try {
                if (this.Channel.TryReadFromRequest <AccessProtectedResourceRequest>(httpRequestInfo, out request))
                {
                    accessToken = this.AccessTokenAnalyzer.DeserializeAccessToken(request, request.AccessToken);
                    ErrorUtilities.VerifyHost(accessToken != null, "IAccessTokenAnalyzer.DeserializeAccessToken returned a null reslut.");
                    if (string.IsNullOrEmpty(accessToken.User) && string.IsNullOrEmpty(accessToken.ClientIdentifier))
                    {
                        Logger.OAuth.Error("Access token rejected because both the username and client id properties were null or empty.");
                        ErrorUtilities.ThrowProtocol(ResourceServerStrings.InvalidAccessToken);
                    }

                    var requiredScopesSet = OAuthUtilities.ParseScopeSet(requiredScopes);
                    if (!this.ScopeSatisfiedCheck.IsScopeSatisfied(requiredScope: requiredScopesSet, grantedScope: accessToken.Scope))
                    {
                        var response = UnauthorizedResponse.InsufficientScope(request, requiredScopesSet);
                        throw new ProtocolFaultResponseException(this.Channel, response);
                    }

                    return(accessToken);
                }
                else
                {
                    var ex       = new ProtocolException(ResourceServerStrings.MissingAccessToken);
                    var response = UnauthorizedResponse.InvalidRequest(ex);
                    throw new ProtocolFaultResponseException(this.Channel, response, innerException: ex);
                }
            } catch (ProtocolException ex) {
                if (ex is ProtocolFaultResponseException)
                {
                    // This doesn't need to be wrapped again.
                    throw;
                }

                var response = request != null?UnauthorizedResponse.InvalidToken(request, ex) : UnauthorizedResponse.InvalidRequest(ex);

                throw new ProtocolFaultResponseException(this.Channel, response, innerException: ex);
            }
        }
        /// <summary>
        /// Prepares a request for user authorization from an authorization server.
        /// </summary>
        /// <param name="authorization">The authorization state to associate with this particular request.</param>
        /// <returns>The authorization request.</returns>
        public OutgoingWebResponse PrepareRequestUserAuthorization(IAuthorizationState authorization)
        {
            Requires.NotNull(authorization, "authorization");
            RequiresEx.ValidState(authorization.Callback != null || (HttpContext.Current != null && HttpContext.Current.Request != null), MessagingStrings.HttpContextRequired);
            RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), Strings.RequiredPropertyNotYetPreset, "ClientIdentifier");

            if (authorization.Callback == null)
            {
                authorization.Callback = this.Channel.GetRequestFromContext().GetPublicFacingUrl()
                                         .StripMessagePartsFromQueryString(this.Channel.MessageDescriptions.Get(typeof(EndUserAuthorizationSuccessResponseBase), Protocol.Default.Version))
                                         .StripMessagePartsFromQueryString(this.Channel.MessageDescriptions.Get(typeof(EndUserAuthorizationFailedResponse), Protocol.Default.Version));
                authorization.SaveChanges();
            }

            var request = new EndUserAuthorizationRequestC(this.AuthorizationServer)
            {
                ClientIdentifier = this.ClientIdentifier,
                Callback         = authorization.Callback,
            };

            request.Scope.ResetContents(authorization.Scope);

            // Mitigate XSRF attacks by including a state value that would be unpredictable between users, but
            // verifiable for the same user/session.
            // If the host is implementing the authorization tracker though, they're handling this protection themselves.
            HttpCookie cookie = null;

            if (this.AuthorizationTracker == null)
            {
                var context = this.Channel.GetHttpContext();

                string xsrfKey = MessagingUtilities.GetNonCryptoRandomDataAsBase64(16);
                cookie = new HttpCookie(XsrfCookieName, xsrfKey)
                {
                    HttpOnly = true,
                    Secure   = FormsAuthentication.RequireSSL,
                    ////Expires = DateTime.Now.Add(OAuth2ClientSection.Configuration.MaxAuthorizationTime), // we prefer session cookies to persistent ones
                };
                request.ClientState = xsrfKey;
            }

            var response = this.Channel.PrepareResponse(request);

            if (cookie != null)
            {
                response.Cookies.Add(cookie);
            }

            return(response);
        }
        /// <summary>
        /// Processes the authorization response from an authorization server, if available.
        /// </summary>
        /// <param name="request">The incoming HTTP request that may carry an authorization response.</param>
        /// <returns>The authorization state that contains the details of the authorization.</returns>
        public IAuthorizationState ProcessUserAuthorization(HttpRequestBase request = null)
        {
            RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), Strings.RequiredPropertyNotYetPreset, "ClientIdentifier");
            RequiresEx.ValidState(this.ClientCredentialApplicator != null, Strings.RequiredPropertyNotYetPreset, "ClientCredentialApplicator");

            if (request == null)
            {
                request = this.Channel.GetRequestFromContext();
            }

            IMessageWithClientState response;

            if (this.Channel.TryReadFromRequest <IMessageWithClientState>(request, out response))
            {
                Uri callback = MessagingUtilities.StripMessagePartsFromQueryString(request.GetPublicFacingUrl(), this.Channel.MessageDescriptions.Get(response));
                IAuthorizationState authorizationState;
                if (this.AuthorizationTracker != null)
                {
                    authorizationState = this.AuthorizationTracker.GetAuthorizationState(callback, response.ClientState);
                    ErrorUtilities.VerifyProtocol(authorizationState != null, ClientStrings.AuthorizationResponseUnexpectedMismatch);
                }
                else
                {
                    var context = this.Channel.GetHttpContext();

                    HttpCookie cookie = request.Cookies[XsrfCookieName];
                    ErrorUtilities.VerifyProtocol(cookie != null && string.Equals(response.ClientState, cookie.Value, StringComparison.Ordinal), ClientStrings.AuthorizationResponseUnexpectedMismatch);
                    authorizationState = new AuthorizationState {
                        Callback = callback
                    };
                }
                var success = response as EndUserAuthorizationSuccessAuthCodeResponse;
                var failure = response as EndUserAuthorizationFailedResponse;
                ErrorUtilities.VerifyProtocol(success != null || failure != null, MessagingStrings.UnexpectedMessageReceivedOfMany);
                if (success != null)
                {
                    this.UpdateAuthorizationWithResponse(authorizationState, success);
                }
                else                     // failure
                {
                    Logger.OAuth.Info("User refused to grant the requested authorization at the Authorization Server.");
                    authorizationState.Delete();
                }

                return(authorizationState);
            }

            return(null);
        }
        /// <summary>
        /// Gets the dictionary of message parts that should be deserialized into extensions.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ignoreUnsigned">If set to <c>true</c> only signed extensions will be available.</param>
        /// <returns>
        /// A dictionary of message parts, including only signed parts when appropriate.
        /// </returns>
        private IDictionary <string, string> GetExtensionsDictionary(IProtocolMessage message, bool ignoreUnsigned)
        {
            RequiresEx.ValidState(this.Channel != null);

            IndirectSignedResponse signedResponse = message as IndirectSignedResponse;

            if (signedResponse != null && ignoreUnsigned)
            {
                return(signedResponse.GetSignedMessageParts(this.Channel));
            }
            else
            {
                return(this.Channel.MessageDescriptions.GetAccessor(message));
            }
        }
Example #18
0
        /// <summary>
        /// Decrypts the specified value using either the symmetric or asymmetric encryption algorithm as appropriate.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="symmetricSecretHandle">The symmetric secret handle.  <c>null</c> when using an asymmetric algorithm.</param>
        /// <returns>
        /// The decrypted value.
        /// </returns>
        private byte[] Decrypt(byte[] value, string symmetricSecretHandle)
        {
            RequiresEx.ValidState(this.asymmetricEncrypting != null || symmetricSecretHandle != null);

            if (this.asymmetricEncrypting != null)
            {
                return(this.asymmetricEncrypting.DecryptWithRandomSymmetricKey(value));
            }
            else
            {
                var key = this.cryptoKeyStore.GetKey(this.cryptoKeyBucket, symmetricSecretHandle);
                ErrorUtilities.VerifyProtocol(key != null, MessagingStrings.MissingDecryptionKeyForHandle, this.cryptoKeyBucket, symmetricSecretHandle);
                return(MessagingUtilities.Decrypt(value, key.Key));
            }
        }
Example #19
0
        /// <summary>
        /// Processes the authorization response from an authorization server, if available.
        /// </summary>
        /// <param name="request">The incoming HTTP request that may carry an authorization response.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The authorization state that contains the details of the authorization.</returns>
        public async Task <IAuthorizationState> ProcessUserAuthorizationAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(request, "request");
            RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), Strings.RequiredPropertyNotYetPreset, "ClientIdentifier");
            RequiresEx.ValidState(this.ClientCredentialApplicator != null, Strings.RequiredPropertyNotYetPreset, "ClientCredentialApplicator");

            var response = await this.Channel.TryReadFromRequestAsync <IMessageWithClientState>(request, cancellationToken);

            if (response != null)
            {
                Uri callback = request.RequestUri.StripMessagePartsFromQueryString(this.Channel.MessageDescriptions.Get(response));
                IAuthorizationState authorizationState;
                if (this.AuthorizationTracker != null)
                {
                    authorizationState = this.AuthorizationTracker.GetAuthorizationState(callback, response.ClientState);
                    ErrorUtilities.VerifyProtocol(authorizationState != null, ClientStrings.AuthorizationResponseUnexpectedMismatch);
                }
                else
                {
                    var xsrfCookieValue = (from cookieHeader in request.Headers.GetCookies()
                                           from cookie in cookieHeader.Cookies
                                           where cookie.Name == XsrfCookieName
                                           select cookie.Value).FirstOrDefault();
                    ErrorUtilities.VerifyProtocol(xsrfCookieValue != null && string.Equals(response.ClientState, xsrfCookieValue, StringComparison.Ordinal), ClientStrings.AuthorizationResponseUnexpectedMismatch);
                    authorizationState = new AuthorizationState {
                        Callback = callback
                    };
                }
                var success = response as EndUserAuthorizationSuccessAuthCodeResponse;
                var failure = response as EndUserAuthorizationFailedResponse;
                ErrorUtilities.VerifyProtocol(success != null || failure != null, MessagingStrings.UnexpectedMessageReceivedOfMany);
                if (success != null)
                {
                    await this.UpdateAuthorizationWithResponseAsync(authorizationState, success, cancellationToken);
                }
                else                     // failure
                {
                    Logger.OAuth.Info("User refused to grant the requested authorization at the Authorization Server.");
                    authorizationState.Delete();
                }

                return(authorizationState);
            }

            return(null);
        }
Example #20
0
        /// <summary>
        /// Prepares a request for user authorization from an authorization server.
        /// </summary>
        /// <param name="authorization">The authorization state to associate with this particular request.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// The authorization request.
        /// </returns>
        public async Task <HttpResponseMessage> PrepareRequestUserAuthorizationAsync(IAuthorizationState authorization, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(authorization, "authorization");
            RequiresEx.ValidState(authorization.Callback != null || (HttpContext.Current != null && HttpContext.Current.Request != null), MessagingStrings.HttpContextRequired);
            RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), Strings.RequiredPropertyNotYetPreset, "ClientIdentifier");

            if (authorization.Callback == null)
            {
                authorization.Callback = this.Channel.GetRequestFromContext().GetPublicFacingUrl()
                                         .StripMessagePartsFromQueryString(this.Channel.MessageDescriptions.Get(typeof(EndUserAuthorizationSuccessResponseBase), Protocol.Default.Version))
                                         .StripMessagePartsFromQueryString(this.Channel.MessageDescriptions.Get(typeof(EndUserAuthorizationFailedResponse), Protocol.Default.Version));
                authorization.SaveChanges();
            }

            var request = new EndUserAuthorizationRequestC(this.AuthorizationServer)
            {
                ClientIdentifier = this.ClientIdentifier,
                Callback         = authorization.Callback,
            };

            request.Scope.ResetContents(authorization.Scope);

            // Mitigate XSRF attacks by including a state value that would be unpredictable between users, but
            // verifiable for the same user/session.
            // If the host is implementing the authorization tracker though, they're handling this protection themselves.
            var cookies = new List <CookieHeaderValue>();

            if (this.AuthorizationTracker == null)
            {
                string xsrfKey = MessagingUtilities.GetNonCryptoRandomDataAsBase64(16, useWeb64: true);
                cookies.Add(new CookieHeaderValue(XsrfCookieName, xsrfKey)
                {
                    HttpOnly = true,
                    Secure   = FormsAuthentication.RequireSSL,
                });
                request.ClientState = xsrfKey;
            }

            var response = await this.Channel.PrepareResponseAsync(request, cancellationToken);

            response.Headers.AddCookies(cookies);

            return(response);
        }
Example #21
0
        /// <summary>
        /// Generates a URL that the user's browser can be directed to in order to authorize
        /// this client to access protected data at some resource server.
        /// </summary>
        /// <param name="authorization">The authorization state that is tracking this particular request.  Optional.</param>
        /// <param name="implicitResponseType">
        /// <c>true</c> to request an access token in the fragment of the response's URL;
        /// <c>false</c> to authenticate to the authorization server and acquire the access token (and possibly a refresh token) via a private channel.
        /// </param>
        /// <param name="state">The client state that should be returned with the authorization response.</param>
        /// <returns>
        /// A message to send to the authorization server.
        /// </returns>
        internal EndUserAuthorizationRequest PrepareRequestUserAuthorization(IAuthorizationState authorization, bool implicitResponseType = false, string state = null)
        {
            Requires.NotNull(authorization, "authorization");
            RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier));

            if (authorization.Callback == null)
            {
                authorization.Callback = new Uri("http://localhost/");
            }

            var request = implicitResponseType ? (EndUserAuthorizationRequest) new EndUserAuthorizationImplicitRequestC(this.AuthorizationServer) : new EndUserAuthorizationRequestC(this.AuthorizationServer);

            request.ClientIdentifier = this.ClientIdentifier;
            request.Callback         = authorization.Callback;
            request.ClientState      = state;
            request.Scope.ResetContents(authorization.Scope);

            return(request);
        }
Example #22
0
        private byte[] CalculateSignature(byte[] bytesToSign, string symmetricSecretHandle)
        {
            Requires.NotNull(bytesToSign, "bytesToSign");
            RequiresEx.ValidState(this.asymmetricSigning != null || this.cryptoKeyStore != null);

            if (this.asymmetricSigning != null)
            {
                using (var hasher = SHA1.Create()) {
                    return(this.asymmetricSigning.SignData(bytesToSign, hasher));
                }
            }
            else
            {
                var key = this.cryptoKeyStore.GetKey(this.cryptoKeyBucket, symmetricSecretHandle);
                ErrorUtilities.VerifyProtocol(key != null, MessagingStrings.MissingDecryptionKeyForHandle, this.cryptoKeyBucket, symmetricSecretHandle);
                using (var symmetricHasher = HmacAlgorithms.Create(HmacAlgorithms.HmacSha256, key.Key)) {
                    return(symmetricHasher.ComputeHash(bytesToSign));
                }
            }
        }
Example #23
0
        private void GetRequestedClaims(out string required, out string optional)
        {
            RequiresEx.ValidState(this.ClaimsRequested != null);

            var nonEmptyClaimTypes = this.ClaimsRequested.Where(c => c.Name != null);

            var optionalClaims = from claim in nonEmptyClaimTypes
                                 where claim.IsOptional
                                 select claim.Name;
            var requiredClaims = from claim in nonEmptyClaimTypes
                                 where !claim.IsOptional
                                 select claim.Name;

            string[] requiredClaimsArray = requiredClaims.ToArray();
            string[] optionalClaimsArray = optionalClaims.ToArray();
            required = string.Join(" ", requiredClaimsArray);
            optional = string.Join(" ", optionalClaimsArray);
            Assumes.True(required != null);
            Assumes.True(optional != null);
        }
Example #24
0
        /// <summary>
        /// Processes an incoming authorization-granted message from an SP and obtains an access token.
        /// </summary>
        /// <param name="openIdAuthenticationResponse">The OpenID authentication response that may be carrying an authorized request token.</param>
        /// <returns>
        /// The access token, or null if OAuth authorization was denied by the user or service provider.
        /// </returns>
        /// <remarks>
        /// The access token, if granted, is automatically stored in the <see cref="ConsumerBase.TokenManager"/>.
        /// The token manager instance must implement <see cref="IOpenIdOAuthTokenManager"/>.
        /// </remarks>
        public AuthorizedTokenResponse ProcessUserAuthorization(IAuthenticationResponse openIdAuthenticationResponse)
        {
            Requires.NotNull(openIdAuthenticationResponse, "openIdAuthenticationResponse");
            RequiresEx.ValidState(this.TokenManager is IOpenIdOAuthTokenManager);
            var openidTokenManager = this.TokenManager as IOpenIdOAuthTokenManager;

            ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName);

            // The OAuth extension is only expected in positive assertion responses.
            if (openIdAuthenticationResponse.Status != AuthenticationStatus.Authenticated)
            {
                return(null);
            }

            // Retrieve the OAuth extension
            var positiveAuthorization = openIdAuthenticationResponse.GetExtension <AuthorizationApprovedResponse>();

            if (positiveAuthorization == null)
            {
                return(null);
            }

            // Prepare a message to exchange the request token for an access token.
            // We are careful to use a v1.0 message version so that the oauth_verifier is not required.
            var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, Protocol.V10.Version)
            {
                RequestToken = positiveAuthorization.RequestToken,
                ConsumerKey  = this.ConsumerKey,
            };

            // Retrieve the access token and store it in the token manager.
            openidTokenManager.StoreOpenIdAuthorizedRequestToken(this.ConsumerKey, positiveAuthorization);
            var grantAccess = this.Channel.Request <AuthorizedTokenResponse>(requestAccess);

            this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, positiveAuthorization.RequestToken, grantAccess.AccessToken, grantAccess.TokenSecret);

            // Provide the caller with the access token so it may be associated with the user
            // that is logging in.
            return(grantAccess);
        }
        /// <summary>
        /// Gets the value to use for the openid.signed parameter.
        /// </summary>
        /// <param name="signedMessage">The signable message.</param>
        /// <returns>
        /// A comma-delimited list of parameter names, omitting the 'openid.' prefix, that determines
        /// the inclusion and order of message parts that will be signed.
        /// </returns>
        private string GetSignedParameterOrder(ITamperResistantOpenIdMessage signedMessage)
        {
            RequiresEx.ValidState(this.Channel != null);
            Requires.NotNull(signedMessage, "signedMessage");

            Protocol protocol = Protocol.Lookup(signedMessage.Version);

            MessageDescription description = this.Channel.MessageDescriptions.Get(signedMessage);
            var signedParts =
                from part in description.Mapping.Values
                where (part.RequiredProtection & System.Net.Security.ProtectionLevel.Sign) != 0 &&
                part.GetValue(signedMessage) != null
                select part.Name;
            string prefix = Protocol.V20.openid.Prefix;

            ErrorUtilities.VerifyInternal(signedParts.All(name => name.StartsWith(prefix, StringComparison.Ordinal)), "All signed message parts must start with 'openid.'.");

            if (this.opSecuritySettings.SignOutgoingExtensions)
            {
                // Tack on any ExtraData parameters that start with 'openid.'.
                List <string> extraSignedParameters = new List <string>(signedMessage.ExtraData.Count);
                foreach (string key in signedMessage.ExtraData.Keys)
                {
                    if (key.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal))
                    {
                        extraSignedParameters.Add(key);
                    }
                    else
                    {
                        Logger.Signatures.DebugFormat("The extra parameter '{0}' will not be signed because it does not start with 'openid.'.", key);
                    }
                }
                signedParts = signedParts.Concat(extraSignedParameters);
            }

            int    skipLength   = prefix.Length;
            string signedFields = string.Join(",", signedParts.Select(name => name.Substring(skipLength)).ToArray());

            return(signedFields);
        }
Example #26
0
        /// <summary>
        /// Adds Javascript snippets to the page to help the Information Card selector do its work,
        /// or to downgrade gracefully if the user agent lacks an Information Card selector.
        /// </summary>
        private void RenderSupportingScript()
        {
            RequiresEx.ValidState(this.infoCardSupportedPanel != null);

            this.Page.ClientScript.RegisterClientScriptResource(typeof(InfoCardSelector), ScriptResourceName);

            if (this.RenderMode == RenderMode.Static)
            {
                this.Page.ClientScript.RegisterStartupScript(
                    typeof(InfoCardSelector),
                    "SelectorSupportingScript_" + this.ClientID,
                    string.Format(CultureInfo.InvariantCulture, "document.infoCard.checkStatic('{0}', '{1}');", this.infoCardSupportedPanel.ClientID, this.infoCardNotSupportedPanel.ClientID),
                    true);
            }
            else if (RenderMode == RenderMode.Dynamic)
            {
                this.Page.ClientScript.RegisterStartupScript(
                    typeof(InfoCardSelector),
                    "SelectorSupportingScript_" + this.ClientID,
                    string.Format(CultureInfo.InvariantCulture, "document.infoCard.checkDynamic('{0}', '{1}');", this.infoCardSupportedPanel.ClientID, this.infoCardNotSupportedPanel.ClientID),
                    true);
            }
        }
Example #27
0
        /// <summary>
        /// Adds query parameters for OpenID extensions to the request directed
        /// at the OpenID provider.
        /// </summary>
        /// <param name="extensionTypeUri">The extension type URI.</param>
        /// <param name="arguments">The arguments for this extension to add to the message.</param>
        public void AddExtensionArguments(string extensionTypeUri, IDictionary <string, string> arguments)
        {
            RequiresEx.ValidState(!this.ReadMode);
            Requires.NotNullOrEmpty(extensionTypeUri, "extensionTypeUri");
            Requires.NotNull(arguments, "arguments");
            if (arguments.Count == 0)
            {
                return;
            }

            IDictionary <string, string> extensionArgs;

            if (!this.extensions.TryGetValue(extensionTypeUri, out extensionArgs))
            {
                this.extensions.Add(extensionTypeUri, extensionArgs = new Dictionary <string, string>(arguments.Count));
            }

            ErrorUtilities.VerifyProtocol(extensionArgs.Count == 0, OpenIdStrings.ExtensionAlreadyAddedWithSameTypeURI, extensionTypeUri);
            foreach (var pair in arguments)
            {
                extensionArgs.Add(pair.Key, pair.Value);
            }
        }
        /// <summary>
        /// Prepares an identity assertion on behalf of one of this Provider's
        /// members in order to redirect the user agent to a relying party
        /// web site and log him/her in immediately in one uninterrupted step.
        /// </summary>
        /// <param name="providerEndpoint">The absolute URL on the Provider site that receives OpenID messages.</param>
        /// <param name="relyingPartyRealm">The URL of the Relying Party web site.
        /// This will typically be the home page, but may be a longer URL if
        /// that Relying Party considers the scope of its realm to be more specific.
        /// The URL provided here must allow discovery of the Relying Party's
        /// XRDS document that advertises its OpenID RP endpoint.</param>
        /// <param name="claimedIdentifier">The Identifier you are asserting your member controls.</param>
        /// <param name="localIdentifier">The Identifier you know your user by internally.  This will typically
        /// be the same as <paramref name="claimedIdentifier" />.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <param name="extensions">The extensions.</param>
        /// <returns>
        /// A <see cref="HttpResponseMessage" /> object describing the HTTP response to send
        /// the user agent to allow the redirect with assertion to happen.
        /// </returns>
        public async Task <HttpResponseMessage> PrepareUnsolicitedAssertionAsync(Uri providerEndpoint, Realm relyingPartyRealm, Identifier claimedIdentifier, Identifier localIdentifier, CancellationToken cancellationToken = default(CancellationToken), params IExtensionMessage[] extensions)
        {
            Requires.NotNull(providerEndpoint, "providerEndpoint");
            Requires.That(providerEndpoint.IsAbsoluteUri, "providerEndpoint", OpenIdStrings.AbsoluteUriRequired);
            Requires.NotNull(relyingPartyRealm, "relyingPartyRealm");
            Requires.NotNull(claimedIdentifier, "claimedIdentifier");
            Requires.NotNull(localIdentifier, "localIdentifier");
            RequiresEx.ValidState(this.Channel.HostFactories != null);

            // Although the RP should do their due diligence to make sure that this OP
            // is authorized to send an assertion for the given claimed identifier,
            // do due diligence by performing our own discovery on the claimed identifier
            // and make sure that it is tied to this OP and OP local identifier.
            if (this.SecuritySettings.UnsolicitedAssertionVerification != ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.NeverVerify)
            {
                var serviceEndpoint     = IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, localIdentifier, new ProviderEndpointDescription(providerEndpoint, Protocol.Default.Version), null, null);
                var discoveredEndpoints = await this.discoveryServices.DiscoverAsync(claimedIdentifier, cancellationToken);

                if (!discoveredEndpoints.Contains(serviceEndpoint))
                {
                    Logger.OpenId.WarnFormat(
                        "Failed to send unsolicited assertion for {0} because its discovered services did not include this endpoint: {1}{2}{1}Discovered endpoints: {1}{3}",
                        claimedIdentifier,
                        Environment.NewLine,
                        serviceEndpoint,
                        discoveredEndpoints.ToStringDeferred(true));

                    // Only FAIL if the setting is set for it.
                    if (this.securitySettings.UnsolicitedAssertionVerification == ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.RequireSuccess)
                    {
                        ErrorUtilities.ThrowProtocol(OpenIdStrings.UnsolicitedAssertionForUnrelatedClaimedIdentifier, claimedIdentifier);
                    }
                }
            }

            Logger.OpenId.InfoFormat("Preparing unsolicited assertion for {0}", claimedIdentifier);
            RelyingPartyEndpointDescription returnToEndpoint = null;
            var returnToEndpoints = await relyingPartyRealm.DiscoverReturnToEndpointsAsync(this.Channel.HostFactories, true, cancellationToken);

            if (returnToEndpoints != null)
            {
                returnToEndpoint = returnToEndpoints.FirstOrDefault();
            }
            ErrorUtilities.VerifyProtocol(returnToEndpoint != null, OpenIdStrings.NoRelyingPartyEndpointDiscovered, relyingPartyRealm);

            var positiveAssertion = new PositiveAssertionResponse(returnToEndpoint)
            {
                ProviderEndpoint  = providerEndpoint,
                ClaimedIdentifier = claimedIdentifier,
                LocalIdentifier   = localIdentifier,
            };

            if (extensions != null)
            {
                foreach (IExtensionMessage extension in extensions)
                {
                    positiveAssertion.Extensions.Add(extension);
                }
            }

            Reporting.RecordEventOccurrence(this, "PrepareUnsolicitedAssertion");
            return(await this.Channel.PrepareResponseAsync(positiveAssertion, cancellationToken));
        }
 /// <summary>
 /// Creates a signing element that includes all the signing elements this service provider supports.
 /// </summary>
 /// <returns>The created signing element.</returns>
 internal ITamperProtectionChannelBindingElement CreateTamperProtectionElement()
 {
     RequiresEx.ValidState(this.TamperProtectionElements != null);
     return(new SigningBindingElementChain(this.TamperProtectionElements.Select(el => (ITamperProtectionChannelBindingElement)el.Clone()).ToArray()));
 }
        /// <summary>
        /// Automatically sends the appropriate response to the user agent
        /// and signals ASP.NET to short-circuit the page execution pipeline
        /// now that the response has been completed.
        /// Not safe to call from ASP.NET web forms.
        /// </summary>
        /// <remarks>
        /// Requires a current HttpContext.
        /// This call is not safe to make from an ASP.NET web form (.aspx file or code-behind) because
        /// ASP.NET will render HTML after the protocol message has been sent, which will corrupt the response.
        /// Use the <see cref="Send()"/> method instead for web forms.
        /// </remarks>
        public virtual void Respond()
        {
            RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);

            this.Respond(HttpContext.Current);
        }