summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2010-06-23 22:30:23 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2010-06-24 17:08:57 -0700
commit507e04b91c97fa75900a60d79e35e547e3454965 (patch)
tree25f9767dd8c76c1df252f97ec44efbdc6f8bdb69 /src
parent6dd05a3312483393bb3c27b3c7e7b1d945d583a2 (diff)
downloadDotNetOpenAuth-507e04b91c97fa75900a60d79e35e547e3454965.zip
DotNetOpenAuth-507e04b91c97fa75900a60d79e35e547e3454965.tar.gz
DotNetOpenAuth-507e04b91c97fa75900a60d79e35e547e3454965.tar.bz2
Abstracted out token serialization from the token class itself.
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj2
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs14
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs14
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs39
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs21
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs295
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/IDataBagFormatter.cs54
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs40
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/UriStyleMessageFormatter.cs306
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs59
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebServerVerificationCodeBindingElement.cs9
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/IAccessTokenAnalyzer.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs1
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentRequest.cs1
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentSuccessResponse.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebServerRequest.cs10
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs4
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/WebServerAuthorizationServer.cs9
20 files changed, 457 insertions, 429 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index 3100115..bca5705 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -312,6 +312,8 @@ http://opensource.org/licenses/ms-pl.html
<Compile Include="OAuthWrap\ChannelElements\AccessToken.cs" />
<Compile Include="OAuthWrap\ChannelElements\AuthorizationDataBag.cs" />
<Compile Include="OAuthWrap\ChannelElements\AuthServerBindingElementBase.cs" />
+ <Compile Include="OAuthWrap\ChannelElements\IDataBagFormatter.cs" />
+ <Compile Include="OAuthWrap\ChannelElements\UriStyleMessageFormatter.cs" />
<Compile Include="OAuthWrap\ChannelElements\IAccessTokenRequest.cs" />
<Compile Include="OAuthWrap\ChannelElements\IAuthorizationDescription.cs" />
<Compile Include="OAuthWrap\ChannelElements\ITokenCarryingRequest.cs" />
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index 76dab88..0767a4c 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -840,7 +840,7 @@ namespace DotNetOpenAuth.Messaging {
MessageSerializer.DeserializeJsonAsFlatDictionary(dictionary, jsonReader);
return dictionary;
}
-
+
/// <summary>
/// Prepares a message for transmit by applying signatures, nonces, etc.
/// </summary>
diff --git a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs
index db6aa2d..9f56507 100644
--- a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs
@@ -62,21 +62,19 @@ namespace DotNetOpenAuth.OAuthWrap {
Contract.Requires<ArgumentNullException>(request != null, "request");
var tokenRequest = (ITokenCarryingRequest)request;
- var accessToken = new AccessToken(
- this.AuthorizationServer.AccessTokenSigningPrivateKey,
- accessTokenEncryptingPublicKey,
- tokenRequest.AuthorizationDescription,
- accessTokenLifetime);
+ var accessTokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServer.AccessTokenSigningPrivateKey, accessTokenEncryptingPublicKey);
+ var accessToken = new AccessToken(tokenRequest.AuthorizationDescription, accessTokenLifetime);
var response = new AccessTokenSuccessResponse(request) {
Scope = tokenRequest.AuthorizationDescription.Scope,
- AccessToken = accessToken.Encode(),
+ AccessToken = accessTokenFormatter.Serialize(accessToken),
Lifetime = accessToken.Lifetime,
};
if (includeRefreshToken) {
- var refreshToken = new RefreshToken(this.AuthorizationServer.Secret, tokenRequest.AuthorizationDescription);
- response.RefreshToken = refreshToken.Encode();
+ var refreshTokenFormatter = RefreshToken.CreateFormatter(this.AuthorizationServer.Secret);
+ var refreshToken = new RefreshToken(tokenRequest.AuthorizationDescription);
+ response.RefreshToken = refreshTokenFormatter.Serialize(refreshToken);
}
return response;
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs
index 1943021..770bb23 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs
@@ -53,8 +53,10 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
public override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
var tokenRequest = message as ITokenCarryingRequest;
if (tokenRequest != null) {
- var tokenBag = (AuthorizationDataBag)tokenRequest.AuthorizationDescription;
- tokenRequest.CodeOrToken = tokenBag.Encode();
+ ErrorUtilities.VerifyInternal(tokenRequest.CodeOrTokenType == CodeOrTokenType.VerificationCode, "Only verification codes are expected here.");
+ var tokenBag = (VerificationCode)tokenRequest.AuthorizationDescription;
+ var formatter = VerificationCode.CreateFormatter(this.AuthorizationServer);
+ tokenRequest.CodeOrToken = formatter.Serialize(tokenBag);
return MessageProtections.None;
}
@@ -85,10 +87,14 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
try {
switch (tokenRequest.CodeOrTokenType) {
case CodeOrTokenType.VerificationCode:
- tokenRequest.AuthorizationDescription = VerificationCode.Decode(this.AuthorizationServer.Secret, this.AuthorizationServer.VerificationCodeNonceStore, tokenRequest.CodeOrToken, message);
+ var verificationCodeFormatter = VerificationCode.CreateFormatter(this.AuthorizationServer);
+ var verificationCode = verificationCodeFormatter.Deserialize(message, tokenRequest.CodeOrToken);
+ tokenRequest.AuthorizationDescription = verificationCode;
break;
case CodeOrTokenType.RefreshToken:
- tokenRequest.AuthorizationDescription = RefreshToken.Decode(this.AuthorizationServer.Secret, tokenRequest.CodeOrToken, message);
+ var refreshTokenFormatter = RefreshToken.CreateFormatter(this.AuthorizationServer.Secret);
+ var refreshToken = refreshTokenFormatter.Deserialize(message, tokenRequest.CodeOrToken);
+ tokenRequest.AuthorizationDescription = refreshToken;
break;
default:
throw ErrorUtilities.ThrowInternal("Unexpected value for CodeOrTokenType: " + tokenRequest.CodeOrTokenType);
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs
index 6181b60..de6ef98 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs
@@ -21,12 +21,15 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// <summary>
/// Initializes a new instance of the <see cref="AccessToken"/> class.
/// </summary>
- /// <param name="signingKey">The signing key.</param>
- /// <param name="encryptingKey">The encrypting key.</param>
+ public AccessToken() {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AccessToken"/> class.
+ /// </summary>
/// <param name="authorization">The authorization to be described by the access token.</param>
/// <param name="lifetime">The lifetime of the access token.</param>
- internal AccessToken(RSAParameters signingKey, RSAParameters encryptingKey, IAuthorizationDescription authorization, TimeSpan? lifetime)
- : this(signingKey, encryptingKey) {
+ internal AccessToken(IAuthorizationDescription authorization, TimeSpan? lifetime) {
Contract.Requires<ArgumentNullException>(authorization != null, "authorization");
this.ClientIdentifier = authorization.ClientIdentifier;
@@ -37,37 +40,17 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
}
/// <summary>
- /// Initializes a new instance of the <see cref="AccessToken"/> class.
- /// </summary>
- /// <param name="signingKey">The signing key.</param>
- /// <param name="encryptingKey">The encrypting key.</param>
- private AccessToken(RSAParameters signingKey, RSAParameters encryptingKey)
- : base(signingKey, encryptingKey) {
- }
-
- /// <summary>
/// Gets or sets the lifetime of the access token.
/// </summary>
/// <value>The lifetime.</value>
[MessagePart]
internal TimeSpan? Lifetime { get; set; }
- /// <summary>
- /// Deserializes an access token.
- /// </summary>
- /// <param name="signingKey">The signing public key.</param>
- /// <param name="encryptingKey">The encrypting private key.</param>
- /// <param name="value">The access token.</param>
- /// <param name="containingMessage">The message containing this token.</param>
- /// <returns>The access token.</returns>
- internal static AccessToken Decode(RSAParameters signingKey, RSAParameters encryptingKey, string value, IProtocolMessage containingMessage) {
- Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value));
- Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage");
- Contract.Ensures(Contract.Result<AccessToken>() != null);
+ internal static IDataBagFormatter<AccessToken> CreateFormatter(RSAParameters signingKey, RSAParameters encryptingKey)
+ {
+ Contract.Ensures(Contract.Result<IDataBagFormatter<AccessToken>>() != null);
- var self = new AccessToken(signingKey, encryptingKey);
- self.Decode(value, containingMessage);
- return self;
+ return new UriStyleMessageFormatter<AccessToken>(signingKey, encryptingKey);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs
index ef7e390..134f1af 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs
@@ -21,26 +21,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// <summary>
/// Initializes a new instance of the <see cref="AuthorizationDataBag"/> class.
/// </summary>
- /// <param name="secret">The symmetric secret to use for signing and encrypting.</param>
- /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param>
- /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param>
- /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param>
- /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param>
- /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
- protected AuthorizationDataBag(byte[] secret, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
- : base(secret, signed, encrypted, compressed, maximumAge, decodeOnceOnly) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="AuthorizationDataBag"/> class.
- /// </summary>
- /// <param name="signingKey">The asymmetric private key to use for signing the token.</param>
- /// <param name="encryptingKey">The asymmetric public key to use for encrypting the token.</param>
- /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param>
- /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param>
- /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
- protected AuthorizationDataBag(RSAParameters? signingKey = null, RSAParameters? encryptingKey = null, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
- : base(signingKey, encryptingKey, compressed, maximumAge, decodeOnceOnly) {
+ protected AuthorizationDataBag() {
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs
index 6f6d5b6..a7ec958 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs
@@ -24,126 +24,10 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// </summary>
internal abstract class DataBag : MessageBase {
/// <summary>
- /// The message description cache to use for data bag types.
- /// </summary>
- private static readonly MessageDescriptionCollection MessageDescriptions = new MessageDescriptionCollection();
-
- /// <summary>
- /// The length of the nonce to include in tokens that can be decoded once only.
- /// </summary>
- private const int NonceLength = 6;
-
- /// <summary>
- /// The symmetric secret used for signing/encryption of verification codes and refresh tokens.
- /// </summary>
- private readonly byte[] symmetricSecret;
-
- /// <summary>
- /// The hashing algorithm to use while signing when using a symmetric secret.
- /// </summary>
- private readonly HashAlgorithm symmetricHasher;
-
- /// <summary>
- /// The crypto to use for signing access tokens.
- /// </summary>
- private readonly RSACryptoServiceProvider asymmetricSigning;
-
- /// <summary>
- /// The crypto to use for encrypting access tokens.
- /// </summary>
- private readonly RSACryptoServiceProvider asymmetricEncrypting;
-
- /// <summary>
- /// The hashing algorithm to use for asymmetric signatures.
- /// </summary>
- private readonly HashAlgorithm hasherForAsymmetricSigning;
-
- /// <summary>
- /// A value indicating whether the data in this instance will be protected against tampering.
- /// </summary>
- private readonly bool signed;
-
- /// <summary>
- /// The nonce store to use to ensure that this instance is only decoded once.
- /// </summary>
- private readonly INonceStore decodeOnceOnly;
-
- /// <summary>
- /// The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.
- /// </summary>
- private readonly TimeSpan? maximumAge;
-
- /// <summary>
- /// A value indicating whether the data in this instance will be protected against eavesdropping.
- /// </summary>
- private readonly bool encrypted;
-
- /// <summary>
- /// A value indicating whether the data in this instance will be GZip'd.
- /// </summary>
- private readonly bool compressed;
-
- /// <summary>
/// Initializes a new instance of the <see cref="DataBag"/> class.
/// </summary>
- /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param>
- /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param>
- /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param>
- /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param>
- /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
- protected DataBag(bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
+ protected DataBag()
: base(Protocol.Default.Version) {
- 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.signed = signed;
- this.maximumAge = maximumAge;
- this.decodeOnceOnly = decodeOnceOnly;
- this.encrypted = encrypted;
- this.compressed = compressed;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DataBag"/> class.
- /// </summary>
- /// <param name="signingKey">The asymmetric private key to use for signing the token.</param>
- /// <param name="encryptingKey">The asymmetric public key to use for encrypting the token.</param>
- /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param>
- /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param>
- /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
- protected DataBag(RSAParameters? signingKey = null, RSAParameters? encryptingKey = null, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
- : this(signingKey.HasValue, encryptingKey.HasValue, compressed, maximumAge, decodeOnceOnly) {
- if (signingKey.HasValue) {
- this.asymmetricSigning = new RSACryptoServiceProvider();
- this.asymmetricSigning.ImportParameters(signingKey.Value);
- }
-
- if (encryptingKey.HasValue) {
- this.asymmetricEncrypting = new RSACryptoServiceProvider();
- this.asymmetricEncrypting.ImportParameters(encryptingKey.Value);
- }
-
- this.hasherForAsymmetricSigning = new SHA1CryptoServiceProvider();
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DataBag"/> class.
- /// </summary>
- /// <param name="symmetricSecret">The symmetric secret to use for signing and encrypting.</param>
- /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param>
- /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param>
- /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param>
- /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param>
- /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
- protected DataBag(byte[] symmetricSecret = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
- : this(signed, encrypted, compressed, maximumAge, decodeOnceOnly) {
- Contract.Requires<ArgumentException>(symmetricSecret != null || (!signed && !encrypted), "A secret is required when signing or encrypting is required.");
-
- if (symmetricSecret != null) {
- this.symmetricHasher = new HMACSHA256(symmetricSecret);
- }
-
- this.symmetricSecret = symmetricSecret;
}
/// <summary>
@@ -161,9 +45,16 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
internal DateTime UtcCreationDate { get; set; }
/// <summary>
+ /// Gets or sets the signature.
+ /// </summary>
+ /// <value>The signature.</value>
+ [MessagePart("sig")]
+ internal byte[] Signature { get; set; }
+
+ /// <summary>
/// Gets or sets the message that delivered this DataBag instance to this host.
/// </summary>
- protected IProtocolMessage ContainingMessage { get; set; }
+ protected internal IProtocolMessage ContainingMessage { get; set; }
/// <summary>
/// Gets the type of this instance.
@@ -176,173 +67,5 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
private string BagType {
get { return this.GetType().Name; }
}
-
- /// <summary>
- /// Gets or sets the signature.
- /// </summary>
- /// <value>The signature.</value>
- [MessagePart("sig")]
- private byte[] Signature { get; set; }
-
- /// <summary>
- /// Serializes this instance as a string that can be sent as part of a larger message.
- /// </summary>
- /// <returns>The serialized version of the data in this instance.</returns>
- internal virtual string Encode() {
- Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));
-
- this.UtcCreationDate = DateTime.UtcNow;
-
- if (this.decodeOnceOnly != null) {
- this.Nonce = MessagingUtilities.GetNonCryptoRandomData(NonceLength);
- }
-
- if (this.signed) {
- this.Signature = this.CalculateSignature();
- }
-
- var fields = MessageSerializer.Get(this.GetType()).Serialize(MessageDescriptions.GetAccessor(this));
- string value = MessagingUtilities.CreateQueryString(fields);
-
- byte[] encoded = Encoding.UTF8.GetBytes(value);
-
- if (this.compressed) {
- encoded = MessagingUtilities.Compress(encoded);
- }
-
- if (this.encrypted) {
- encoded = this.Encrypt(encoded);
- }
-
- return Convert.ToBase64String(encoded);
- }
-
- /// <summary>
- /// Populates this instance with data from a given string.
- /// </summary>
- /// <param name="value">The value to deserialize from.</param>
- /// <param name="containingMessage">The message that contained this token.</param>
- protected virtual void Decode(string value, IProtocolMessage containingMessage) {
- Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value));
- Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage");
-
- this.ContainingMessage = containingMessage;
- byte[] encoded = Convert.FromBase64String(value);
-
- if (this.encrypted) {
- encoded = this.Decrypt(encoded);
- }
-
- if (this.compressed) {
- encoded = MessagingUtilities.Decompress(encoded);
- }
-
- value = Encoding.UTF8.GetString(encoded);
-
- // Deserialize into this newly created instance.
- var serializer = MessageSerializer.Get(this.GetType());
- var fields = MessageDescriptions.GetAccessor(this);
- serializer.Deserialize(HttpUtility.ParseQueryString(value).ToDictionary(), fields);
-
- if (this.signed) {
- // Verify that the verification code was issued by this authorization server.
- ErrorUtilities.VerifyProtocol(this.IsSignatureValid(), Protocol.bad_verification_code);
- }
-
- if (this.maximumAge.HasValue) {
- // Has this verification code expired?
- DateTime expirationDate = this.UtcCreationDate + 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 (this.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, Convert.ToBase64String(this.Nonce), this.UtcCreationDate)) {
- Logger.OpenId.ErrorFormat("Replayed nonce detected ({0} {1}). Rejecting message.", this.Nonce, this.UtcCreationDate);
- throw new ReplayedMessageException(containingMessage);
- }
- }
- }
-
- /// <summary>
- /// Determines whether the signature on this instance is valid.
- /// </summary>
- /// <returns>
- /// <c>true</c> if the signature is valid; otherwise, <c>false</c>.
- /// </returns>
- private bool IsSignatureValid() {
- if (this.asymmetricSigning != null) {
- byte[] bytesToSign = this.GetBytesToSign();
- return this.asymmetricSigning.VerifyData(bytesToSign, this.hasherForAsymmetricSigning, this.Signature);
- } else {
- return MessagingUtilities.AreEquivalent(this.Signature, this.CalculateSignature());
- }
- }
-
- /// <summary>
- /// Calculates the signature for the data in this verification code.
- /// </summary>
- /// <returns>The calculated signature.</returns>
- private byte[] CalculateSignature() {
- Contract.Requires<InvalidOperationException>(this.asymmetricSigning != null || this.symmetricHasher != null);
- Contract.Ensures(Contract.Result<byte[]>() != null);
-
- byte[] bytesToSign = this.GetBytesToSign();
- if (this.asymmetricSigning != null) {
- return this.asymmetricSigning.SignData(bytesToSign, this.hasherForAsymmetricSigning);
- } else {
- return this.symmetricHasher.ComputeHash(bytesToSign);
- }
- }
-
- /// <summary>
- /// Gets the bytes to sign.
- /// </summary>
- /// <returns>A buffer of the bytes to sign.</returns>
- private byte[] GetBytesToSign() {
- // Sign the data, being sure to avoid any impact of the signature field itself.
- var fields = MessageDescriptions.GetAccessor(this);
- var fieldsCopy = fields.ToDictionary();
- fieldsCopy.Remove("sig");
-
- var sortedData = new SortedDictionary<string, string>(fieldsCopy, StringComparer.OrdinalIgnoreCase);
- string value = MessagingUtilities.CreateQueryString(sortedData);
- byte[] bytesToSign = Encoding.UTF8.GetBytes(value);
- return bytesToSign;
- }
-
- /// <summary>
- /// Encrypts the specified value using either the symmetric or asymmetric encryption algorithm as appropriate.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>The encrypted value.</returns>
- private byte[] Encrypt(byte[] value) {
- Contract.Requires<InvalidOperationException>(this.asymmetricEncrypting != null || this.symmetricSecret != null);
-
- if (this.asymmetricEncrypting != null) {
- return this.asymmetricEncrypting.EncryptWithRandomSymmetricKey(value);
- } else {
- return MessagingUtilities.Encrypt(value, this.symmetricSecret);
- }
- }
-
- /// <summary>
- /// Decrypts the specified value using either the symmetric or asymmetric encryption algorithm as appropriate.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>The decrypted value.</returns>
- private byte[] Decrypt(byte[] value) {
- Contract.Requires<InvalidOperationException>(this.asymmetricEncrypting != null || this.symmetricSecret != null);
-
- if (this.asymmetricEncrypting != null) {
- return this.asymmetricEncrypting.DecryptWithRandomSymmetricKey(value);
- } else {
- return MessagingUtilities.Decrypt(value, this.symmetricSecret);
- }
- }
}
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IDataBagFormatter.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IDataBagFormatter.cs
new file mode 100644
index 0000000..8e1d76b
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IDataBagFormatter.cs
@@ -0,0 +1,54 @@
+//-----------------------------------------------------------------------
+// <copyright file="IDataBagFormatter.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
+ using System;
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A serializer for <see cref="DataBag"/>-derived types
+ /// </summary>
+ /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam>
+ [ContractClass(typeof(IDataBagFormatterContract<>))]
+ internal interface IDataBagFormatter<T> where T : DataBag, new() {
+ string Serialize(T message);
+
+ T Deserialize(IProtocolMessage containingMessage, string data);
+ }
+
+ /// <summary>
+ /// Contract class for the IDataBagFormatter interface.
+ /// </summary>
+ /// <typeparam name="T">The type of DataBag to serialize.</typeparam>
+ [ContractClassFor(typeof(IDataBagFormatter<>))]
+ internal abstract class IDataBagFormatterContract<T> : IDataBagFormatter<T> where T : DataBag, new() {
+ /// <summary>
+ /// Prevents a default instance of the <see cref="IDataBagFormatterContract&lt;T&gt;"/> class from being created.
+ /// </summary>
+ private IDataBagFormatterContract() {
+ }
+
+ #region IDataBagFormatter<T> Members
+
+ string IDataBagFormatter<T>.Serialize(T message) {
+ Contract.Requires<ArgumentNullException>(message != null, "message");
+ Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
+
+ throw new System.NotImplementedException();
+ }
+
+ T IDataBagFormatter<T>.Deserialize(IProtocolMessage containingMessage, string data) {
+ Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(data));
+ Contract.Ensures(Contract.Result<T>() != null);
+
+ throw new System.NotImplementedException();
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs
index 029d583..ea08da0 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs
@@ -175,7 +175,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
return (IDirectedProtocolMessage)this.Receive(fields, recipient);
}
-
+
return base.ReadFromRequestCore(request);
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs
index e95c5cc..55f3e69 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs
@@ -20,11 +20,14 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// <summary>
/// Initializes a new instance of the <see cref="RefreshToken"/> class.
/// </summary>
- /// <param name="secret">The symmetric secret used to sign/encrypt refresh tokens.</param>
+ public RefreshToken() {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RefreshToken"/> class.
+ /// </summary>
/// <param name="authorization">The authorization this refresh token should describe.</param>
- internal RefreshToken(byte[] secret, IAuthorizationDescription authorization)
- : this(secret) {
- Contract.Requires<ArgumentNullException>(secret != null, "secret");
+ internal RefreshToken(IAuthorizationDescription authorization) {
Contract.Requires<ArgumentNullException>(authorization != null, "authorization");
this.ClientIdentifier = authorization.ClientIdentifier;
@@ -33,31 +36,12 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
this.Scope = authorization.Scope;
}
- /// <summary>
- /// Initializes a new instance of the <see cref="RefreshToken"/> class.
- /// </summary>
- /// <param name="secret">The symmetric secret used to sign/encrypt refresh tokens.</param>
- private RefreshToken(byte[] secret)
- : base(secret, true, true) {
- Contract.Requires<ArgumentNullException>(secret != null, "secret");
- }
-
- /// <summary>
- /// Deserializes a refresh token.
- /// </summary>
- /// <param name="secret">The symmetric secret used to sign and encrypt the token.</param>
- /// <param name="value">The token.</param>
- /// <param name="containingMessage">The message containing this token.</param>
- /// <returns>The refresh token.</returns>
- internal static RefreshToken Decode(byte[] secret, string value, IProtocolMessage containingMessage) {
- Contract.Requires<ArgumentNullException>(secret != null, "secret");
- Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value));
- Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage");
- Contract.Ensures(Contract.Result<RefreshToken>() != null);
+ internal static IDataBagFormatter<RefreshToken> CreateFormatter(byte[] symmetricSecret)
+ {
+ Contract.Requires<ArgumentNullException>(symmetricSecret != null, "symmetricSecret");
+ Contract.Ensures(Contract.Result<IDataBagFormatter<RefreshToken>>() != null);
- var self = new RefreshToken(secret);
- self.Decode(value, containingMessage);
- return self;
+ return new UriStyleMessageFormatter<RefreshToken>(symmetricSecret, true, true);
}
}
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/UriStyleMessageFormatter.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/UriStyleMessageFormatter.cs
new file mode 100644
index 0000000..12c532a
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/UriStyleMessageFormatter.cs
@@ -0,0 +1,306 @@
+//-----------------------------------------------------------------------
+// <copyright file="UriStyleMessageFormatter.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.Messaging.Reflection;
+
+ /// <summary>
+ /// A serializer for <see cref="DataBag"/>-derived types
+ /// </summary>
+ /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam>
+ internal class UriStyleMessageFormatter<T> : IDataBagFormatter<T> where T : DataBag, new() {
+ /// <summary>
+ /// The length of the nonce to include in tokens that can be decoded once only.
+ /// </summary>
+ private const int NonceLength = 6;
+
+ /// <summary>
+ /// The message description cache to use for data bag types.
+ /// </summary>
+ private static readonly MessageDescriptionCollection MessageDescriptions = new MessageDescriptionCollection();
+
+ /// <summary>
+ /// The symmetric secret used for signing/encryption of verification codes and refresh tokens.
+ /// </summary>
+ private readonly byte[] symmetricSecret;
+
+ /// <summary>
+ /// The hashing algorithm to use while signing when using a symmetric secret.
+ /// </summary>
+ private readonly HashAlgorithm symmetricHasher;
+
+ /// <summary>
+ /// The crypto to use for signing access tokens.
+ /// </summary>
+ private readonly RSACryptoServiceProvider asymmetricSigning;
+
+ /// <summary>
+ /// The crypto to use for encrypting access tokens.
+ /// </summary>
+ private readonly RSACryptoServiceProvider asymmetricEncrypting;
+
+ /// <summary>
+ /// The hashing algorithm to use for asymmetric signatures.
+ /// </summary>
+ private readonly HashAlgorithm hasherForAsymmetricSigning;
+
+ /// <summary>
+ /// A value indicating whether the data in this instance will be protected against tampering.
+ /// </summary>
+ private readonly bool signed;
+
+ /// <summary>
+ /// The nonce store to use to ensure that this instance is only decoded once.
+ /// </summary>
+ private readonly INonceStore decodeOnceOnly;
+
+ /// <summary>
+ /// The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.
+ /// </summary>
+ private readonly TimeSpan? maximumAge;
+
+ /// <summary>
+ /// A value indicating whether the data in this instance will be protected against eavesdropping.
+ /// </summary>
+ private readonly bool encrypted;
+
+ /// <summary>
+ /// A value indicating whether the data in this instance will be GZip'd.
+ /// </summary>
+ private readonly bool compressed;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriStyleMessageFormatter&lt;T&gt;"/> class.
+ /// </summary>
+ /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param>
+ /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param>
+ /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param>
+ /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param>
+ /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
+ internal UriStyleMessageFormatter(bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) {
+ 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.signed = signed;
+ this.maximumAge = maximumAge;
+ this.decodeOnceOnly = decodeOnceOnly;
+ this.encrypted = encrypted;
+ this.compressed = compressed;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriStyleMessageFormatter&lt;T&gt;"/> class.
+ /// </summary>
+ /// <param name="signingKey">The asymmetric private key to use for signing the token.</param>
+ /// <param name="encryptingKey">The asymmetric public key to use for encrypting the token.</param>
+ /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param>
+ /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param>
+ /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
+ internal UriStyleMessageFormatter(RSAParameters? signingKey = null, RSAParameters? encryptingKey = null, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
+ : this(signingKey.HasValue, encryptingKey.HasValue, compressed, maximumAge, decodeOnceOnly) {
+ if (signingKey.HasValue) {
+ this.asymmetricSigning = new RSACryptoServiceProvider();
+ this.asymmetricSigning.ImportParameters(signingKey.Value);
+ }
+
+ if (encryptingKey.HasValue) {
+ this.asymmetricEncrypting = new RSACryptoServiceProvider();
+ this.asymmetricEncrypting.ImportParameters(encryptingKey.Value);
+ }
+
+ this.hasherForAsymmetricSigning = new SHA1CryptoServiceProvider();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriStyleMessageFormatter&lt;T&gt;"/> class.
+ /// </summary>
+ /// <param name="symmetricSecret">The symmetric secret to use for signing and encrypting.</param>
+ /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param>
+ /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param>
+ /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param>
+ /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param>
+ /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param>
+ internal UriStyleMessageFormatter(byte[] symmetricSecret = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null)
+ : this(signed, encrypted, compressed, maximumAge, decodeOnceOnly) {
+ Contract.Requires<ArgumentException>(symmetricSecret != null || (!signed && !encrypted), "A secret is required when signing or encrypting is required.");
+
+ if (symmetricSecret != null) {
+ this.symmetricHasher = new HMACSHA256(symmetricSecret);
+ }
+
+ this.symmetricSecret = symmetricSecret;
+ }
+
+ public string Serialize(T message) {
+ message.UtcCreationDate = DateTime.UtcNow;
+
+ if (this.decodeOnceOnly != null) {
+ message.Nonce = MessagingUtilities.GetNonCryptoRandomData(NonceLength);
+ }
+
+ if (this.signed) {
+ message.Signature = this.CalculateSignature(message);
+ }
+
+ var fields = MessageSerializer.Get(message.GetType()).Serialize(MessageDescriptions.GetAccessor(message));
+ string value = MessagingUtilities.CreateQueryString(fields);
+
+ byte[] encoded = Encoding.UTF8.GetBytes(value);
+
+ if (this.compressed) {
+ encoded = MessagingUtilities.Compress(encoded);
+ }
+
+ if (this.encrypted) {
+ encoded = this.Encrypt(encoded);
+ }
+
+ return Convert.ToBase64String(encoded);
+ }
+
+ public T Deserialize(IProtocolMessage containingMessage, string value) {
+ var message = new T();
+ message.ContainingMessage = containingMessage;
+ byte[] data = Convert.FromBase64String(value);
+
+ if (this.encrypted) {
+ data = this.Decrypt(data);
+ }
+
+ if (this.compressed) {
+ data = MessagingUtilities.Decompress(data);
+ }
+
+ value = Encoding.UTF8.GetString(data);
+
+ // Deserialize into message newly created instance.
+ var serializer = MessageSerializer.Get(message.GetType());
+ var fields = MessageDescriptions.GetAccessor(message);
+ serializer.Deserialize(HttpUtility.ParseQueryString(value).ToDictionary(), fields);
+
+ if (this.signed) {
+ // Verify that the verification code was issued by message authorization server.
+ ErrorUtilities.VerifyProtocol(this.IsSignatureValid(message), Protocol.bad_verification_code);
+ }
+
+ if (this.maximumAge.HasValue) {
+ // Has message verification code expired?
+ DateTime expirationDate = message.UtcCreationDate + this.maximumAge.Value;
+ if (expirationDate < DateTime.UtcNow) {
+ throw new ExpiredMessageException(expirationDate, containingMessage);
+ }
+ }
+
+ // Has message verification code already been used to obtain an access/refresh token?
+ if (this.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, Convert.ToBase64String(message.Nonce), message.UtcCreationDate)) {
+ Logger.OpenId.ErrorFormat("Replayed nonce detected ({0} {1}). Rejecting message.", message.Nonce, message.UtcCreationDate);
+ throw new ReplayedMessageException(containingMessage);
+ }
+ }
+
+ ((IMessage)message).EnsureValidMessage();
+
+ return message;
+ }
+
+ /// <summary>
+ /// Determines whether the signature on this instance is valid.
+ /// </summary>
+ /// <param name="message">The message whose signature is to be checked.</param>
+ /// <returns>
+ /// <c>true</c> if the signature is valid; otherwise, <c>false</c>.
+ /// </returns>
+ private bool IsSignatureValid(DataBag message) {
+ Contract.Requires<ArgumentNullException>(message != null, "message");
+
+ if (this.asymmetricSigning != null) {
+ byte[] bytesToSign = this.GetBytesToSign(message);
+ return this.asymmetricSigning.VerifyData(bytesToSign, this.hasherForAsymmetricSigning, message.Signature);
+ } else {
+ return MessagingUtilities.AreEquivalent(message.Signature, this.CalculateSignature(message));
+ }
+ }
+
+ /// <summary>
+ /// Calculates the signature for the data in this verification code.
+ /// </summary>
+ /// <param name="message">The message whose signature is to be calculated.</param>
+ /// <returns>The calculated signature.</returns>
+ private byte[] CalculateSignature(DataBag message) {
+ Contract.Requires<ArgumentNullException>(message != null, "message");
+ Contract.Requires<InvalidOperationException>(this.asymmetricSigning != null || this.symmetricHasher != null);
+ Contract.Ensures(Contract.Result<byte[]>() != null);
+
+ byte[] bytesToSign = this.GetBytesToSign(message);
+ if (this.asymmetricSigning != null) {
+ return this.asymmetricSigning.SignData(bytesToSign, this.hasherForAsymmetricSigning);
+ } else {
+ return this.symmetricHasher.ComputeHash(bytesToSign);
+ }
+ }
+
+ /// <summary>
+ /// Gets the bytes to sign.
+ /// </summary>
+ /// <param name="message">The message to be encoded as normalized bytes for signing.</param>
+ /// <returns>A buffer of the bytes to sign.</returns>
+ private byte[] GetBytesToSign(DataBag message) {
+ Contract.Requires<ArgumentNullException>(message != null, "message");
+
+ // Sign the data, being sure to avoid any impact of the signature field itself.
+ var fields = MessageDescriptions.GetAccessor(message);
+ var fieldsCopy = fields.ToDictionary();
+ fieldsCopy.Remove("sig");
+
+ var sortedData = new SortedDictionary<string, string>(fieldsCopy, StringComparer.OrdinalIgnoreCase);
+ string value = MessagingUtilities.CreateQueryString(sortedData);
+ byte[] bytesToSign = Encoding.UTF8.GetBytes(value);
+ return bytesToSign;
+ }
+
+ /// <summary>
+ /// Encrypts the specified value using either the symmetric or asymmetric encryption algorithm as appropriate.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <returns>The encrypted value.</returns>
+ private byte[] Encrypt(byte[] value) {
+ Contract.Requires<InvalidOperationException>(this.asymmetricEncrypting != null || this.symmetricSecret != null);
+
+ if (this.asymmetricEncrypting != null) {
+ return this.asymmetricEncrypting.EncryptWithRandomSymmetricKey(value);
+ } else {
+ return MessagingUtilities.Encrypt(value, this.symmetricSecret);
+ }
+ }
+
+ /// <summary>
+ /// Decrypts the specified value using either the symmetric or asymmetric encryption algorithm as appropriate.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <returns>The decrypted value.</returns>
+ private byte[] Decrypt(byte[] value) {
+ Contract.Requires<InvalidOperationException>(this.asymmetricEncrypting != null || this.symmetricSecret != null);
+
+ if (this.asymmetricEncrypting != null) {
+ return this.asymmetricEncrypting.DecryptWithRandomSymmetricKey(value);
+ } else {
+ return MessagingUtilities.Decrypt(value, this.symmetricSecret);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs
index 24fe3a6..84380d9 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs
@@ -25,18 +25,18 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
/// <summary>
/// Initializes a new instance of the <see cref="VerificationCode"/> class.
/// </summary>
- /// <param name="secret">The symmetric secret to use in signing/encryption.</param>
- /// <param name="nonceStore">The nonce store to use to ensure verification codes are used only once.</param>
+ public VerificationCode() {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VerificationCode"/> class.
+ /// </summary>
/// <param name="clientIdentifier">The client identifier.</param>
/// <param name="callback">The callback the client used to obtain authorization.</param>
/// <param name="scope">The scope.</param>
/// <param name="username">The name on the account that authorized access.</param>
- internal VerificationCode(byte[] secret, INonceStore nonceStore, string clientIdentifier, Uri callback, string scope, string username)
- : this(secret, nonceStore) {
- Contract.Requires<ArgumentNullException>(secret != null, "secret");
- Contract.Requires<ArgumentNullException>(nonceStore != null, "nonceStore");
+ internal VerificationCode(string clientIdentifier, Uri callback, string scope, string username) {
Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(clientIdentifier));
- Contract.Requires<ArgumentNullException>(secret != null, "secret");
Contract.Requires<ArgumentNullException>(callback != null, "callback");
this.ClientIdentifier = clientIdentifier;
@@ -46,47 +46,22 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
}
/// <summary>
- /// Initializes a new instance of the <see cref="VerificationCode"/> class.
- /// </summary>
- /// <param name="secret">The symmetric secret to use in signing/encryption.</param>
- /// <param name="nonceStore">The nonce store to use to ensure verification codes are used only once.</param>
- private VerificationCode(byte[] secret, INonceStore nonceStore)
- : base(secret, true, true, false, MaximumMessageAge, nonceStore) {
- Contract.Requires<ArgumentNullException>(secret != null, "secret");
- Contract.Requires<ArgumentNullException>(nonceStore != null, "nonceStore");
- }
-
- /// <summary>
- /// Gets the maximum message age from the standard expiration binding element.
- /// </summary>
- private static TimeSpan MaximumMessageAge {
- get { return StandardExpirationBindingElement.MaximumMessageAge; }
- }
-
- /// <summary>
/// Gets or sets the hash of the callback URL.
/// </summary>
[MessagePart("cb")]
private byte[] CallbackHash { get; set; }
- /// <summary>
- /// Deserializes a verification code.
- /// </summary>
- /// <param name="secret">The symmetric secret used to sign and encrypt the token.</param>
- /// <param name="nonceStore">The nonce store to use to ensure verification codes can only be exchanged once.</param>
- /// <param name="value">The verification token.</param>
- /// <param name="containingMessage">The message containing this verification code.</param>
- /// <returns>The verification code.</returns>
- internal static VerificationCode Decode(byte[] secret, INonceStore nonceStore, string value, IProtocolMessage containingMessage) {
- Contract.Requires<ArgumentNullException>(secret != null, "secret");
- Contract.Requires<ArgumentNullException>(nonceStore != null, "nonceStore");
- Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value));
- Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage");
- Contract.Ensures(Contract.Result<VerificationCode>() != null);
+ internal static IDataBagFormatter<VerificationCode> CreateFormatter(IAuthorizationServer authorizationServer) {
+ Contract.Requires<ArgumentNullException>(authorizationServer != null, "authorizationServer");
+ Contract.Ensures(Contract.Result<IDataBagFormatter<VerificationCode>>() != null);
- var self = new VerificationCode(secret, nonceStore);
- self.Decode(value, containingMessage);
- return self;
+ return new UriStyleMessageFormatter<VerificationCode>(
+ authorizationServer.Secret,
+ true,
+ true,
+ false,
+ WebServerVerificationCodeBindingElement.MaximumMessageAge,
+ authorizationServer.VerificationCodeNonceStore);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebServerVerificationCodeBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebServerVerificationCodeBindingElement.cs
index 4c6e2de..1c71ebe 100644
--- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebServerVerificationCodeBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebServerVerificationCodeBindingElement.cs
@@ -36,6 +36,13 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
}
/// <summary>
+ /// Gets the maximum message age from the standard expiration binding element.
+ /// </summary>
+ internal 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>
@@ -53,7 +60,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
var directResponse = (IDirectResponseProtocolMessage)response;
var request = (EndUserAuthorizationRequest)directResponse.OriginatingRequest;
ITokenCarryingRequest tokenCarryingResponse = response;
- tokenCarryingResponse.AuthorizationDescription = new VerificationCode(this.AuthorizationServer.Secret, this.AuthorizationServer.VerificationCodeNonceStore, request.ClientIdentifier, request.Callback, request.Scope, response.AuthorizingUsername);
+ tokenCarryingResponse.AuthorizationDescription = new VerificationCode(request.ClientIdentifier, request.Callback, request.Scope, response.AuthorizingUsername);
return MessageProtections.None;
}
diff --git a/src/DotNetOpenAuth/OAuthWrap/IAccessTokenAnalyzer.cs b/src/DotNetOpenAuth/OAuthWrap/IAccessTokenAnalyzer.cs
index 4e692c6..06e8e90 100644
--- a/src/DotNetOpenAuth/OAuthWrap/IAccessTokenAnalyzer.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/IAccessTokenAnalyzer.cs
@@ -16,6 +16,7 @@ namespace DotNetOpenAuth.OAuthWrap {
/// An interface that resource server hosts should implement if they accept access tokens
/// issued by non-DotNetOpenAuth authorization servers.
/// </summary>
+ [ContractClass((typeof(IAccessTokenAnalyzerContract)))]
public interface IAccessTokenAnalyzer {
/// <summary>
/// Reads an access token to find out what data it authorizes access to.
@@ -31,6 +32,7 @@ namespace DotNetOpenAuth.OAuthWrap {
/// <summary>
/// Code contract for the <see cref="IAccessTokenAnalyzer"/> interface.
/// </summary>
+ [ContractClassFor(typeof(IAccessTokenAnalyzer))]
internal abstract class IAccessTokenAnalyzerContract : IAccessTokenAnalyzer {
/// <summary>
/// Prevents a default instance of the <see cref="IAccessTokenAnalyzerContract"/> class from being created.
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs
index 1a8094e..50e697a 100644
--- a/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs
@@ -13,6 +13,7 @@ namespace DotNetOpenAuth.OAuthWrap.Messages {
/// <summary>
/// A common message base class for OAuth WRAP messages.
/// </summary>
+ [Serializable]
public class MessageBase : IDirectedProtocolMessage, IDirectResponseProtocolMessage {
/// <summary>
/// A dictionary to contain extra message data.
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentRequest.cs
index df6b07e..e36a1f1 100644
--- a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentRequest.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentRequest.cs
@@ -15,6 +15,7 @@ namespace DotNetOpenAuth.OAuthWrap.Messages {
/// A message requesting user authorization to access protected data on behalf
/// of an installed application or browser-hosted Javascript.
/// </summary>
+ [Serializable]
internal class UserAgentRequest : EndUserAuthorizationRequest {
/// <summary>
/// The type of message.
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentSuccessResponse.cs
index a4f5b1a..a0e082b 100644
--- a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentSuccessResponse.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentSuccessResponse.cs
@@ -37,7 +37,7 @@ namespace DotNetOpenAuth.OAuthWrap.Messages {
}
/// <summary>
- /// Gets the access token.
+ /// Gets or sets the access token.
/// </summary>
/// <value>The access token.</value>
[MessagePart(Protocol.access_token, IsRequired = true, AllowEmpty = false)]
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebServerRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebServerRequest.cs
index 9024cb2..1843c64 100644
--- a/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebServerRequest.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebServerRequest.cs
@@ -12,6 +12,12 @@ namespace DotNetOpenAuth.OAuthWrap.Messages {
using System.Text;
using DotNetOpenAuth.Messaging;
+ /// <summary>
+ /// A message sent by a web application Client to the AuthorizationServer
+ /// via the user agent to obtain authorization from the user and prepare
+ /// to issue an access token to the Consumer if permission is granted.
+ /// </summary>
+ [Serializable]
public class WebServerRequest : EndUserAuthorizationRequest {
/// <summary>
/// The type of message.
@@ -20,7 +26,7 @@ namespace DotNetOpenAuth.OAuthWrap.Messages {
private const string Type = "web_server";
/// <summary>
- /// Initializes a new instance of the <see cref="EndUserAuthorizationRequest"/> class.
+ /// Initializes a new instance of the <see cref="WebServerRequest"/> class.
/// </summary>
/// <param name="authorizationEndpoint">The Authorization Server's user authorization URL to direct the user to.</param>
/// <param name="version">The protocol version.</param>
@@ -31,7 +37,7 @@ namespace DotNetOpenAuth.OAuthWrap.Messages {
}
/// <summary>
- /// Initializes a new instance of the <see cref="EndUserAuthorizationRequest"/> class.
+ /// Initializes a new instance of the <see cref="WebServerRequest"/> class.
/// </summary>
/// <param name="authorizationServer">The authorization server.</param>
internal WebServerRequest(AuthorizationServerDescription authorizationServer)
diff --git a/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs b/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs
index 769fba1..3cd138c 100644
--- a/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs
@@ -5,6 +5,7 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OAuthWrap {
+ using System;
using System.Security.Cryptography;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuthWrap.ChannelElements;
@@ -46,7 +47,8 @@ namespace DotNetOpenAuth.OAuthWrap {
/// A value indicating whether this access token is valid.
/// </returns>
public bool TryValidateAccessToken(IDirectedProtocolMessage message, string accessToken, out string user, out string scope) {
- var token = AccessToken.Decode(this.AuthorizationServerPublicSigningKey, this.ResourceServerPrivateEncryptionKey, accessToken, message);
+ var accessTokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServerPublicSigningKey, this.ResourceServerPrivateEncryptionKey);
+ var token = accessTokenFormatter.Deserialize(message, accessToken);
user = token.User;
scope = token.Scope;
return true;
diff --git a/src/DotNetOpenAuth/OAuthWrap/WebServerAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/WebServerAuthorizationServer.cs
index bfd53fd..868af77 100644
--- a/src/DotNetOpenAuth/OAuthWrap/WebServerAuthorizationServer.cs
+++ b/src/DotNetOpenAuth/OAuthWrap/WebServerAuthorizationServer.cs
@@ -60,18 +60,15 @@ namespace DotNetOpenAuth.OAuthWrap {
this.Channel.Send(response);
}
- public bool TryPrepareAccessTokenResponse(out IDirectResponseProtocolMessage response)
- {
+ public bool TryPrepareAccessTokenResponse(out IDirectResponseProtocolMessage response) {
return this.TryPrepareAccessTokenResponse(this.Channel.GetRequestFromContext(), out response);
}
- public bool TryPrepareAccessTokenResponse(HttpRequestInfo httpRequestInfo, out IDirectResponseProtocolMessage response)
- {
+ public bool TryPrepareAccessTokenResponse(HttpRequestInfo httpRequestInfo, out IDirectResponseProtocolMessage response) {
Contract.Requires<ArgumentNullException>(httpRequestInfo != null, "httpRequestInfo");
var request = this.ReadAccessTokenRequest(httpRequestInfo);
- if (request != null)
- {
+ if (request != null) {
// This convenience method only encrypts access tokens assuming that this auth server
// doubles as the resource server.
response = this.PrepareAccessTokenResponse(request, this.AuthorizationServer.AccessTokenSigningPrivateKey);