diff options
Diffstat (limited to 'src')
7 files changed, 89 insertions, 109 deletions
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index 4be9eb5..8fc691f 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -421,10 +421,10 @@ namespace DotNetOpenAuth.Messaging { string prefix = scheme + " "; if (authorizationHeader != null) { // The authorization header may have multiple sections. Look for the appropriate one. - string[] authorizationSections = authorizationHeader.Split(';'); // TODO: is this the right delimiter? + string[] authorizationSections = new string[] { authorizationHeader }; // what is the right delimiter, if any? foreach (string authorization in authorizationSections) { string trimmedAuth = authorization.Trim(); - if (trimmedAuth.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { + if (trimmedAuth.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { // RFC 2617 says this is case INsensitive string data = trimmedAuth.Substring(prefix.Length); return from element in data.Split(CommaArray) let parts = element.Split(EqualsArray, 2) diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs index 60e464c..a292977 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs @@ -49,39 +49,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// The deserialized message, if one is found. Null otherwise. /// </returns> protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestInfo request) { - // First search the Authorization header. - var fields = MessagingUtilities.ParseAuthorizationHeader( - Protocol.HttpAuthorizationScheme, - request.Headers[HttpRequestHeader.Authorization]).ToDictionary(); - - // Failing that, try the query string (for GET or POST or any other method) - if (fields.Count == 0) { - if (request.QueryStringBeforeRewriting["oauth_token"] != null) { - // We're only interested in the oauth_token parameter -- not the others that can appear in an Authorization header. - // Note that we intentionally change the name of the key here - // because depending on the method used to obtain the token, the token's key changes - // but we need to consolidate to one name so it works with the rest of the system. - fields.Add("token", request.QueryStringBeforeRewriting["oauth_token"]); - } - } - - // Failing that, scan the entity - if (fields.Count == 0) { - // The spec calls out that this is allowed only for these three HTTP methods. - if (request.HttpMethod == "POST" || request.HttpMethod == "DELETE" || request.HttpMethod == "PUT") { - if (!string.IsNullOrEmpty(request.Headers[HttpRequestHeader.ContentType])) { - var contentType = new ContentType(request.Headers[HttpRequestHeader.ContentType]); - if (string.Equals(contentType.MediaType, HttpFormUrlEncoded, StringComparison.Ordinal)) { - if (request.Form["oauth_token"] != null) { - // We're only interested in the oauth_token parameter -- not the others that can appear in an Authorization header. - // Note that we intentionally change the name of the key here - // because depending on the method used to obtain the token, the token's key changes - // but we need to consolidate to one name so it works with the rest of the system. - fields.Add("token", request.Form["oauth_token"]); - } - } - } - } + var fields = new Dictionary<string, string>(); + string accessToken; + if ((accessToken = SearchForBearerAccessTokenInRequest(request)) != null) { + fields["token_type"] = Protocol.AccessTokenTypes.Bearer; + fields["access_token"] = accessToken; } if (fields.Count > 0) { @@ -93,9 +65,6 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { return null; } - // TODO: remove this after signatures are supported. - ErrorUtilities.VerifyProtocol(!fields.ContainsKey("signature"), "OAuth signatures not supported yet."); - // Deserialize the message using all the data we've collected. var message = (IDirectedProtocolMessage)this.Receive(fields, recipient); return message; @@ -145,8 +114,44 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { // Now serialize all the message parts into the WWW-Authenticate header. var fields = this.MessageDescriptions.GetAccessor(response); - webResponse.Headers[HttpResponseHeader.WwwAuthenticate] = MessagingUtilities.AssembleAuthorizationHeader(Protocol.HttpAuthorizationScheme, fields); + webResponse.Headers[HttpResponseHeader.WwwAuthenticate] = MessagingUtilities.AssembleAuthorizationHeader(Protocol.BearerHttpAuthorizationScheme, fields); return webResponse; } + + /// <summary> + /// Searches for a bearer access token in the request. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>The bearer access token, if one exists. Otherwise <c>null</c>.</returns> + private static string SearchForBearerAccessTokenInRequest(HttpRequestInfo request) { + Contract.Requires<ArgumentNullException>(request != null, "request"); + + // First search the authorization header. + string authorizationHeader = request.Headers[HttpRequestHeader.Authorization]; + if (authorizationHeader.StartsWith(Protocol.BearerHttpAuthorizationSchemeWithTrailingSpace, StringComparison.OrdinalIgnoreCase)) { + return authorizationHeader.Substring(Protocol.BearerHttpAuthorizationSchemeWithTrailingSpace.Length); + } + + // Failing that, scan the entity + if (!string.IsNullOrEmpty(request.Headers[HttpRequestHeader.ContentType])) { + var contentType = new ContentType(request.Headers[HttpRequestHeader.ContentType]); + if (string.Equals(contentType.MediaType, HttpFormUrlEncoded, StringComparison.Ordinal)) { + if (request.Form[Protocol.BearerTokenEncodedUrlParameterName] != null) { + // We're only interested in the oauth_token parameter -- not the others that can appear in an Authorization header. + // Note that we intentionally change the name of the key here + // because depending on the method used to obtain the token, the token's key changes + // but we need to consolidate to one name so it works with the rest of the system. + return request.Form[Protocol.BearerTokenEncodedUrlParameterName]; + } + } + } + + // Finally, check the least desirable location: the query string + if (!String.IsNullOrEmpty(request.QueryStringBeforeRewriting[Protocol.BearerTokenEncodedUrlParameterName])) { + return request.QueryStringBeforeRewriting[Protocol.BearerTokenEncodedUrlParameterName]; + } + + return null; + } } } diff --git a/src/DotNetOpenAuth/OAuth2/ClientBase.cs b/src/DotNetOpenAuth/OAuth2/ClientBase.cs index 045fdf2..f91b1f5 100644 --- a/src/DotNetOpenAuth/OAuth2/ClientBase.cs +++ b/src/DotNetOpenAuth/OAuth2/ClientBase.cs @@ -66,7 +66,7 @@ namespace DotNetOpenAuth.OAuth2 { Contract.Requires<ArgumentNullException>(request != null); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(accessToken)); - OAuthUtilities.AuthorizeWithOAuthWrap(request, accessToken); + OAuthUtilities.AuthorizeWithBearerToken(request, accessToken); } /// <summary> diff --git a/src/DotNetOpenAuth/OAuth2/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuth2/Messages/AccessProtectedResourceRequest.cs index 461ed08..2e94156 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/AccessProtectedResourceRequest.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/AccessProtectedResourceRequest.cs @@ -15,6 +15,11 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// <summary> /// A message that accompanies an HTTP request to a resource server that provides authorization. /// </summary> + /// <remarks> + /// In its current form, this class only accepts bearer access tokens. + /// When support for additional access token types is added, this class should probably be refactored + /// into derived types, where each derived type supports a particular access token type. + /// </remarks> internal class AccessProtectedResourceRequest : MessageBase, IAuthorizationCarryingRequest { /// <summary> /// Initializes a new instance of the <see cref="AccessProtectedResourceRequest"/> class. @@ -48,72 +53,21 @@ namespace DotNetOpenAuth.OAuth2.Messages { IAuthorizationDescription IAuthorizationCarryingRequest.AuthorizationDescription { get; set; } /// <summary> - /// Gets or sets the access token. - /// </summary> - /// <value>The access token.</value> - [MessagePart("token", IsRequired = true)] - internal string AccessToken { get; set; } - - /// <summary> - /// Gets or sets the nonce. - /// </summary> - /// <value>The nonce.</value> - [MessagePart("nonce")] - internal string Nonce { get; set; } - - /// <summary> - /// Gets or sets the timestamp. - /// </summary> - /// <value>The timestamp.</value> - [MessagePart("timestamp", Encoder = typeof(TimestampEncoder))] - internal DateTime? Timestamp { get; set; } - - /// <summary> - /// Gets or sets the signature. + /// Gets the type of the access token. /// </summary> - /// <value>The signature.</value> - [MessagePart("signature")] - internal string Signature { get; set; } - - /// <summary> - /// Gets or sets the algorithm. - /// </summary> - /// <value>The algorithm.</value> - [MessagePart("algorithm")] - internal string Algorithm { get; set; } - - /// <summary> - /// Gets a value indicating whether this request is signed. - /// </summary> - internal bool SignedRequest { - get { return this.Signature != null; } + /// <value> + /// Always "bearer". + /// </value> + [MessagePart("token_type", IsRequired = true)] + internal string TokenType { + get { return Protocol.AccessTokenTypes.Bearer; } } /// <summary> - /// Checks the message state for conformity to the protocol specification - /// and throws an exception if the message is invalid. + /// Gets or sets the access token. /// </summary> - /// <remarks> - /// <para>Some messages have required fields, or combinations of fields that must relate to each other - /// in specialized ways. After deserializing a message, this method checks the state of the - /// message to see if it conforms to the protocol.</para> - /// <para>Note that this property should <i>not</i> check signatures or perform any state checks - /// outside this scope of this particular message.</para> - /// </remarks> - /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception> - protected override void EnsureValidMessage() { - base.EnsureValidMessage(); - - // If any of the optional parameters are present, all of them are required. - if (this.Signature == null) { - ErrorUtilities.VerifyProtocol(this.Algorithm == null, this, MessagingStrings.UnexpectedMessagePartValue, "algorithm", this.Algorithm); - ErrorUtilities.VerifyProtocol(!this.Timestamp.HasValue, this, MessagingStrings.UnexpectedMessagePartValue, "timestamp", this.Timestamp); - ErrorUtilities.VerifyProtocol(this.Nonce == null, this, MessagingStrings.UnexpectedMessagePartValue, "nonce", this.Nonce); - } else { - ErrorUtilities.VerifyProtocol(this.Algorithm != null, this, MessagingStrings.RequiredParametersMissing, "algorithm"); - ErrorUtilities.VerifyProtocol(this.Timestamp.HasValue, this, MessagingStrings.RequiredParametersMissing, "timestamp"); - ErrorUtilities.VerifyProtocol(this.Nonce != null, this, MessagingStrings.RequiredParametersMissing, "nonce"); - } - } + /// <value>The access token.</value> + [MessagePart("access_token", IsRequired = true)] + internal string AccessToken { get; set; } } } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/UnauthorizedResponse.cs b/src/DotNetOpenAuth/OAuth2/Messages/UnauthorizedResponse.cs index 34da922..d79c00b 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/UnauthorizedResponse.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/UnauthorizedResponse.cs @@ -63,7 +63,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { WebHeaderCollection IHttpDirectResponse.Headers { get { return new WebHeaderCollection() { - { HttpResponseHeader.WwwAuthenticate, Protocol.HttpAuthorizationScheme }, + { HttpResponseHeader.WwwAuthenticate, Protocol.BearerHttpAuthorizationScheme }, }; } } diff --git a/src/DotNetOpenAuth/OAuth2/OAuthUtilities.cs b/src/DotNetOpenAuth/OAuth2/OAuthUtilities.cs index 74d5791..c2b2f5f 100644 --- a/src/DotNetOpenAuth/OAuth2/OAuthUtilities.cs +++ b/src/DotNetOpenAuth/OAuth2/OAuthUtilities.cs @@ -90,14 +90,14 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="request">The request to authorize.</param> /// <param name="accessToken">The access token previously obtained from the Authorization Server.</param> - internal static void AuthorizeWithOAuthWrap(this HttpWebRequest request, string accessToken) { + internal static void AuthorizeWithBearerToken(this HttpWebRequest request, string accessToken) { Contract.Requires<ArgumentNullException>(request != null); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(accessToken)); ErrorUtilities.VerifyProtocol(accessToken.All(ch => accessTokenAuthorizationHeaderAllowedCharacters.IndexOf(ch) >= 0), "The access token contains characters that must not appear in the HTTP Authorization header."); request.Headers[HttpRequestHeader.Authorization] = string.Format( CultureInfo.InvariantCulture, - Protocol.HttpAuthorizationHeaderFormat, + Protocol.BearerHttpAuthorizationHeaderFormat, accessToken); } diff --git a/src/DotNetOpenAuth/OAuth2/Protocol.cs b/src/DotNetOpenAuth/OAuth2/Protocol.cs index 44ecf52..d9d7dd2 100644 --- a/src/DotNetOpenAuth/OAuth2/Protocol.cs +++ b/src/DotNetOpenAuth/OAuth2/Protocol.cs @@ -24,14 +24,25 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> internal class Protocol { /// <summary> - /// The HTTP authorization scheme "OAuth"; + /// The HTTP authorization scheme "Bearer"; /// </summary> - internal const string HttpAuthorizationScheme = "OAuth"; + internal const string BearerHttpAuthorizationScheme = "Bearer"; /// <summary> - /// The format of the HTTP Authorization header value that authorizes OAuth 2.0 requests. + /// The HTTP authorization scheme "Bearer "; /// </summary> - internal const string HttpAuthorizationHeaderFormat = "OAuth token=\"{0}\""; + internal const string BearerHttpAuthorizationSchemeWithTrailingSpace = BearerHttpAuthorizationScheme + " "; + + /// <summary> + /// The format of the HTTP Authorization header value that authorizes OAuth 2.0 requests using bearer access tokens. + /// </summary> + internal const string BearerHttpAuthorizationHeaderFormat = BearerHttpAuthorizationSchemeWithTrailingSpace + "{0}"; + + /// <summary> + /// The name of the parameter whose value is an OAuth 2.0 bearer access token, as it is defined + /// in a URL-encoded POST entity or URL query string. + /// </summary> + internal const string BearerTokenEncodedUrlParameterName = "bearer_token"; /// <summary> /// The "type" string. @@ -325,5 +336,15 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> internal const string InvalidScope = "invalid_scope"; } + + /// <summary> + /// Recognized access token types. + /// </summary> + internal static class AccessTokenTypes { + /// <summary> + /// The "bearer" token type. + /// </summary> + internal const string Bearer = "bearer"; + } } } |