diff options
Diffstat (limited to 'src/DotNetOpenAuth.OAuth2.Client/OAuth2')
4 files changed, 79 insertions, 14 deletions
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs index c802be6..db8b9f8 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using System.Collections.Generic; using System.Linq; using System.Text; + using System.Xml; /// <summary> /// An interface that defines the OAuth2 client specific channel additions. @@ -23,5 +24,10 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// Gets or sets the client credentials applicator extension to use. /// </summary> ClientCredentialApplicator ClientCredentialApplicator { get; set; } + + /// <summary> + /// Gets quotas used when deserializing JSON. + /// </summary> + XmlDictionaryReaderQuotas JsonReaderQuotas { get; } } } diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs index 8ad2ed9..b0cdb4b 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using System.Diagnostics.Contracts; using System.Net; using System.Web; + using System.Xml; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.Messages; @@ -50,6 +51,13 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { public ClientCredentialApplicator ClientCredentialApplicator { get; set; } /// <summary> + /// Gets quotas used when deserializing JSON. + /// </summary> + public XmlDictionaryReaderQuotas JsonReaderQuotas { + get { return this.XmlDictionaryReaderQuotas; } + } + + /// <summary> /// Prepares an HTTP request that carries a given message. /// </summary> /// <param name="request">The message to send.</param> diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs index 49d0732..e9c952d 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs @@ -16,6 +16,8 @@ namespace DotNetOpenAuth.OAuth2 { #endif using System.Security; using System.Text; + using System.Xml; + using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.ChannelElements; using DotNetOpenAuth.OAuth2.Messages; @@ -71,6 +73,13 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> + /// Gets quotas used when deserializing JSON. + /// </summary> + public XmlDictionaryReaderQuotas JsonReaderQuotas { + get { return this.OAuthChannel.JsonReaderQuotas; } + } + + /// <summary> /// Gets the OAuth client channel. /// </summary> internal IOAuth2ChannelWithClient OAuthChannel { @@ -87,7 +96,20 @@ namespace DotNetOpenAuth.OAuth2 { Requires.NotNull(request, "request"); Requires.NotNullOrEmpty(accessToken, "accessToken"); - OAuthUtilities.AuthorizeWithBearerToken(request, accessToken); + AuthorizeRequest(request.Headers, accessToken); + } + + /// <summary> + /// Adds the necessary HTTP Authorization header to an HTTP request for protected resources + /// so that the Service Provider will allow the request through. + /// </summary> + /// <param name="requestHeaders">The headers on the request for protected resources from the service provider.</param> + /// <param name="accessToken">The access token previously obtained from the Authorization Server.</param> + public static void AuthorizeRequest(WebHeaderCollection requestHeaders, string accessToken) { + Requires.NotNull(requestHeaders, "requestHeaders"); + Requires.NotNullOrEmpty(accessToken, "accessToken"); + + OAuthUtilities.AuthorizeWithBearerToken(requestHeaders, accessToken); } /// <summary> @@ -99,6 +121,19 @@ namespace DotNetOpenAuth.OAuth2 { public void AuthorizeRequest(HttpWebRequest request, IAuthorizationState authorization) { Requires.NotNull(request, "request"); Requires.NotNull(authorization, "authorization"); + + this.AuthorizeRequest(request.Headers, authorization); + } + + /// <summary> + /// Adds the OAuth authorization token to an outgoing HTTP request, renewing a + /// (nearly) expired access token if necessary. + /// </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) { + Requires.NotNull(requestHeaders, "requestHeaders"); + Requires.NotNull(authorization, "authorization"); Requires.True(!string.IsNullOrEmpty(authorization.AccessToken), "authorization"); ErrorUtilities.VerifyProtocol(!authorization.AccessTokenExpirationUtc.HasValue || authorization.AccessTokenExpirationUtc >= DateTime.UtcNow || authorization.RefreshToken != null, ClientStrings.AuthorizationExpired); @@ -107,7 +142,7 @@ namespace DotNetOpenAuth.OAuth2 { this.RefreshAuthorization(authorization); } - AuthorizeRequest(request, authorization.AccessToken); + AuthorizeRequest(requestHeaders, authorization.AccessToken); } #if CLR4 @@ -216,7 +251,7 @@ namespace DotNetOpenAuth.OAuth2 { Requires.NotNull(password, "password"); var request = new AccessTokenResourceOwnerPasswordCredentialsRequest(this.AuthorizationServer.TokenEndpoint, this.AuthorizationServer.Version) { - UserName = userName, + RequestingUserName = userName, Password = password, }; @@ -372,6 +407,7 @@ namespace DotNetOpenAuth.OAuth2 { var failure = response as AccessTokenFailedResponse; ErrorUtilities.VerifyProtocol(success != null || failure != null, MessagingStrings.UnexpectedMessageReceivedOfMany); if (success != null) { + authorizationState.Scope.Clear(); // clear the scope we requested so that the response will repopulate it. UpdateAuthorizationWithResponse(authorizationState, success); } else { // failure Logger.OAuth.Info("Credentials rejected by the Authorization Server."); diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs index 939d1df..879e4e3 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs @@ -8,10 +8,14 @@ namespace DotNetOpenAuth.OAuth2 { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; + using System.Globalization; using System.Linq; using System.Net; using System.Text; using System.Web; + using System.Web.Security; + + using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.Messages; @@ -20,6 +24,11 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> public class WebServerClient : ClientBase { /// <summary> + /// The cookie name for XSRF mitigation during authorization code grant flows. + /// </summary> + private const string XsrfCookieName = "DotNetOpenAuth.WebServerClient.XSRF-Session"; + + /// <summary> /// Initializes a new instance of the <see cref="WebServerClient"/> class. /// </summary> /// <param name="authorizationServer">The authorization server.</param> @@ -100,16 +109,25 @@ 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; if (this.AuthorizationTracker == null) { var context = this.Channel.GetHttpContext(); - if (context.Session != null) { - request.ClientState = context.Session.SessionID; - } else { - Logger.OAuth.WarnFormat("No request context discovered, so no client state parameter could be set to mitigate XSRF attacks."); - } + + 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 this.Channel.PrepareResponse(request); + return response; } /// <summary> @@ -134,12 +152,9 @@ namespace DotNetOpenAuth.OAuth2 { ErrorUtilities.VerifyProtocol(authorizationState != null, ClientStrings.AuthorizationResponseUnexpectedMismatch); } else { var context = this.Channel.GetHttpContext(); - if (context.Session != null) { - ErrorUtilities.VerifyProtocol(string.Equals(response.ClientState, context.Session.SessionID, StringComparison.Ordinal), ClientStrings.AuthorizationResponseUnexpectedMismatch); - } else { - Logger.OAuth.WarnFormat("No request context discovered, so no client state parameter could be checked to mitigate XSRF attacks."); - } + 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; |