diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2010-05-31 15:44:46 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2010-05-31 15:44:46 -0700 |
commit | 0284a9702e786bb85017dae1b9074e15904224b5 (patch) | |
tree | 9664b98238de27e1345fceb6c8312aedbfa6122c /src | |
parent | 49eb5203633516d6985a772a585a1801061d1187 (diff) | |
download | DotNetOpenAuth-0284a9702e786bb85017dae1b9074e15904224b5.zip DotNetOpenAuth-0284a9702e786bb85017dae1b9074e15904224b5.tar.gz DotNetOpenAuth-0284a9702e786bb85017dae1b9074e15904224b5.tar.bz2 |
Work toward a signed refresh token.
Diffstat (limited to 'src')
11 files changed, 306 insertions, 127 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index d561d21..56fba07 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -313,6 +313,9 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OAuthWrap\ChannelElements\IAccessTokenRequest.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\TimestampEncoder.cs" /> <Compile Include="OAuthWrap\ChannelElements\VerificationCode.cs" /> <Compile Include="OAuthWrap\ChannelElements\WebAppVerificationCodeBindingElement.cs" /> diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index aebe98c..f9aae38 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -12,6 +12,7 @@ namespace DotNetOpenAuth.Messaging { using System.Diagnostics.Contracts; using System.Globalization; using System.IO; + using System.IO.Compression; using System.Linq; using System.Net; using System.Net.Mime; @@ -530,6 +531,35 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Compresses a given buffer. + /// </summary> + /// <param name="buffer">The buffer to compress.</param> + /// <returns>The compressed data.</returns> + internal static byte[] Compress(byte[] buffer) { + var ms = new MemoryStream(); + using (var compressingStream = new GZipStream(ms, CompressionMode.Compress, true)) { + compressingStream.Write(buffer, 0, buffer.Length); + } + + return ms.ToArray(); + } + + /// <summary> + /// Decompresses a given buffer. + /// </summary> + /// <param name="buffer">The buffer to decompress.</param> + /// <returns>The decompressed data.</returns> + internal static byte[] Decompress(byte[] buffer) { + var compressedDataStream = new MemoryStream(buffer); + var decompressedDataStream = new MemoryStream(); + using (var decompressingStream = new GZipStream(compressedDataStream, CompressionMode.Decompress, true)) { + decompressingStream.CopyTo(decompressedDataStream); + } + + return decompressedDataStream.ToArray(); + } + + /// <summary> /// Adds a set of HTTP headers to an <see cref="HttpResponse"/> instance, /// taking care to set some headers to the appropriate properties of /// <see cref="HttpResponse" /> @@ -597,7 +627,6 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<ArgumentException>(copyTo.CanWrite, MessagingStrings.StreamUnwritable); return CopyUpTo(copyFrom, copyTo, int.MaxValue); } - #endif /// <summary> diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAccessTokenRequest.cs index 34fa329..3579955 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAccessTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAccessTokenRequest.cs @@ -16,5 +16,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { string ClientIdentifier { get; } string Scope { get; } + + string SecretType { get; set; } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs index aa6e1be..562b818 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs @@ -177,6 +177,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { if (authorizationServer != null) { bindingElements.Add(new AuthServerWebServerFlowBindingElement()); bindingElements.Add(new WebAppVerificationCodeBindingElement()); + bindingElements.Add(new RefreshAccessTokenBindingElement()); } return bindingElements.ToArray(); diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshAccessTokenBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshAccessTokenBindingElement.cs new file mode 100644 index 0000000..299353e --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshAccessTokenBindingElement.cs @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------- +// <copyright file="RefreshAccessTokenBindingElement.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; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuthWrap.Messages; + + internal class RefreshAccessTokenBindingElement : AuthServerBindingElementBase { + /// <summary> + /// Gets the protection commonly offered (if any) by this binding element. + /// </summary> + /// <remarks> + /// This value is used to assist in sorting binding elements in the channel stack. + /// </remarks> + public override MessageProtections Protection { + get { return MessageProtections.None; } + } + + public override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + return null; + } + + public override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + var request = message as RefreshAccessTokenRequest; + if (request != null) { + // Decode and validate the refresh token + //request.RefreshToken + + // 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 = + + return MessageProtections.None; + } + + return null; + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs new file mode 100644 index 0000000..55a4416 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs @@ -0,0 +1,24 @@ +//----------------------------------------------------------------------- +// <copyright file="RefreshToken.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; + + internal class RefreshToken : DataBag { + /// <summary> + /// Initializes a new instance of the <see cref="RefreshToken"/> class. + /// </summary> + /// <param name="channel">The channel.</param> + internal RefreshToken(OAuthWrapAuthorizationServerChannel channel) + : base(channel, true, true, true) { + Contract.Requires<ArgumentNullException>(channel != null, "channel"); + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/SignedDataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/SignedDataBag.cs new file mode 100644 index 0000000..4b5a296 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/SignedDataBag.cs @@ -0,0 +1,148 @@ +//----------------------------------------------------------------------- +// <copyright file="DataBag.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.Security.Cryptography; + using System.Text; + using System.Web; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.Messaging.Bindings; + using DotNetOpenAuth.OAuthWrap.Messages; + + internal abstract class DataBag : MessageBase { + private const int NonceLength = 6; + + private readonly bool signed; + + private readonly INonceStore decodeOnceOnly; + + private readonly TimeSpan? maximumAge; + + private readonly bool encrypted; + + private readonly bool compressed; + + protected DataBag(OAuthWrapAuthorizationServerChannel channel, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) + : base(Protocol.Default.Version) { + Contract.Requires<ArgumentNullException>(channel != null, "channel"); + Contract.Requires<ArgumentException>(signed || decodeOnceOnly == null, "A signature must be applied if this data is meant to be decoded only once."); + Contract.Requires<ArgumentException>(maximumAge.HasValue || decodeOnceOnly == null, "A maximum age must be given if a message can only be decoded once."); + + this.Hasher = new HMACSHA256(this.Channel.AuthorizationServer.Secret); + this.Channel = channel; + this.signed = signed; + this.maximumAge = maximumAge; + this.decodeOnceOnly = decodeOnceOnly; + this.encrypted = encrypted; + this.compressed = compressed; + } + + protected OAuthWrapAuthorizationServerChannel Channel { get; set; } + + protected HashAlgorithm Hasher { get; set; } + + [MessagePart("sig")] + private string Signature { get; set; } + + [MessagePart] + internal string Nonce { get; set; } + + [MessagePart("timestamp", IsRequired = true, Encoder = typeof(TimestampEncoder))] + internal DateTime CreationDateUtc { get; set; } + + internal virtual string Encode() { + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); + + this.CreationDateUtc = DateTime.UtcNow; + + if (decodeOnceOnly != null) { + this.Nonce = Convert.ToBase64String(MessagingUtilities.GetNonCryptoRandomData(NonceLength)); + } + + if (signed) { + this.Signature = this.CalculateSignature(); + } + + var fields = this.Channel.MessageDescriptions.GetAccessor(this); + string value = MessagingUtilities.CreateQueryString(fields); + + byte[] encoded = Encoding.UTF8.GetBytes(value); + + if (compressed) { + encoded = MessagingUtilities.Compress(encoded); + } + + if (encrypted) { + encoded = MessagingUtilities.Encrypt(encoded, this.Channel.AuthorizationServer.Secret); + } + + return Convert.ToBase64String(encoded); + } + + protected virtual void Decode(string value, IProtocolMessage containingMessage) { + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value)); + Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage"); + + byte[] encoded = Convert.FromBase64String(value); + + if (encrypted) { + encoded = MessagingUtilities.Decrypt(encoded, this.Channel.AuthorizationServer.Secret); + } + + if (compressed) { + encoded = MessagingUtilities.Decompress(encoded); + } + + value = Encoding.UTF8.GetString(encoded); + + // Deserialize into this newly created instance. + var fields = this.Channel.MessageDescriptions.GetAccessor(this); + var nvc = HttpUtility.ParseQueryString(value); + foreach (string key in nvc) { + fields[key] = nvc[key]; + } + + if (signed) { + // Verify that the verification code was issued by this authorization server. + ErrorUtilities.VerifyProtocol(string.Equals(this.Signature, this.CalculateSignature(), StringComparison.Ordinal), Protocol.bad_verification_code); + } + + if (maximumAge.HasValue) { + // Has this verification code expired? + DateTime expirationDate = this.CreationDateUtc + this.maximumAge.Value; + if (expirationDate < DateTime.UtcNow) { + throw new ExpiredMessageException(expirationDate, containingMessage); + } + } + + // Has this verification code already been used to obtain an access/refresh token? + 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); + throw new ReplayedMessageException(containingMessage); + } + } + } + + /// <summary> + /// Calculates the signature for the data in this verification code. + /// </summary> + /// <returns>The calculated signature.</returns> + private string CalculateSignature() { + // Sign the data, being sure to avoid any impact of the signature field itself. + var fields = this.Channel.MessageDescriptions.GetAccessor(this); + var fieldsCopy = fields.ToDictionary(); + fieldsCopy.Remove("sig"); + return this.Hasher.ComputeHash(fieldsCopy); + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs index cd26dd0..faedf8b 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs @@ -1,25 +1,23 @@ -using System.Diagnostics.Contracts; -using System.Security.Cryptography; -using System.Web; -using DotNetOpenAuth.Messaging; +//----------------------------------------------------------------------- +// <copyright file="VerificationCode.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; - using DotNetOpenAuth.OAuthWrap.Messages; - - internal class VerificationCode : MessageBase, IMessageWithEvents { - private HashAlgorithm hasher; - - private const int NonceLength = 6; + using System.Diagnostics.Contracts; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.Messaging.Bindings; + internal class VerificationCode : DataBag { /// <summary> /// Initializes a new instance of the <see cref="VerificationCode"/> class. /// </summary> /// <param name="channel">The channel.</param> /// <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) : this(channel) { Contract.Requires<ArgumentNullException>(channel != null, "channel"); @@ -28,8 +26,6 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { this.CallbackHash = this.CalculateCallbackHash(callback); this.Scope = scope; this.User = username; - this.CreationDateUtc = DateTime.UtcNow; - this.Nonce = Convert.ToBase64String(MessagingUtilities.GetNonCryptoRandomData(NonceLength)); } /// <summary> @@ -37,110 +33,44 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// </summary> /// <param name="channel">The channel.</param> private VerificationCode(OAuthWrapAuthorizationServerChannel channel) - : base(Protocol.Default.Version) { + : base(channel, true, true, true, MaximumMessageAge, channel.AuthorizationServer.VerificationCodeNonceStore) { Contract.Requires<ArgumentNullException>(channel != null, "channel"); - this.Channel = channel; - this.hasher = new HMACSHA256(this.Channel.AuthorizationServer.Secret); + Contract.Requires<ArgumentException>(channel.AuthorizationServer != null); } - /// <summary> - /// Gets or sets the channel. - /// </summary> - public OAuthWrapAuthorizationServerChannel Channel { get; set; } - - [MessagePart("cb")] - private string CallbackHash { get; set; } - [MessagePart] internal string Scope { get; set; } [MessagePart] internal string User { get; set; } - [MessagePart] - internal string Nonce { get; set; } - - [MessagePart("timestamp", IsRequired = true, Encoder = typeof(TimestampEncoder))] - internal DateTime CreationDateUtc { get; set; } - - [MessagePart("sig")] - private string Signature { get; set; } - - /// <summary> - /// Called when the message is about to be transmitted, - /// before it passes through the channel binding elements. - /// </summary> - void IMessageWithEvents.OnSending() { - // Encrypt the authorizing username so as to not expose unintended private user data - // to the client or any eavesdropping third party. - if (this.User != null) { - this.User = MessagingUtilities.Encrypt(this.User, this.Channel.AuthorizationServer.Secret); - } - - this.Signature = this.CalculateSignature(); - } + [MessagePart("cb")] + private string CallbackHash { get; set; } /// <summary> - /// Called when the message has been received, - /// after it passes through the channel binding elements. + /// Gets the maximum message age from the standard expiration binding element. /// </summary> - void IMessageWithEvents.OnReceiving() { - // Verify that the verification code was issued by this authorization server. - ErrorUtilities.VerifyProtocol(string.Equals(this.Signature, this.CalculateSignature(), StringComparison.Ordinal), Protocol.bad_verification_code); - - // Decrypt the authorizing username. - if (this.User != null) { - this.User = MessagingUtilities.Decrypt(this.User, this.Channel.AuthorizationServer.Secret); - } + private static TimeSpan MaximumMessageAge { + get { return StandardExpirationBindingElement.MaximumMessageAge; } } - internal static VerificationCode Decode(OAuthWrapAuthorizationServerChannel channel, string value) { + internal static VerificationCode 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<VerificationCode>() != null); - // Construct a new instance of this type. var self = new VerificationCode(channel); - var fields = channel.MessageDescriptions.GetAccessor(self); - - // Deserialize into this newly created instance. - var nvc = HttpUtility.ParseQueryString(value); - foreach (string key in nvc) { - fields[key] = nvc[key]; - } - - ((IMessageWithEvents)self).OnReceiving(); - + self.Decode(value, containingMessage); return self; } - internal string Encode() { - Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); - - ((IMessageWithEvents)this).OnSending(); - - var fields = this.Channel.MessageDescriptions.GetAccessor(this); - return MessagingUtilities.CreateQueryString(fields); - } - internal void VerifyCallback(Uri callback) { ErrorUtilities.VerifyProtocol(string.Equals(this.CallbackHash, this.CalculateCallbackHash(callback), StringComparison.Ordinal), Protocol.redirect_uri_mismatch); } private string CalculateCallbackHash(Uri callback) { - return this.hasher.ComputeHash(callback.AbsoluteUri); - } - - /// <summary> - /// Calculates the signature for the data in this verification code. - /// </summary> - /// <returns>The calculated signature.</returns> - private string CalculateSignature() { - // Sign the data, being sure to avoid any impact of the signature field itself. - var fields = this.Channel.MessageDescriptions.GetAccessor(this); - var fieldsCopy = fields.ToDictionary(); - fieldsCopy.Remove("sig"); - return this.hasher.ComputeHash(fieldsCopy); + return this.Hasher.ComputeHash(callback.AbsoluteUri); } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs index 125a448..285cf0f 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs @@ -18,8 +18,6 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// issued verification codes as part of obtaining access/refresh tokens. /// </summary> internal class WebAppVerificationCodeBindingElement : AuthServerBindingElementBase { - private const string VerificationCodeContext = "{VerificationCode}"; - /// <summary> /// Initializes a new instance of the <see cref="WebAppVerificationCodeBindingElement"/> class. /// </summary> @@ -38,13 +36,6 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } /// <summary> - /// Gets the maximum message age from the standard expiration binding element. - /// </summary> - private static TimeSpan MaximumMessageAge { - get { return StandardExpirationBindingElement.MaximumMessageAge; } - } - - /// <summary> /// Prepares a message for sending based on the rules of this channel binding element. /// </summary> /// <param name="message">The message to prepare for sending.</param> @@ -94,21 +85,9 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { var client = this.AuthorizationServer.GetClient(request.ClientIdentifier); ErrorUtilities.VerifyProtocol(string.Equals(client.Secret, request.ClientSecret, StringComparison.Ordinal), Protocol.incorrect_client_credentials); - var verificationCode = VerificationCode.Decode(this.OAuthChannel, request.VerificationCode); + var verificationCode = VerificationCode.Decode(this.OAuthChannel, request.VerificationCode, message); verificationCode.VerifyCallback(request.Callback); - // Has this verification code expired? - DateTime expirationDate = verificationCode.CreationDateUtc + MaximumMessageAge; - if (expirationDate < DateTime.UtcNow) { - throw new ExpiredMessageException(expirationDate, message); - } - - // Has this verification code already been used to obtain an access/refresh token? - if (!this.AuthorizationServer.VerificationCodeNonceStore.StoreNonce(VerificationCodeContext, verificationCode.Nonce, verificationCode.CreationDateUtc)) { - Logger.OpenId.ErrorFormat("Replayed nonce detected ({0} {1}). Rejecting message.", verificationCode.Nonce, verificationCode.CreationDateUtc); - throw new ReplayedMessageException(message); - } - request.Scope = verificationCode.Scope; return MessageProtections.None; diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs index b895f20..8ab77ff 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs @@ -7,12 +7,20 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { using System; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuthWrap.ChannelElements; + using DotNetOpenAuth.OAuthWrap.Messages.WebServer; /// <summary> /// A request from the client to the token endpoint for a new access token /// in exchange for a refresh token that the client has previously obtained. /// </summary> - internal class RefreshAccessTokenRequest : MessageBase { + internal class RefreshAccessTokenRequest : MessageBase, IAccessTokenRequest, IOAuthDirectResponseFormat { + /// <summary> + /// The type of message. + /// </summary> + [MessagePart(Protocol.type, IsRequired = true)] + private const string Type = "refresh"; + /// <summary> /// Initializes a new instance of the <see cref="RefreshAccessTokenRequest"/> class. /// </summary> @@ -23,11 +31,23 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { } /// <summary> + /// Gets or sets the type of the secret. + /// </summary> + /// <value>The type of the secret.</value> + /// <remarks> + /// OPTIONAL. The access token secret type as described by Section 5.3 (Cryptographic Tokens Requests). If omitted, the authorization server will issue a bearer token (an access token without a matching secret) as described by Section 5.2 (Bearer Token Requests). + /// </remarks> + [MessagePart(Protocol.secret_type, IsRequired = false, AllowEmpty = false)] + public string SecretType { get; set; } + + /// <summary> /// Gets or sets the identifier by which this client is known to the Authorization Server. /// </summary> /// <value>The client identifier.</value> [MessagePart(Protocol.client_id, IsRequired = true, AllowEmpty = false)] - internal string ClientIdentifier { get; set; } + public string ClientIdentifier { get; set; } + + public string Scope { get; set; } /// <summary> /// Gets or sets the client secret. @@ -49,14 +69,11 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { [MessagePart(Protocol.refresh_token, IsRequired = true, AllowEmpty = false)] internal string RefreshToken { get; set; } - /// <summary> - /// Gets or sets the type of the secret. - /// </summary> - /// <value>The type of the secret.</value> - /// <remarks> - /// OPTIONAL. The access token secret type as described by Section 5.3 (Cryptographic Tokens Requests). If omitted, the authorization server will issue a bearer token (an access token without a matching secret) as described by Section 5.2 (Bearer Token Requests). - /// </remarks> - [MessagePart(Protocol.secret_type, IsRequired = false, AllowEmpty = false)] - internal string SecretType { get; set; } + ResponseFormat IOAuthDirectResponseFormat.Format { + get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } + } + + [MessagePart(Protocol.format, Encoder = typeof(ResponseFormatEncoder))] + private ResponseFormat? Format { get; set; } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppAccessTokenRequest.cs index d9d482d..ff81f14 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppAccessTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppAccessTokenRequest.cs @@ -92,7 +92,7 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// OPTIONAL. The access token secret type as described by Section 5.3 (Cryptographic Tokens Requests). If omitted, the authorization server will issue a bearer token (an access token without a matching secret) as described by Section 5.2 (Bearer Token Requests). /// </remarks> [MessagePart(Protocol.secret_type, IsRequired = false, AllowEmpty = false)] - internal string SecretType { get; set; } + public string SecretType { get; set; } public string Scope { get; internal set; } |