summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2011-06-11 21:45:18 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2011-06-11 21:45:18 -0700
commit02399ba17dafb981143c8c5e6e83e43f7c7fe4b2 (patch)
tree57bd29f9343dff87c354bbb039674f39037467bf /src
parentd01fab593c838a3289bea1b22dee32a4713567a2 (diff)
downloadDotNetOpenAuth-02399ba17dafb981143c8c5e6e83e43f7c7fe4b2.zip
DotNetOpenAuth-02399ba17dafb981143c8c5e6e83e43f7c7fe4b2.tar.gz
DotNetOpenAuth-02399ba17dafb981143c8c5e6e83e43f7c7fe4b2.tar.bz2
Brought bearer access token up to the draft 16 spec.
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs4
-rw-r--r--src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs79
-rw-r--r--src/DotNetOpenAuth/OAuth2/ClientBase.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/AccessProtectedResourceRequest.cs78
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/UnauthorizedResponse.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth2/OAuthUtilities.cs4
-rw-r--r--src/DotNetOpenAuth/OAuth2/Protocol.cs29
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";
+ }
}
}