diff options
-rw-r--r-- | src/DotNetOpenAuth/DotNetOpenAuth.csproj | 5 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs | 36 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs | 35 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs (renamed from src/DotNetOpenAuth/OAuthWrap/ChannelElements/SignedDataBag.cs) | 20 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAuthorizationDescription.cs | 38 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs | 1 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshAccessTokenBindingElement.cs | 4 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs | 14 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs | 12 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs | 3 |
10 files changed, 149 insertions, 19 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 56fba07..5131b7e 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -309,13 +309,16 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="Messaging\StandardMessageFactory.cs" /> <Compile Include="OAuthWrap\AuthorizationServerBase.cs" /> <Compile Include="OAuthWrap\AuthorizationState.cs" /> + <Compile Include="OAuthWrap\ChannelElements\AccessToken.cs" /> + <Compile Include="OAuthWrap\ChannelElements\AuthorizationDataBag.cs" /> <Compile Include="OAuthWrap\ChannelElements\AuthServerBindingElementBase.cs" /> <Compile Include="OAuthWrap\ChannelElements\IAccessTokenRequest.cs" /> + <Compile Include="OAuthWrap\ChannelElements\IAuthorizationDescription.cs" /> <Compile Include="OAuthWrap\ChannelElements\OAuthWrapResourceServerChannel.cs" /> <Compile Include="Messaging\StandardMessageFactoryChannel.cs" /> <Compile Include="OAuthWrap\ChannelElements\RefreshAccessTokenBindingElement.cs" /> <Compile Include="OAuthWrap\ChannelElements\RefreshToken.cs" /> - <Compile Include="OAuthWrap\ChannelElements\SignedDataBag.cs" /> + <Compile Include="OAuthWrap\ChannelElements\DataBag.cs" /> <Compile Include="OAuthWrap\ChannelElements\TimestampEncoder.cs" /> <Compile Include="OAuthWrap\ChannelElements\VerificationCode.cs" /> <Compile Include="OAuthWrap\ChannelElements\WebAppVerificationCodeBindingElement.cs" /> diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs new file mode 100644 index 0000000..e770f09 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessToken.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.ChannelElements { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + + internal class AccessToken : AuthorizationDataBag { + /// <summary> + /// Initializes a new instance of the <see cref="AccessToken"/> class. + /// </summary> + /// <param name="channel">The channel.</param> + internal AccessToken(OAuthWrapAuthorizationServerChannel channel, TimeSpan lifetime) + : base(channel, true, true, true, lifetime) { + Contract.Requires<ArgumentNullException>(channel != null, "channel"); + } + + internal static AccessToken Decode(OAuthWrapAuthorizationServerChannel channel, string value, TimeSpan lifetime, IProtocolMessage containingMessage) { + Contract.Requires<ArgumentNullException>(channel != null, "channel"); + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value)); + Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage"); + Contract.Ensures(Contract.Result<AccessToken>() != null); + + var self = new AccessToken(channel, lifetime); + self.Decode(value, containingMessage); + return self; + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs new file mode 100644 index 0000000..6bd3041 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------- +// <copyright file="AuthorizationDataBag.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.ChannelElements { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.Messaging.Bindings; + + internal abstract class AuthorizationDataBag : DataBag, IAuthorizationDescription { + protected AuthorizationDataBag(OAuthWrapAuthorizationServerChannel channel, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) + : base(channel, signed, encrypted, compressed, maximumAge, decodeOnceOnly) { + Contract.Requires<ArgumentNullException>(channel != null, "channel"); + } + + [MessagePart] + public string ClientIdentifier { get; set; } + + public DateTime UtcIssued { + get { return this.UtcCreationDate; } + } + + [MessagePart] + public string User { get; set; } + + [MessagePart] + public string Scope { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/SignedDataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs index 4b5a296..0b0bd3a 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/SignedDataBag.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs @@ -55,12 +55,12 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { internal string Nonce { get; set; } [MessagePart("timestamp", IsRequired = true, Encoder = typeof(TimestampEncoder))] - internal DateTime CreationDateUtc { get; set; } + internal DateTime UtcCreationDate { get; set; } internal virtual string Encode() { Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); - this.CreationDateUtc = DateTime.UtcNow; + this.UtcCreationDate = DateTime.UtcNow; if (decodeOnceOnly != null) { this.Nonce = Convert.ToBase64String(MessagingUtilities.GetNonCryptoRandomData(NonceLength)); @@ -71,7 +71,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } var fields = this.Channel.MessageDescriptions.GetAccessor(this); - string value = MessagingUtilities.CreateQueryString(fields); + string value = Uri.EscapeDataString(this.BagTypeName) + "&" + MessagingUtilities.CreateQueryString(fields); byte[] encoded = Encoding.UTF8.GetBytes(value); @@ -104,6 +104,10 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { // Deserialize into this newly created instance. var fields = this.Channel.MessageDescriptions.GetAccessor(this); + string[] halves = value.Split(new char[] { '&' }, 2); + ErrorUtilities.VerifyProtocol(string.Equals(halves[0], Uri.EscapeDataString(this.BagTypeName), StringComparison.Ordinal), "Unexpected type of message while decoding."); + value = halves[1]; + var nvc = HttpUtility.ParseQueryString(value); foreach (string key in nvc) { fields[key] = nvc[key]; @@ -116,7 +120,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { if (maximumAge.HasValue) { // Has this verification code expired? - DateTime expirationDate = this.CreationDateUtc + this.maximumAge.Value; + DateTime expirationDate = this.UtcCreationDate + this.maximumAge.Value; if (expirationDate < DateTime.UtcNow) { throw new ExpiredMessageException(expirationDate, containingMessage); } @@ -126,13 +130,17 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { if (decodeOnceOnly != null) { ErrorUtilities.VerifyInternal(this.maximumAge.HasValue, "Oops! How can we validate a nonce without a maximum message age?"); string context = "{" + GetType().FullName + "}"; - if (!this.decodeOnceOnly.StoreNonce(context, this.Nonce, this.CreationDateUtc)) { - Logger.OpenId.ErrorFormat("Replayed nonce detected ({0} {1}). Rejecting message.", this.Nonce, this.CreationDateUtc); + if (!this.decodeOnceOnly.StoreNonce(context, this.Nonce, this.UtcCreationDate)) { + Logger.OpenId.ErrorFormat("Replayed nonce detected ({0} {1}). Rejecting message.", this.Nonce, this.UtcCreationDate); throw new ReplayedMessageException(containingMessage); } } } + private string BagTypeName { + get { return this.GetType().Name; } + } + /// <summary> /// Calculates the signature for the data in this verification code. /// </summary> diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAuthorizationDescription.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAuthorizationDescription.cs new file mode 100644 index 0000000..d7735b9 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAuthorizationDescription.cs @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------- +// <copyright file="IAuthorizationDescription.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.ChannelElements { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + /// <summary> + /// Describes a delegated authorization between a resource server, a client, and a user. + /// </summary> + public interface IAuthorizationDescription { + /// <summary> + /// Gets the identifier of the client authorized to access protected data. + /// </summary> + string ClientIdentifier { get; } + + /// <summary> + /// Gets the date this authorization was established or the token was issued. + /// </summary> + /// <value>A date/time expressed in UTC.</value> + DateTime UtcIssued { get; } + + /// <summary> + /// Gets the name on the account whose data on the resource server is accessible using this authorization. + /// </summary> + string User { get; } + + /// <summary> + /// Gets the scope of operations the client is allowed to invoke. + /// </summary> + string Scope { get; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs index 562b818..0148b7a 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs @@ -71,6 +71,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { var response = new AccessTokenSuccessResponse(request) { Scope = request.Scope, + Lifetime = TimeSpan.FromDays(1), // reasonable default for access token lifetime // TODO: code here to initialize the response AccessToken = "TODO", RefreshToken = "TODO", diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshAccessTokenBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshAccessTokenBindingElement.cs index 299353e..24aa1af 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshAccessTokenBindingElement.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshAccessTokenBindingElement.cs @@ -31,11 +31,11 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { var request = message as RefreshAccessTokenRequest; if (request != null) { // Decode and validate the refresh token - //request.RefreshToken + var refreshToken = RefreshToken.Decode(this.OAuthChannel, request.RefreshToken, message); // Fill in the authorized access scope from the refresh token and fill in the property // on the message so that others can read it later. - //request.Scope = + request.Scope = refreshToken.Scope; return MessageProtections.None; } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs index 55a4416..c523127 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs @@ -10,8 +10,9 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using System.Diagnostics.Contracts; using System.Linq; using System.Text; + using DotNetOpenAuth.Messaging; - internal class RefreshToken : DataBag { + internal class RefreshToken : AuthorizationDataBag { /// <summary> /// Initializes a new instance of the <see cref="RefreshToken"/> class. /// </summary> @@ -20,5 +21,16 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { : base(channel, true, true, true) { Contract.Requires<ArgumentNullException>(channel != null, "channel"); } + + internal static RefreshToken Decode(OAuthWrapAuthorizationServerChannel channel, string value, IProtocolMessage containingMessage) { + Contract.Requires<ArgumentNullException>(channel != null, "channel"); + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value)); + Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage"); + Contract.Ensures(Contract.Result<RefreshToken>() != null); + + var self = new RefreshToken(channel); + self.Decode(value, containingMessage); + return self; + } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs index faedf8b..4e5e449 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs @@ -10,7 +10,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; - internal class VerificationCode : DataBag { + internal class VerificationCode : AuthorizationDataBag { /// <summary> /// Initializes a new instance of the <see cref="VerificationCode"/> class. /// </summary> @@ -18,11 +18,13 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// <param name="callback">The callback.</param> /// <param name="scope">The scope.</param> /// <param name="username">The username.</param> - internal VerificationCode(OAuthWrapAuthorizationServerChannel channel, Uri callback, string scope, string username) + internal VerificationCode(OAuthWrapAuthorizationServerChannel channel, string clientIdentifier, Uri callback, string scope, string username) : this(channel) { + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(clientIdentifier)); Contract.Requires<ArgumentNullException>(channel != null, "channel"); Contract.Requires<ArgumentNullException>(callback != null, "callback"); + this.ClientIdentifier = clientIdentifier; this.CallbackHash = this.CalculateCallbackHash(callback); this.Scope = scope; this.User = username; @@ -38,12 +40,6 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { Contract.Requires<ArgumentException>(channel.AuthorizationServer != null); } - [MessagePart] - internal string Scope { get; set; } - - [MessagePart] - internal string User { get; set; } - [MessagePart("cb")] private string CallbackHash { get; set; } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs index 285cf0f..faf896a 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs @@ -53,7 +53,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { var directResponse = (IDirectResponseProtocolMessage)response; var request = (WebAppRequest)directResponse.OriginatingRequest; - var code = new VerificationCode(this.OAuthChannel, request.Callback, request.Scope, response.AuthorizingUsername); + var code = new VerificationCode(this.OAuthChannel, request.ClientIdentifier, request.Callback, request.Scope, response.AuthorizingUsername); response.VerificationCode = code.Encode(); return MessageProtections.None; @@ -86,6 +86,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { ErrorUtilities.VerifyProtocol(string.Equals(client.Secret, request.ClientSecret, StringComparison.Ordinal), Protocol.incorrect_client_credentials); var verificationCode = VerificationCode.Decode(this.OAuthChannel, request.VerificationCode, message); + ErrorUtilities.VerifyProtocol(string.Equals(verificationCode.ClientIdentifier, request.ClientIdentifier, StringComparison.Ordinal), Protocol.bad_verification_code); verificationCode.VerifyCallback(request.Callback); request.Scope = verificationCode.Scope; |