summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2010-05-31 15:44:46 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2010-05-31 15:44:46 -0700
commit0284a9702e786bb85017dae1b9074e15904224b5 (patch)
tree9664b98238de27e1345fceb6c8312aedbfa6122c /src
parent49eb5203633516d6985a772a585a1801061d1187 (diff)
downloadDotNetOpenAuth-0284a9702e786bb85017dae1b9074e15904224b5.zip
DotNetOpenAuth-0284a9702e786bb85017dae1b9074e15904224b5.tar.gz
DotNetOpenAuth-0284a9702e786bb85017dae1b9074e15904224b5.tar.bz2
Work toward a signed refresh token.
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj3
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs31
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAccessTokenRequest.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs1
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshAccessTokenBindingElement.cs46
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs24
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/SignedDataBag.cs148
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs114
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs23
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs39
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppAccessTokenRequest.cs2
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; }