diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2013-03-26 11:19:06 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2013-03-26 11:19:06 -0700 |
commit | 3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb (patch) | |
tree | c15816c3d7f6e74334553f2ff98605ce1c22c538 /src/DotNetOpenAuth.OAuth2.Client/OAuth2 | |
parent | 5e9014f36b2d53b8e419918675df636540ea24e2 (diff) | |
parent | e6f7409f4caceb7bc2a5b4ddbcb1a4097af340f2 (diff) | |
download | DotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.zip DotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.tar.gz DotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.tar.bz2 |
Move to HttpClient throughout library.
Diffstat (limited to 'src/DotNetOpenAuth.OAuth2.Client/OAuth2')
6 files changed, 166 insertions, 122 deletions
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/BearerTokenHttpMessageHandler.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/BearerTokenHttpMessageHandler.cs index da4c869..92f882f 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/BearerTokenHttpMessageHandler.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/BearerTokenHttpMessageHandler.cs @@ -72,21 +72,21 @@ namespace DotNetOpenAuth.OAuth2 { /// <returns> /// Returns <see cref="T:System.Threading.Tasks.Task`1" />. The task object representing the asynchronous operation. /// </returns> - protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { string bearerToken = this.BearerToken; if (bearerToken == null) { - ErrorUtilities.VerifyProtocol(!this.Authorization.AccessTokenExpirationUtc.HasValue || this.Authorization.AccessTokenExpirationUtc < DateTime.UtcNow || this.Authorization.RefreshToken != null, ClientStrings.AuthorizationExpired); + ErrorUtilities.VerifyProtocol(!this.Authorization.AccessTokenExpirationUtc.HasValue || this.Authorization.AccessTokenExpirationUtc >= DateTime.UtcNow || this.Authorization.RefreshToken != null, ClientStrings.AuthorizationExpired); if (this.Authorization.AccessTokenExpirationUtc.HasValue && this.Authorization.AccessTokenExpirationUtc.Value < DateTime.UtcNow) { ErrorUtilities.VerifyProtocol(this.Authorization.RefreshToken != null, ClientStrings.AccessTokenRefreshFailed); - this.Client.RefreshAuthorization(this.Authorization); + await this.Client.RefreshAuthorizationAsync(this.Authorization, cancellationToken: cancellationToken); } bearerToken = this.Authorization.AccessToken; } request.Headers.Authorization = new AuthenticationHeaderValue(Protocol.BearerHttpAuthorizationScheme, bearerToken); - return base.SendAsync(request, cancellationToken); + return await base.SendAsync(request, cancellationToken); } } } diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs index cf57618..aba0290 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs @@ -9,12 +9,16 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using System.Collections.Generic; using System.Collections.Specialized; using System.Net; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; using System.Web; using System.Xml; - using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.Messages; + using Validation; + /// <summary> /// The messaging channel used by OAuth 2.0 Clients. /// </summary> @@ -32,10 +36,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { }; /// <summary> - /// Initializes a new instance of the <see cref="OAuth2ClientChannel"/> class. + /// Initializes a new instance of the <see cref="OAuth2ClientChannel" /> class. /// </summary> - internal OAuth2ClientChannel() - : base(MessageTypes) { + /// <param name="hostFactories">The host factories.</param> + internal OAuth2ClientChannel(IHostFactories hostFactories) + : base(MessageTypes, hostFactories: hostFactories) { } /// <summary> @@ -64,11 +69,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// The <see cref="HttpWebRequest"/> prepared to send the request. /// </returns> /// <remarks> - /// This method must be overridden by a derived class, unless the <see cref="Channel.RequestCore"/> method + /// This method must be overridden by a derived class, unless the <see cref="Channel.RequestCoreAsync"/> method /// is overridden and does not require this method. /// </remarks> - protected override HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage request) { - HttpWebRequest httpRequest; + protected override HttpRequestMessage CreateHttpRequest(IDirectedProtocolMessage request) { + HttpRequestMessage httpRequest; if ((request.HttpMethods & HttpDeliveryMethods.GetRequest) != 0) { httpRequest = InitializeRequestAsGet(request); } else if ((request.HttpMethods & HttpDeliveryMethods.PostRequest) != 0) { @@ -88,16 +93,17 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// The deserialized message parts, if found. Null otherwise. /// </returns> /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception> - protected override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) { + protected override async Task<IDictionary<string, string>> ReadFromResponseCoreAsync(HttpResponseMessage response, CancellationToken cancellationToken) { // The spec says direct responses should be JSON objects, but Facebook uses HttpFormUrlEncoded instead, calling it text/plain // Others return text/javascript. Again bad. - string body = response.GetResponseReader().ReadToEnd(); - if (response.ContentType.MediaType == JsonEncoded || response.ContentType.MediaType == JsonTextEncoded) { + string body = await response.Content.ReadAsStringAsync(); + var contentType = response.Content.Headers.ContentType.MediaType; + if (contentType == JsonEncoded || contentType == JsonTextEncoded) { return this.DeserializeFromJson(body); - } else if (response.ContentType.MediaType == HttpFormUrlEncoded || response.ContentType.MediaType == PlainTextEncoded) { + } else if (contentType == HttpFormUrlEncoded || contentType == PlainTextEncoded) { return HttpUtility.ParseQueryString(body).ToDictionary(); } else { - throw ErrorUtilities.ThrowProtocol(ClientStrings.UnexpectedResponseContentType, response.ContentType.MediaType); + throw ErrorUtilities.ThrowProtocol(ClientStrings.UnexpectedResponseContentType, contentType); } } @@ -105,21 +111,24 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// Gets the protocol message that may be embedded in the given HTTP request. /// </summary> /// <param name="request">The request to search for an embedded message.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// The deserialized message, if one is found. Null otherwise. /// </returns> - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request) { - Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.GetPublicFacingUrl().AbsoluteUri); + protected override async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + Requires.NotNull(request, "request"); + + Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.Method, request.RequestUri.AbsoluteUri); - var fields = request.GetQueryStringBeforeRewriting().ToDictionary(); + var fields = HttpUtility.ParseQueryString(request.RequestUri.Query).ToDictionary(); // Also read parameters from the fragment, if it's available. // Typically the fragment is not available because the browser doesn't send it to a web server // but this request may have been fabricated by an installed desktop app, in which case // the fragment is available. - string fragment = request.GetPublicFacingUrl().Fragment; + string fragment = request.RequestUri.Fragment; if (!string.IsNullOrEmpty(fragment)) { - foreach (var pair in HttpUtility.ParseQueryString(fragment.Substring(1)).ToDictionary()) { + foreach (var pair in HttpUtility.ParseQueryString(fragment.Substring(1)).AsKeyValuePairs()) { fields.Add(pair.Key, pair.Value); } } @@ -146,7 +155,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <remarks> /// This method implements spec OAuth V1.0 section 5.3. /// </remarks> - protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) { + protected override HttpResponseMessage PrepareDirectResponse(IProtocolMessage response) { // Clients don't ever send direct responses. throw new NotImplementedException(); } @@ -155,7 +164,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// Performs additional processing on an outgoing web request before it is sent to the remote server. /// </summary> /// <param name="request">The request.</param> - protected override void PrepareHttpWebRequest(HttpWebRequest request) { + protected override void PrepareHttpWebRequest(HttpRequestMessage request) { base.PrepareHttpWebRequest(request); if (this.ClientCredentialApplicator != null) { diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs index d66f4fd..9730321 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs @@ -13,8 +13,9 @@ namespace DotNetOpenAuth.OAuth2 { using System.Net.Http; using System.Security; using System.Text; + using System.Threading; + using System.Threading.Tasks; using System.Xml; - using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.ChannelElements; using DotNetOpenAuth.OAuth2.Messages; @@ -33,10 +34,10 @@ namespace DotNetOpenAuth.OAuth2 { /// The tool to use to apply client credentials to authenticated requests to the Authorization Server. /// May be <c>null</c> for clients with no secret or other means of authentication. /// </param> - protected ClientBase(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, ClientCredentialApplicator clientCredentialApplicator = null) { + protected ClientBase(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, ClientCredentialApplicator clientCredentialApplicator = null, IHostFactories hostFactories = null) { Requires.NotNull(authorizationServer, "authorizationServer"); this.AuthorizationServer = authorizationServer; - this.Channel = new OAuth2ClientChannel(); + this.Channel = new OAuth2ClientChannel(hostFactories); this.ClientIdentifier = clientIdentifier; this.ClientCredentialApplicator = clientCredentialApplicator; } @@ -116,11 +117,15 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="request">The request for protected resources from the service provider.</param> /// <param name="authorization">The authorization for this request previously obtained via OAuth.</param> - public void AuthorizeRequest(HttpWebRequest request, IAuthorizationState authorization) { + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// A task that completes with the asynchronous operation. + /// </returns> + public Task AuthorizeRequestAsync(HttpWebRequest request, IAuthorizationState authorization, CancellationToken cancellationToken) { Requires.NotNull(request, "request"); Requires.NotNull(authorization, "authorization"); - this.AuthorizeRequest(request.Headers, authorization); + return this.AuthorizeRequestAsync(request.Headers, authorization, cancellationToken); } /// <summary> @@ -129,7 +134,11 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="requestHeaders">The headers on the request for protected resources from the service provider.</param> /// <param name="authorization">The authorization for this request previously obtained via OAuth.</param> - public void AuthorizeRequest(WebHeaderCollection requestHeaders, IAuthorizationState authorization) { + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// A task that completes with the asynchronous operation. + /// </returns> + public async Task AuthorizeRequestAsync(WebHeaderCollection requestHeaders, IAuthorizationState authorization, CancellationToken cancellationToken) { Requires.NotNull(requestHeaders, "requestHeaders"); Requires.NotNull(authorization, "authorization"); Requires.That(!string.IsNullOrEmpty(authorization.AccessToken), "authorization", "AccessToken required."); @@ -137,7 +146,7 @@ namespace DotNetOpenAuth.OAuth2 { if (authorization.AccessTokenExpirationUtc.HasValue && authorization.AccessTokenExpirationUtc.Value < DateTime.UtcNow) { ErrorUtilities.VerifyProtocol(authorization.RefreshToken != null, ClientStrings.AccessTokenRefreshFailed); - this.RefreshAuthorization(authorization); + await this.RefreshAuthorizationAsync(authorization, cancellationToken: cancellationToken); } AuthorizeRequest(requestHeaders, authorization.AccessToken); @@ -174,13 +183,14 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="authorization">The authorization to update.</param> /// <param name="skipIfUsefulLifeExceeds">If given, the access token will <em>not</em> be refreshed if its remaining lifetime exceeds this value.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A value indicating whether the access token was actually renewed; <c>true</c> if it was renewed, or <c>false</c> if it still had useful life remaining.</returns> /// <remarks> /// This method may modify the value of the <see cref="IAuthorizationState.RefreshToken"/> property on /// the <paramref name="authorization"/> parameter if the authorization server has cycled out your refresh token. /// If the parameter value was updated, this method calls <see cref="IAuthorizationState.SaveChanges"/> on that instance. /// </remarks> - public bool RefreshAuthorization(IAuthorizationState authorization, TimeSpan? skipIfUsefulLifeExceeds = null) { + public async Task<bool> RefreshAuthorizationAsync(IAuthorizationState authorization, TimeSpan? skipIfUsefulLifeExceeds = null, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(authorization, "authorization"); Requires.That(!string.IsNullOrEmpty(authorization.RefreshToken), "authorization", "RefreshToken required."); @@ -200,7 +210,7 @@ namespace DotNetOpenAuth.OAuth2 { this.ApplyClientCredential(request); - var response = this.Channel.Request<AccessTokenSuccessResponse>(request); + var response = await this.Channel.RequestAsync<AccessTokenSuccessResponse>(request, cancellationToken); UpdateAuthorizationWithResponse(authorization, response); return true; } @@ -211,12 +221,13 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="refreshToken">The refresh token.</param> /// <param name="scope">The scope subset desired in the access token.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A description of the obtained access token, and possibly a new refresh token.</returns> /// <remarks> /// If the return value includes a new refresh token, the old refresh token should be discarded and /// replaced with the new one. /// </remarks> - public IAuthorizationState GetScopedAccessToken(string refreshToken, HashSet<string> scope) { + public async Task<IAuthorizationState> GetScopedAccessTokenAsync(string refreshToken, HashSet<string> scope, CancellationToken cancellationToken) { Requires.NotNullOrEmpty(refreshToken, "refreshToken"); Requires.NotNull(scope, "scope"); @@ -227,7 +238,7 @@ namespace DotNetOpenAuth.OAuth2 { this.ApplyClientCredential(request); - var response = this.Channel.Request<AccessTokenSuccessResponse>(request); + var response = await this.Channel.RequestAsync<AccessTokenSuccessResponse>(request, cancellationToken); var authorization = new AuthorizationState(); UpdateAuthorizationWithResponse(authorization, response); @@ -240,8 +251,11 @@ namespace DotNetOpenAuth.OAuth2 { /// <param name="userName">The resource owner's username, as it is known by the authorization server.</param> /// <param name="password">The resource owner's account password.</param> /// <param name="scopes">The desired scope of access.</param> - /// <returns>The result, containing the tokens if successful.</returns> - public IAuthorizationState ExchangeUserCredentialForToken(string userName, string password, IEnumerable<string> scopes = null) { + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The result, containing the tokens if successful. + /// </returns> + public Task<IAuthorizationState> ExchangeUserCredentialForTokenAsync(string userName, string password, IEnumerable<string> scopes = null, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNullOrEmpty(userName, "userName"); Requires.NotNull(password, "password"); @@ -250,17 +264,20 @@ namespace DotNetOpenAuth.OAuth2 { Password = password, }; - return this.RequestAccessToken(request, scopes); + return this.RequestAccessTokenAsync(request, scopes, cancellationToken); } /// <summary> /// Obtains an access token for accessing client-controlled resources on the resource server. /// </summary> /// <param name="scopes">The desired scopes.</param> - /// <returns>The result of the authorization request.</returns> - public IAuthorizationState GetClientAccessToken(IEnumerable<string> scopes = null) { + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The result of the authorization request. + /// </returns> + public Task<IAuthorizationState> GetClientAccessTokenAsync(IEnumerable<string> scopes = null, CancellationToken cancellationToken = default(CancellationToken)) { var request = new AccessTokenClientCredentialsRequest(this.AuthorizationServer.TokenEndpoint, this.AuthorizationServer.Version); - return this.RequestAccessToken(request, scopes); + return this.RequestAccessTokenAsync(request, scopes, cancellationToken); } /// <summary> @@ -322,7 +339,11 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="authorizationState">The authorization state to update.</param> /// <param name="authorizationSuccess">The authorization success message obtained from the authorization server.</param> - internal void UpdateAuthorizationWithResponse(IAuthorizationState authorizationState, EndUserAuthorizationSuccessAuthCodeResponse authorizationSuccess) { + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// A task that completes with the asynchronous operation. + /// </returns> + internal async Task UpdateAuthorizationWithResponseAsync(IAuthorizationState authorizationState, EndUserAuthorizationSuccessAuthCodeResponse authorizationSuccess, CancellationToken cancellationToken) { Requires.NotNull(authorizationState, "authorizationState"); Requires.NotNull(authorizationSuccess, "authorizationSuccess"); @@ -332,7 +353,7 @@ namespace DotNetOpenAuth.OAuth2 { AuthorizationCode = authorizationSuccess.AuthorizationCode, }; this.ApplyClientCredential(accessTokenRequest); - IProtocolMessage accessTokenResponse = this.Channel.Request(accessTokenRequest); + IProtocolMessage accessTokenResponse = await this.Channel.RequestAsync(accessTokenRequest, cancellationToken); var accessTokenSuccess = accessTokenResponse as AccessTokenSuccessResponse; var failedAccessTokenResponse = accessTokenResponse as AccessTokenFailedResponse; if (accessTokenSuccess != null) { @@ -387,8 +408,11 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="request">The request message.</param> /// <param name="scopes">The scopes requested by the client.</param> - /// <returns>The result of the request.</returns> - private IAuthorizationState RequestAccessToken(ScopedAccessTokenRequest request, IEnumerable<string> scopes = null) { + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The result of the request. + /// </returns> + private async Task<IAuthorizationState> RequestAccessTokenAsync(ScopedAccessTokenRequest request, IEnumerable<string> scopes, CancellationToken cancellationToken) { Requires.NotNull(request, "request"); var authorizationState = new AuthorizationState(scopes); @@ -397,7 +421,7 @@ namespace DotNetOpenAuth.OAuth2 { this.ApplyClientCredential(request); request.Scope.UnionWith(authorizationState.Scope); - var response = this.Channel.Request(request); + var response = await this.Channel.RequestAsync(request, cancellationToken); var success = response as AccessTokenSuccessResponse; var failure = response as AccessTokenFailedResponse; ErrorUtilities.VerifyProtocol(success != null || failure != null, MessagingStrings.UnexpectedMessageReceivedOfMany); diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs index 39ba1cb..769cf56 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OAuth2 { using System.Collections.Generic; using System.Linq; using System.Net; + using System.Net.Http; using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.Messages; @@ -75,7 +76,7 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param> /// <param name="request">The outbound message to apply authentication information to.</param> - public virtual void ApplyClientCredential(string clientIdentifier, HttpWebRequest request) { + public virtual void ApplyClientCredential(string clientIdentifier, HttpRequestMessage request) { } /// <summary> @@ -126,7 +127,7 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param> /// <param name="request">The outbound message to apply authentication information to.</param> - public override void ApplyClientCredential(string clientIdentifier, HttpWebRequest request) { + public override void ApplyClientCredential(string clientIdentifier, HttpRequestMessage request) { if (clientIdentifier != null) { if (this.credential != null) { ErrorUtilities.VerifyHost( diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs index dcb3826..7a52c0f 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs @@ -8,9 +8,11 @@ namespace DotNetOpenAuth.OAuth2 { using System; using System.Collections.Generic; using System.Linq; + using System.Net.Http; using System.Text; + using System.Threading; + using System.Threading.Tasks; using System.Web; - using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.Messages; using Validation; @@ -26,8 +28,8 @@ namespace DotNetOpenAuth.OAuth2 { /// <param name="authorizationServer">The token issuer.</param> /// <param name="clientIdentifier">The client identifier.</param> /// <param name="clientSecret">The client secret.</param> - public UserAgentClient(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null) - : this(authorizationServer, clientIdentifier, DefaultSecretApplicator(clientSecret)) { + public UserAgentClient(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null, IHostFactories hostFactories = null) + : this(authorizationServer, clientIdentifier, DefaultSecretApplicator(clientSecret), hostFactories) { } /// <summary> @@ -37,8 +39,8 @@ namespace DotNetOpenAuth.OAuth2 { /// <param name="tokenEndpoint">The token endpoint.</param> /// <param name="clientIdentifier">The client identifier.</param> /// <param name="clientSecret">The client secret.</param> - public UserAgentClient(Uri authorizationEndpoint, Uri tokenEndpoint, string clientIdentifier = null, string clientSecret = null) - : this(authorizationEndpoint, tokenEndpoint, clientIdentifier, DefaultSecretApplicator(clientSecret)) { + public UserAgentClient(Uri authorizationEndpoint, Uri tokenEndpoint, string clientIdentifier = null, string clientSecret = null, IHostFactories hostFactories = null) + : this(authorizationEndpoint, tokenEndpoint, clientIdentifier, DefaultSecretApplicator(clientSecret), hostFactories) { } /// <summary> @@ -51,8 +53,8 @@ namespace DotNetOpenAuth.OAuth2 { /// The tool to use to apply client credentials to authenticated requests to the Authorization Server. /// May be <c>null</c> for clients with no secret or other means of authentication. /// </param> - public UserAgentClient(Uri authorizationEndpoint, Uri tokenEndpoint, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator) - : this(new AuthorizationServerDescription { AuthorizationEndpoint = authorizationEndpoint, TokenEndpoint = tokenEndpoint }, clientIdentifier, clientCredentialApplicator) { + public UserAgentClient(Uri authorizationEndpoint, Uri tokenEndpoint, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator, IHostFactories hostFactories = null) + : this(new AuthorizationServerDescription { AuthorizationEndpoint = authorizationEndpoint, TokenEndpoint = tokenEndpoint }, clientIdentifier, clientCredentialApplicator, hostFactories) { Requires.NotNull(authorizationEndpoint, "authorizationEndpoint"); Requires.NotNull(tokenEndpoint, "tokenEndpoint"); } @@ -66,8 +68,8 @@ namespace DotNetOpenAuth.OAuth2 { /// The tool to use to apply client credentials to authenticated requests to the Authorization Server. /// May be <c>null</c> for clients with no secret or other means of authentication. /// </param> - public UserAgentClient(AuthorizationServerDescription authorizationServer, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator) - : base(authorizationServer, clientIdentifier, clientCredentialApplicator) { + public UserAgentClient(AuthorizationServerDescription authorizationServer, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator, IHostFactories hostFactories = null) + : base(authorizationServer, clientIdentifier, clientCredentialApplicator, hostFactories) { } /// <summary> @@ -77,15 +79,16 @@ namespace DotNetOpenAuth.OAuth2 { /// <param name="scope">The scope of authorized access requested.</param> /// <param name="state">The client state that should be returned with the authorization response.</param> /// <param name="returnTo">The URL that the authorization response should be sent to via a user-agent redirect.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// A fully-qualified URL suitable to initiate the authorization flow. /// </returns> - public Uri RequestUserAuthorization(IEnumerable<string> scope = null, string state = null, Uri returnTo = null) { + public Task<Uri> RequestUserAuthorizationAsync(IEnumerable<string> scope = null, string state = null, Uri returnTo = null, CancellationToken cancellationToken = default(CancellationToken)) { var authorization = new AuthorizationState(scope) { Callback = returnTo, }; - return this.RequestUserAuthorization(authorization, state: state); + return this.RequestUserAuthorizationAsync(authorization, state: state, cancellationToken: cancellationToken); } /// <summary> @@ -93,20 +96,20 @@ namespace DotNetOpenAuth.OAuth2 { /// 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="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 Uri RequestUserAuthorization(IAuthorizationState authorization, bool implicitResponseType = false, string state = null) { + 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); - return this.Channel.PrepareResponse(request).GetDirectUriRequest(this.Channel); + var response = await this.Channel.PrepareResponseAsync(request, cancellationToken); + return response.GetDirectUriRequest(); } /// <summary> @@ -114,21 +117,24 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="actualRedirectUrl">The actual URL of the incoming HTTP request.</param> /// <param name="authorizationState">The authorization.</param> - /// <returns>The granted authorization, or <c>null</c> if the incoming HTTP request did not contain an authorization server response or authorization was rejected.</returns> - public IAuthorizationState ProcessUserAuthorization(Uri actualRedirectUrl, IAuthorizationState authorizationState = null) { + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The granted authorization, or <c>null</c> if the incoming HTTP request did not contain an authorization server response or authorization was rejected. + /// </returns> + public async Task<IAuthorizationState> ProcessUserAuthorizationAsync(Uri actualRedirectUrl, IAuthorizationState authorizationState = null, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(actualRedirectUrl, "actualRedirectUrl"); if (authorizationState == null) { authorizationState = new AuthorizationState(); } - var carrier = new HttpRequestInfo("GET", actualRedirectUrl); - IDirectedProtocolMessage response = this.Channel.ReadFromRequest(carrier); + var carrier = new HttpRequestMessage(HttpMethod.Get, actualRedirectUrl); + IDirectedProtocolMessage response = await this.Channel.ReadFromRequestAsync(carrier, cancellationToken); if (response == null) { return null; } - return this.ProcessUserAuthorization(authorizationState, response); + return await this.ProcessUserAuthorizationAsync(authorizationState, response, cancellationToken); } /// <summary> @@ -136,10 +142,11 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="authorizationState">The authorization.</param> /// <param name="response">The incoming authorization response message.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// The granted authorization, or <c>null</c> if the incoming HTTP request did not contain an authorization server response or authorization was rejected. /// </returns> - internal IAuthorizationState ProcessUserAuthorization(IAuthorizationState authorizationState, IDirectedProtocolMessage response) { + internal async Task<IAuthorizationState> ProcessUserAuthorizationAsync(IAuthorizationState authorizationState, IDirectedProtocolMessage response, CancellationToken cancellationToken) { Requires.NotNull(authorizationState, "authorizationState"); Requires.NotNull(response, "response"); @@ -148,7 +155,7 @@ namespace DotNetOpenAuth.OAuth2 { if ((accessTokenSuccess = response as EndUserAuthorizationSuccessAccessTokenResponse) != null) { UpdateAuthorizationWithResponse(authorizationState, accessTokenSuccess); } else if ((authCodeSuccess = response as EndUserAuthorizationSuccessAuthCodeResponse) != null) { - this.UpdateAuthorizationWithResponse(authorizationState, authCodeSuccess); + await this.UpdateAuthorizationWithResponseAsync(authorizationState, authCodeSuccess, cancellationToken); } else if (response is EndUserAuthorizationFailedResponse) { authorizationState.Delete(); return null; diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs index 63d96e1..2b5a80a 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs @@ -10,10 +10,13 @@ namespace DotNetOpenAuth.OAuth2 { using System.Globalization; using System.Linq; using System.Net; + using System.Net.Http; + using System.Net.Http.Headers; using System.Text; + using System.Threading; + using System.Threading.Tasks; using System.Web; using System.Web.Security; - using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.Messages; @@ -29,26 +32,26 @@ namespace DotNetOpenAuth.OAuth2 { private const string XsrfCookieName = "DotNetOpenAuth.WebServerClient.XSRF-Session"; /// <summary> - /// Initializes a new instance of the <see cref="WebServerClient"/> class. + /// Initializes a new instance of the <see cref="WebServerClient" /> class. /// </summary> /// <param name="authorizationServer">The authorization server.</param> /// <param name="clientIdentifier">The client identifier.</param> /// <param name="clientSecret">The client secret.</param> - public WebServerClient(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null) - : this(authorizationServer, clientIdentifier, DefaultSecretApplicator(clientSecret)) { + /// <param name="hostFactories">The host factories.</param> + public WebServerClient(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null, IHostFactories hostFactories = null) + : this(authorizationServer, clientIdentifier, DefaultSecretApplicator(clientSecret), hostFactories) { } /// <summary> - /// Initializes a new instance of the <see cref="WebServerClient"/> class. + /// Initializes a new instance of the <see cref="WebServerClient" /> class. /// </summary> /// <param name="authorizationServer">The authorization server.</param> /// <param name="clientIdentifier">The client identifier.</param> - /// <param name="clientCredentialApplicator"> - /// The tool to use to apply client credentials to authenticated requests to the Authorization Server. - /// May be <c>null</c> for clients with no secret or other means of authentication. - /// </param> - public WebServerClient(AuthorizationServerDescription authorizationServer, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator) - : base(authorizationServer, clientIdentifier, clientCredentialApplicator) { + /// <param name="clientCredentialApplicator">The tool to use to apply client credentials to authenticated requests to the Authorization Server. + /// May be <c>null</c> for clients with no secret or other means of authentication.</param> + /// <param name="hostFactories"></param> + public WebServerClient(AuthorizationServerDescription authorizationServer, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator, IHostFactories hostFactories = null) + : base(authorizationServer, clientIdentifier, clientCredentialApplicator, hostFactories) { } /// <summary> @@ -60,34 +63,28 @@ namespace DotNetOpenAuth.OAuth2 { /// <summary> /// Prepares a request for user authorization from an authorization server. /// </summary> - /// <param name="scope">The scope of authorized access requested.</param> - /// <param name="returnTo">The URL the authorization server should redirect the browser (typically on this site) to when the authorization is completed. If null, the current request's URL will be used.</param> - public void RequestUserAuthorization(IEnumerable<string> scope = null, Uri returnTo = null) { - var authorizationState = new AuthorizationState(scope) { - Callback = returnTo, - }; - this.PrepareRequestUserAuthorization(authorizationState).Send(); - } - - /// <summary> - /// Prepares a request for user authorization from an authorization server. - /// </summary> /// <param name="scopes">The scope of authorized access requested.</param> /// <param name="returnTo">The URL the authorization server should redirect the browser (typically on this site) to when the authorization is completed. If null, the current request's URL will be used.</param> - /// <returns>The authorization request.</returns> - public OutgoingWebResponse PrepareRequestUserAuthorization(IEnumerable<string> scopes = null, Uri returnTo = null) { + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The authorization request. + /// </returns> + public Task<HttpResponseMessage> PrepareRequestUserAuthorizationAsync(IEnumerable<string> scopes = null, Uri returnTo = null, CancellationToken cancellationToken = default(CancellationToken)) { var authorizationState = new AuthorizationState(scopes) { Callback = returnTo, }; - return this.PrepareRequestUserAuthorization(authorizationState); + return this.PrepareRequestUserAuthorizationAsync(authorizationState, cancellationToken); } /// <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) { + /// <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"); @@ -108,23 +105,18 @@ namespace DotNetOpenAuth.OAuth2 { // 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; + var cookies = new List<CookieHeaderValue>(); if (this.AuthorizationTracker == null) { - var context = this.Channel.GetHttpContext(); - string xsrfKey = MessagingUtilities.GetNonCryptoRandomDataAsBase64(16); - cookie = new HttpCookie(XsrfCookieName, xsrfKey) { + cookies.Add(new CookieHeaderValue(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); - } + var response = await this.Channel.PrepareResponseAsync(request, cancellationToken); + response.Headers.AddCookies(cookies); return response; } @@ -133,34 +125,45 @@ namespace DotNetOpenAuth.OAuth2 { /// 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 IAuthorizationState ProcessUserAuthorization(HttpRequestBase request = null) { + public Task<IAuthorizationState> ProcessUserAuthorizationAsync( + HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.Channel.GetRequestFromContext(); + return this.ProcessUserAuthorizationAsync(request.AsHttpRequestMessage(), cancellationToken); + } + + /// <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"); - 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)); + 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 context = this.Channel.GetHttpContext(); - - HttpCookie cookie = request.Cookies[XsrfCookieName]; - ErrorUtilities.VerifyProtocol(cookie != null && string.Equals(response.ClientState, cookie.Value, StringComparison.Ordinal), ClientStrings.AuthorizationResponseUnexpectedMismatch); + 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) { - this.UpdateAuthorizationWithResponse(authorizationState, success); + await this.UpdateAuthorizationWithResponseAsync(authorizationState, success, cancellationToken); } else { // failure Logger.OAuth.Info("User refused to grant the requested authorization at the Authorization Server."); authorizationState.Delete(); |