summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements')
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IConsumerDescription.cs59
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderAccessToken.cs48
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderRequestToken.cs52
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderTokenManager.cs251
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/ITokenGenerator.cs40
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthIdentity.cs64
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthPrincipal.cs109
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthServiceProviderChannel.cs74
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs126
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/RsaSha1ServiceProviderSigningBindingElement.cs81
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/StandardTokenGenerator.cs64
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/TokenHandlingBindingElement.cs201
12 files changed, 1169 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IConsumerDescription.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IConsumerDescription.cs
new file mode 100644
index 0000000..db505d5
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IConsumerDescription.cs
@@ -0,0 +1,59 @@
+//-----------------------------------------------------------------------
+// <copyright file="IConsumerDescription.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Security.Cryptography.X509Certificates;
+
+ /// <summary>
+ /// A description of a consumer from a Service Provider's point of view.
+ /// </summary>
+ public interface IConsumerDescription {
+ /// <summary>
+ /// Gets the Consumer key.
+ /// </summary>
+ string Key { get; }
+
+ /// <summary>
+ /// Gets the consumer secret.
+ /// </summary>
+ string Secret { get; }
+
+ /// <summary>
+ /// Gets the certificate that can be used to verify the signature of an incoming
+ /// message from a Consumer.
+ /// </summary>
+ /// <returns>The public key from the Consumer's X.509 Certificate, if one can be found; otherwise <c>null</c>.</returns>
+ /// <remarks>
+ /// This property must be implemented only if the RSA-SHA1 algorithm is supported by the Service Provider.
+ /// </remarks>
+ X509Certificate2 Certificate { get; }
+
+ /// <summary>
+ /// Gets the callback URI that this consumer has pre-registered with the service provider, if any.
+ /// </summary>
+ /// <value>A URI that user authorization responses should be directed to; or <c>null</c> if no preregistered callback was arranged.</value>
+ Uri Callback { get; }
+
+ /// <summary>
+ /// Gets the verification code format that is most appropriate for this consumer
+ /// when a callback URI is not available.
+ /// </summary>
+ /// <value>A set of characters that can be easily keyed in by the user given the Consumer's
+ /// application type and form factor.</value>
+ /// <remarks>
+ /// The value <see cref="OAuth.VerificationCodeFormat.IncludedInCallback"/> should NEVER be returned
+ /// since this property is only used in no callback scenarios anyway.
+ /// </remarks>
+ VerificationCodeFormat VerificationCodeFormat { get; }
+
+ /// <summary>
+ /// Gets the length of the verification code to issue for this Consumer.
+ /// </summary>
+ /// <value>A positive number, generally at least 4.</value>
+ int VerificationCodeLength { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderAccessToken.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderAccessToken.cs
new file mode 100644
index 0000000..35ba52d
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderAccessToken.cs
@@ -0,0 +1,48 @@
+//-----------------------------------------------------------------------
+// <copyright file="IServiceProviderAccessToken.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A description of an access token and its metadata as required by a Service Provider.
+ /// </summary>
+ public interface IServiceProviderAccessToken {
+ /// <summary>
+ /// Gets the token itself.
+ /// </summary>
+ string Token { get; }
+
+ /// <summary>
+ /// Gets the expiration date (local time) for the access token.
+ /// </summary>
+ /// <value>The expiration date, or <c>null</c> if there is no expiration date.</value>
+ DateTime? ExpirationDate { get; }
+
+ /// <summary>
+ /// Gets the username of the principal that will be impersonated by this access token.
+ /// </summary>
+ /// <value>
+ /// The name of the user who authorized the OAuth request token originally.
+ /// </value>
+ [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Username", Justification = "Breaking change.")]
+ string Username { get; }
+
+ /// <summary>
+ /// Gets the roles that the OAuth principal should belong to.
+ /// </summary>
+ /// <value>
+ /// The roles that the user belongs to, or a subset of these according to the rights
+ /// granted when the user authorized the request token.
+ /// </value>
+ [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "By design.")]
+ string[] Roles { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderRequestToken.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderRequestToken.cs
new file mode 100644
index 0000000..6dfa416
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderRequestToken.cs
@@ -0,0 +1,52 @@
+//-----------------------------------------------------------------------
+// <copyright file="IServiceProviderRequestToken.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using DotNetOpenAuth.OAuth.Messages;
+
+ /// <summary>
+ /// A description of a request token and its metadata as required by a Service Provider
+ /// </summary>
+ public interface IServiceProviderRequestToken {
+ /// <summary>
+ /// Gets the token itself.
+ /// </summary>
+ string Token { get; }
+
+ /// <summary>
+ /// Gets the consumer key that requested this token.
+ /// </summary>
+ string ConsumerKey { get; }
+
+ /// <summary>
+ /// Gets the (local) date that this request token was first created on.
+ /// </summary>
+ DateTime CreatedOn { get; }
+
+ /// <summary>
+ /// Gets or sets the callback associated specifically with this token, if any.
+ /// </summary>
+ /// <value>The callback URI; or <c>null</c> if no callback was specifically assigned to this token.</value>
+ Uri Callback { get; set; }
+
+ /// <summary>
+ /// Gets or sets the verifier that the consumer must include in the <see cref="AuthorizedTokenRequest"/>
+ /// message to exchange this request token for an access token.
+ /// </summary>
+ /// <value>The verifier code, or <c>null</c> if none has been assigned (yet).</value>
+ string VerificationCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the version of the Consumer that requested this token.
+ /// </summary>
+ /// <remarks>
+ /// This property is used to determine whether a <see cref="VerificationCode"/> must be
+ /// generated when the user authorizes the Consumer or not.
+ /// </remarks>
+ Version ConsumerVersion { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderTokenManager.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderTokenManager.cs
new file mode 100644
index 0000000..13320b3
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/IServiceProviderTokenManager.cs
@@ -0,0 +1,251 @@
+//-----------------------------------------------------------------------
+// <copyright file="IServiceProviderTokenManager.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A token manager for use by a web site in its role as a
+ /// service provider.
+ /// </summary>
+ [ContractClass(typeof(IServiceProviderTokenManagerContract))]
+ public interface IServiceProviderTokenManager : ITokenManager {
+ /// <summary>
+ /// Gets the Consumer description for a given a Consumer Key.
+ /// </summary>
+ /// <param name="consumerKey">The Consumer Key.</param>
+ /// <returns>A description of the consumer. Never null.</returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the consumer key cannot be found.</exception>
+ IConsumerDescription GetConsumer(string consumerKey);
+
+ /// <summary>
+ /// Checks whether a given request token has already been authorized
+ /// by some user for use by the Consumer that requested it.
+ /// </summary>
+ /// <param name="requestToken">The Consumer's request token.</param>
+ /// <returns>
+ /// True if the request token has already been fully authorized by the user
+ /// who owns the relevant protected resources. False if the token has not yet
+ /// been authorized, has expired or does not exist.
+ /// </returns>
+ bool IsRequestTokenAuthorized(string requestToken);
+
+ /// <summary>
+ /// Gets details on the named request token.
+ /// </summary>
+ /// <param name="token">The request token.</param>
+ /// <returns>A description of the token. Never null.</returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception>
+ /// <remarks>
+ /// It is acceptable for implementations to find the token, see that it has expired,
+ /// delete it from the database and then throw <see cref="KeyNotFoundException"/>,
+ /// or alternatively it can return the expired token anyway and the OAuth channel will
+ /// log and throw the appropriate error.
+ /// </remarks>
+ IServiceProviderRequestToken GetRequestToken(string token);
+
+ /// <summary>
+ /// Gets details on the named access token.
+ /// </summary>
+ /// <param name="token">The access token.</param>
+ /// <returns>A description of the token. Never null.</returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception>
+ /// <remarks>
+ /// It is acceptable for implementations to find the token, see that it has expired,
+ /// delete it from the database and then throw <see cref="KeyNotFoundException"/>,
+ /// or alternatively it can return the expired token anyway and the OAuth channel will
+ /// log and throw the appropriate error.
+ /// </remarks>
+ IServiceProviderAccessToken GetAccessToken(string token);
+
+ /// <summary>
+ /// Persists any changes made to the token.
+ /// </summary>
+ /// <param name="token">The token whose properties have been changed.</param>
+ /// <remarks>
+ /// This library will invoke this method after making a set
+ /// of changes to the token as part of a web request to give the host
+ /// the opportunity to persist those changes to a database.
+ /// Depending on the object persistence framework the host site uses,
+ /// this method MAY not need to do anything (if changes made to the token
+ /// will automatically be saved without any extra handling).
+ /// </remarks>
+ void UpdateToken(IServiceProviderRequestToken token);
+ }
+
+ /// <summary>
+ /// Code contract class for the <see cref="IServiceProviderTokenManager"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(IServiceProviderTokenManager))]
+ internal abstract class IServiceProviderTokenManagerContract : IServiceProviderTokenManager {
+ /// <summary>
+ /// Prevents a default instance of the <see cref="IServiceProviderTokenManagerContract"/> class from being created.
+ /// </summary>
+ private IServiceProviderTokenManagerContract() {
+ }
+
+ #region IServiceProviderTokenManager Members
+
+ /// <summary>
+ /// Gets the Consumer description for a given a Consumer Key.
+ /// </summary>
+ /// <param name="consumerKey">The Consumer Key.</param>
+ /// <returns>
+ /// A description of the consumer. Never null.
+ /// </returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the consumer key cannot be found.</exception>
+ IConsumerDescription IServiceProviderTokenManager.GetConsumer(string consumerKey) {
+ Requires.NotNullOrEmpty(consumerKey, "consumerKey");
+ Contract.Ensures(Contract.Result<IConsumerDescription>() != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Checks whether a given request token has already been authorized
+ /// by some user for use by the Consumer that requested it.
+ /// </summary>
+ /// <param name="requestToken">The Consumer's request token.</param>
+ /// <returns>
+ /// True if the request token has already been fully authorized by the user
+ /// who owns the relevant protected resources. False if the token has not yet
+ /// been authorized, has expired or does not exist.
+ /// </returns>
+ bool IServiceProviderTokenManager.IsRequestTokenAuthorized(string requestToken) {
+ Requires.NotNullOrEmpty(requestToken, "requestToken");
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets details on the named request token.
+ /// </summary>
+ /// <param name="token">The request token.</param>
+ /// <returns>A description of the token. Never null.</returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception>
+ /// <remarks>
+ /// It is acceptable for implementations to find the token, see that it has expired,
+ /// delete it from the database and then throw <see cref="KeyNotFoundException"/>,
+ /// or alternatively it can return the expired token anyway and the OAuth channel will
+ /// log and throw the appropriate error.
+ /// </remarks>
+ IServiceProviderRequestToken IServiceProviderTokenManager.GetRequestToken(string token) {
+ Requires.NotNullOrEmpty(token, "token");
+ Contract.Ensures(Contract.Result<IServiceProviderRequestToken>() != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets details on the named access token.
+ /// </summary>
+ /// <param name="token">The access token.</param>
+ /// <returns>A description of the token. Never null.</returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception>
+ /// <remarks>
+ /// It is acceptable for implementations to find the token, see that it has expired,
+ /// delete it from the database and then throw <see cref="KeyNotFoundException"/>,
+ /// or alternatively it can return the expired token anyway and the OAuth channel will
+ /// log and throw the appropriate error.
+ /// </remarks>
+ IServiceProviderAccessToken IServiceProviderTokenManager.GetAccessToken(string token) {
+ Requires.NotNullOrEmpty(token, "token");
+ Contract.Ensures(Contract.Result<IServiceProviderAccessToken>() != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Persists any changes made to the token.
+ /// </summary>
+ /// <param name="token">The token whose properties have been changed.</param>
+ /// <remarks>
+ /// This library will invoke this method after making a set
+ /// of changes to the token as part of a web request to give the host
+ /// the opportunity to persist those changes to a database.
+ /// Depending on the object persistence framework the host site uses,
+ /// this method MAY not need to do anything (if changes made to the token
+ /// will automatically be saved without any extra handling).
+ /// </remarks>
+ void IServiceProviderTokenManager.UpdateToken(IServiceProviderRequestToken token) {
+ Requires.NotNull(token, "token");
+ throw new NotImplementedException();
+ }
+
+ #endregion
+
+ #region ITokenManager Members
+
+ /// <summary>
+ /// Gets the Token Secret given a request or access token.
+ /// </summary>
+ /// <param name="token">The request or access token.</param>
+ /// <returns>
+ /// The secret associated with the given token.
+ /// </returns>
+ /// <exception cref="ArgumentException">Thrown if the secret cannot be found for the given token.</exception>
+ string ITokenManager.GetTokenSecret(string token) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Stores a newly generated unauthorized request token, secret, and optional
+ /// application-specific parameters for later recall.
+ /// </summary>
+ /// <param name="request">The request message that resulted in the generation of a new unauthorized request token.</param>
+ /// <param name="response">The response message that includes the unauthorized request token.</param>
+ /// <exception cref="ArgumentException">Thrown if the consumer key is not registered, or a required parameter was not found in the parameters collection.</exception>
+ /// <remarks>
+ /// Request tokens stored by this method SHOULD NOT associate any user account with this token.
+ /// It usually opens up security holes in your application to do so. Instead, you associate a user
+ /// account with access tokens (not request tokens) in the <see cref="ITokenManager.ExpireRequestTokenAndStoreNewAccessToken"/>
+ /// method.
+ /// </remarks>
+ void ITokenManager.StoreNewRequestToken(DotNetOpenAuth.OAuth.Messages.UnauthorizedTokenRequest request, DotNetOpenAuth.OAuth.Messages.ITokenSecretContainingMessage response) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Deletes a request token and its associated secret and stores a new access token and secret.
+ /// </summary>
+ /// <param name="consumerKey">The Consumer that is exchanging its request token for an access token.</param>
+ /// <param name="requestToken">The Consumer's request token that should be deleted/expired.</param>
+ /// <param name="accessToken">The new access token that is being issued to the Consumer.</param>
+ /// <param name="accessTokenSecret">The secret associated with the newly issued access token.</param>
+ /// <remarks>
+ /// <para>
+ /// Any scope of granted privileges associated with the request token from the
+ /// original call to <see cref="ITokenManager.StoreNewRequestToken"/> should be carried over
+ /// to the new Access Token.
+ /// </para>
+ /// <para>
+ /// To associate a user account with the new access token,
+ /// <see cref="System.Web.HttpContext.User">HttpContext.Current.User</see> may be
+ /// useful in an ASP.NET web application within the implementation of this method.
+ /// Alternatively you may store the access token here without associating with a user account,
+ /// and wait until WebConsumer.ProcessUserAuthorization or
+ /// DesktopConsumer.ProcessUserAuthorization return the access
+ /// token to associate the access token with a user account at that point.
+ /// </para>
+ /// </remarks>
+ void ITokenManager.ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Classifies a token as a request token or an access token.
+ /// </summary>
+ /// <param name="token">The token to classify.</param>
+ /// <returns>
+ /// Request or Access token, or invalid if the token is not recognized.
+ /// </returns>
+ TokenType ITokenManager.GetTokenType(string token) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/ITokenGenerator.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/ITokenGenerator.cs
new file mode 100644
index 0000000..ce22479
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/ITokenGenerator.cs
@@ -0,0 +1,40 @@
+//-----------------------------------------------------------------------
+// <copyright file="ITokenGenerator.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ /// <summary>
+ /// An interface allowing OAuth hosts to inject their own algorithm for generating tokens and secrets.
+ /// </summary>
+ public interface ITokenGenerator {
+ /// <summary>
+ /// Generates a new token to represent a not-yet-authorized request to access protected resources.
+ /// </summary>
+ /// <param name="consumerKey">The consumer that requested this token.</param>
+ /// <returns>The newly generated token.</returns>
+ /// <remarks>
+ /// This method should not store the newly generated token in any persistent store.
+ /// This will be done in <see cref="ITokenManager.StoreNewRequestToken"/>.
+ /// </remarks>
+ string GenerateRequestToken(string consumerKey);
+
+ /// <summary>
+ /// Generates a new token to represent an authorized request to access protected resources.
+ /// </summary>
+ /// <param name="consumerKey">The consumer that requested this token.</param>
+ /// <returns>The newly generated token.</returns>
+ /// <remarks>
+ /// This method should not store the newly generated token in any persistent store.
+ /// This will be done in <see cref="ITokenManager.ExpireRequestTokenAndStoreNewAccessToken"/>.
+ /// </remarks>
+ string GenerateAccessToken(string consumerKey);
+
+ /// <summary>
+ /// Returns a cryptographically strong random string for use as a token secret.
+ /// </summary>
+ /// <returns>The generated string.</returns>
+ string GenerateSecret();
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthIdentity.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthIdentity.cs
new file mode 100644
index 0000000..6865f79
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthIdentity.cs
@@ -0,0 +1,64 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthIdentity.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Runtime.InteropServices;
+ using System.Security.Principal;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// Represents an OAuth consumer that is impersonating a known user on the system.
+ /// </summary>
+ [SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Justification = "Not cocreatable.")]
+ [Serializable]
+ [ComVisible(true)]
+ public class OAuthIdentity : IIdentity {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthIdentity"/> class.
+ /// </summary>
+ /// <param name="username">The username.</param>
+ internal OAuthIdentity(string username) {
+ Requires.NotNullOrEmpty(username, "username");
+ this.Name = username;
+ }
+
+ #region IIdentity Members
+
+ /// <summary>
+ /// Gets the type of authentication used.
+ /// </summary>
+ /// <value>The constant "OAuth"</value>
+ /// <returns>
+ /// The type of authentication used to identify the user.
+ /// </returns>
+ public string AuthenticationType {
+ get { return "OAuth"; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the user has been authenticated.
+ /// </summary>
+ /// <value>The value <c>true</c></value>
+ /// <returns>true if the user was authenticated; otherwise, false.
+ /// </returns>
+ public bool IsAuthenticated {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Gets the name of the user who authorized the OAuth token the consumer is using for authorization.
+ /// </summary>
+ /// <returns>
+ /// The name of the user on whose behalf the code is running.
+ /// </returns>
+ public string Name { get; private set; }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthPrincipal.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthPrincipal.cs
new file mode 100644
index 0000000..f4d167d
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthPrincipal.cs
@@ -0,0 +1,109 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthPrincipal.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Runtime.InteropServices;
+ using System.Security.Principal;
+
+ /// <summary>
+ /// Represents an OAuth consumer that is impersonating a known user on the system.
+ /// </summary>
+ [SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Justification = "Not cocreatable.")]
+ [Serializable]
+ [ComVisible(true)]
+ public class OAuthPrincipal : IPrincipal {
+ /// <summary>
+ /// The roles this user belongs to.
+ /// </summary>
+ private ICollection<string> roles;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class.
+ /// </summary>
+ /// <param name="userName">The username.</param>
+ /// <param name="roles">The roles this user belongs to.</param>
+ public OAuthPrincipal(string userName, string[] roles)
+ : this(new OAuthIdentity(userName), roles) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class.
+ /// </summary>
+ /// <param name="token">The access token.</param>
+ internal OAuthPrincipal(IServiceProviderAccessToken token)
+ : this(token.Username, token.Roles) {
+ Requires.NotNull(token, "token");
+
+ this.AccessToken = token.Token;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class.
+ /// </summary>
+ /// <param name="identity">The identity.</param>
+ /// <param name="roles">The roles this user belongs to.</param>
+ internal OAuthPrincipal(OAuthIdentity identity, string[] roles) {
+ this.Identity = identity;
+ this.roles = roles;
+ }
+
+ /// <summary>
+ /// Gets the access token used to create this principal.
+ /// </summary>
+ /// <value>A non-empty string.</value>
+ public string AccessToken { get; private set; }
+
+ /// <summary>
+ /// Gets the roles that this principal has as a ReadOnlyCollection.
+ /// </summary>
+ public ReadOnlyCollection<string> Roles
+ {
+ get { return new ReadOnlyCollection<string>(this.roles.ToList()); }
+ }
+
+ #region IPrincipal Members
+
+ /// <summary>
+ /// Gets the identity of the current principal.
+ /// </summary>
+ /// <value></value>
+ /// <returns>
+ /// The <see cref="T:System.Security.Principal.IIdentity"/> object associated with the current principal.
+ /// </returns>
+ public IIdentity Identity { get; private set; }
+
+ /// <summary>
+ /// Determines whether the current principal belongs to the specified role.
+ /// </summary>
+ /// <param name="role">The name of the role for which to check membership.</param>
+ /// <returns>
+ /// true if the current principal is a member of the specified role; otherwise, false.
+ /// </returns>
+ /// <remarks>
+ /// The role membership check uses <see cref="StringComparer.OrdinalIgnoreCase"/>.
+ /// </remarks>
+ public bool IsInRole(string role) {
+ return this.roles.Contains(role, StringComparer.OrdinalIgnoreCase);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Creates a new instance of GenericPrincipal based on this OAuthPrincipal.
+ /// </summary>
+ /// <returns>A new instance of GenericPrincipal with a GenericIdentity, having the same username and roles as this OAuthPrincipal and OAuthIdentity</returns>
+ public GenericPrincipal CreateGenericPrincipal()
+ {
+ return new GenericPrincipal(new GenericIdentity(this.Identity.Name), this.roles.ToArray());
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthServiceProviderChannel.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthServiceProviderChannel.cs
new file mode 100644
index 0000000..43e7902
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthServiceProviderChannel.cs
@@ -0,0 +1,74 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthServiceProviderChannel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+
+ /// <summary>
+ /// The messaging channel for OAuth 1.0(a) Service Providers.
+ /// </summary>
+ internal class OAuthServiceProviderChannel : OAuthChannel {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthServiceProviderChannel"/> class.
+ /// </summary>
+ /// <param name="signingBindingElement">The binding element to use for signing.</param>
+ /// <param name="store">The web application store to use for nonces.</param>
+ /// <param name="tokenManager">The token manager instance to use.</param>
+ /// <param name="securitySettings">The security settings.</param>
+ /// <param name="messageTypeProvider">The message type provider.</param>
+ [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "securitySettings", Justification = "Code contracts")]
+ internal OAuthServiceProviderChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, IServiceProviderTokenManager tokenManager, ServiceProviderSecuritySettings securitySettings, IMessageFactory messageTypeProvider = null)
+ : base(
+ signingBindingElement,
+ store,
+ tokenManager,
+ securitySettings,
+ messageTypeProvider ?? new OAuthServiceProviderMessageFactory(tokenManager),
+ InitializeBindingElements(signingBindingElement, store, tokenManager, securitySettings)) {
+ Requires.NotNull(tokenManager, "tokenManager");
+ Requires.NotNull(securitySettings, "securitySettings");
+ Requires.NotNull(signingBindingElement, "signingBindingElement");
+ }
+
+ /// <summary>
+ /// Gets the consumer secret for a given consumer key.
+ /// </summary>
+ /// <param name="consumerKey">The consumer key.</param>
+ /// <returns>The consumer secret.</returns>
+ protected override string GetConsumerSecret(string consumerKey) {
+ return ((IServiceProviderTokenManager)this.TokenManager).GetConsumer(consumerKey).Secret;
+ }
+
+ /// <summary>
+ /// Initializes the binding elements for the OAuth channel.
+ /// </summary>
+ /// <param name="signingBindingElement">The signing binding element.</param>
+ /// <param name="store">The nonce store.</param>
+ /// <param name="tokenManager">The token manager.</param>
+ /// <param name="securitySettings">The security settings.</param>
+ /// <returns>
+ /// An array of binding elements used to initialize the channel.
+ /// </returns>
+ private static new IChannelBindingElement[] InitializeBindingElements(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager, SecuritySettings securitySettings) {
+ Contract.Requires(securitySettings != null);
+
+ var bindingElements = OAuthChannel.InitializeBindingElements(signingBindingElement, store, tokenManager, securitySettings);
+
+ var spTokenManager = tokenManager as IServiceProviderTokenManager;
+ var serviceProviderSecuritySettings = securitySettings as ServiceProviderSecuritySettings;
+ bindingElements.Insert(0, new TokenHandlingBindingElement(spTokenManager, serviceProviderSecuritySettings));
+
+ return bindingElements.ToArray();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs
new file mode 100644
index 0000000..565756b
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs
@@ -0,0 +1,126 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthServiceProviderMessageFactory.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth.Messages;
+
+ /// <summary>
+ /// An OAuth-protocol specific implementation of the <see cref="IMessageFactory"/>
+ /// interface.
+ /// </summary>
+ public class OAuthServiceProviderMessageFactory : IMessageFactory {
+ /// <summary>
+ /// The token manager to use for discerning between request and access tokens.
+ /// </summary>
+ private IServiceProviderTokenManager tokenManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthServiceProviderMessageFactory"/> class.
+ /// </summary>
+ /// <param name="tokenManager">The token manager instance to use.</param>
+ public OAuthServiceProviderMessageFactory(IServiceProviderTokenManager tokenManager) {
+ Requires.NotNull(tokenManager, "tokenManager");
+
+ this.tokenManager = tokenManager;
+ }
+
+ #region IMessageFactory Members
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="recipient">The intended or actual recipient of the request message.</param>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ /// <remarks>
+ /// The request messages are:
+ /// UnauthorizedTokenRequest
+ /// AuthorizedTokenRequest
+ /// UserAuthorizationRequest
+ /// AccessProtectedResourceRequest
+ /// </remarks>
+ public virtual IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) {
+ MessageBase message = null;
+ Protocol protocol = Protocol.V10; // default to assuming the less-secure 1.0 instead of 1.0a until we prove otherwise.
+ string token;
+ fields.TryGetValue("oauth_token", out token);
+
+ try {
+ if (fields.ContainsKey("oauth_consumer_key") && !fields.ContainsKey("oauth_token")) {
+ protocol = fields.ContainsKey("oauth_callback") ? Protocol.V10a : Protocol.V10;
+ message = new UnauthorizedTokenRequest(recipient, protocol.Version);
+ } else if (fields.ContainsKey("oauth_consumer_key") && fields.ContainsKey("oauth_token")) {
+ // Discern between RequestAccessToken and AccessProtectedResources,
+ // which have all the same parameters, by figuring out what type of token
+ // is in the token parameter.
+ bool tokenTypeIsAccessToken = this.tokenManager.GetTokenType(token) == TokenType.AccessToken;
+
+ if (tokenTypeIsAccessToken) {
+ message = (MessageBase)new AccessProtectedResourceRequest(recipient, protocol.Version);
+ } else {
+ // Discern between 1.0 and 1.0a requests by checking on the consumer version we stored
+ // when the consumer first requested an unauthorized token.
+ protocol = Protocol.Lookup(this.tokenManager.GetRequestToken(token).ConsumerVersion);
+ message = new AuthorizedTokenRequest(recipient, protocol.Version);
+ }
+ } else {
+ // fail over to the message with no required fields at all.
+ if (token != null) {
+ protocol = Protocol.Lookup(this.tokenManager.GetRequestToken(token).ConsumerVersion);
+ }
+
+ // If a callback parameter is included, that suggests either the consumer
+ // is following OAuth 1.0 instead of 1.0a, or that a hijacker is trying
+ // to attack. Either way, if the consumer started out as a 1.0a, keep it
+ // that way, and we'll just ignore the oauth_callback included in this message
+ // by virtue of the UserAuthorizationRequest message not including it in its
+ // 1.0a payload.
+ message = new UserAuthorizationRequest(recipient, protocol.Version);
+ }
+
+ if (message != null) {
+ message.SetAsIncoming();
+ }
+
+ return message;
+ } catch (KeyNotFoundException ex) {
+ throw ErrorUtilities.Wrap(ex, OAuthStrings.TokenNotFound);
+ }
+ }
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="request">
+ /// The message that was sent as a request that resulted in the response.
+ /// Null on a Consumer site that is receiving an indirect message from the Service Provider.
+ /// </param>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// The <see cref="IProtocolMessage"/>-derived concrete class that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ /// <remarks>
+ /// The response messages are:
+ /// None.
+ /// </remarks>
+ public virtual IDirectResponseProtocolMessage GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields) {
+ Logger.OAuth.Error("Service Providers are not expected to ever receive responses.");
+ return null;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/RsaSha1ServiceProviderSigningBindingElement.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/RsaSha1ServiceProviderSigningBindingElement.cs
new file mode 100644
index 0000000..06a005b
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/RsaSha1ServiceProviderSigningBindingElement.cs
@@ -0,0 +1,81 @@
+//-----------------------------------------------------------------------
+// <copyright file="RsaSha1ServiceProviderSigningBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Diagnostics.Contracts;
+ using System.Security.Cryptography;
+ using System.Security.Cryptography.X509Certificates;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A binding element that signs outgoing messages and verifies the signature on incoming messages.
+ /// </summary>
+ public class RsaSha1ServiceProviderSigningBindingElement : RsaSha1SigningBindingElement {
+ /// <summary>
+ /// The token manager for the service provider.
+ /// </summary>
+ private IServiceProviderTokenManager tokenManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RsaSha1ServiceProviderSigningBindingElement"/> class.
+ /// </summary>
+ /// <param name="tokenManager">The token manager.</param>
+ public RsaSha1ServiceProviderSigningBindingElement(IServiceProviderTokenManager tokenManager) {
+ Requires.NotNull(tokenManager, "tokenManager");
+
+ this.tokenManager = tokenManager;
+ }
+
+ /// <summary>
+ /// Determines whether the signature on some message is valid.
+ /// </summary>
+ /// <param name="message">The message to check the signature on.</param>
+ /// <returns>
+ /// <c>true</c> if the signature on the message is valid; otherwise, <c>false</c>.
+ /// </returns>
+ protected override bool IsSignatureValid(ITamperResistantOAuthMessage message) {
+ ErrorUtilities.VerifyInternal(this.tokenManager != null, "No token manager available for fetching Consumer public certificates.");
+
+ string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message));
+ byte[] data = Encoding.ASCII.GetBytes(signatureBaseString);
+
+ byte[] carriedSignature = Convert.FromBase64String(message.Signature);
+
+ X509Certificate2 cert = this.tokenManager.GetConsumer(message.ConsumerKey).Certificate;
+ if (cert == null) {
+ Logger.Signatures.WarnFormat("Incoming message from consumer '{0}' could not be matched with an appropriate X.509 certificate for signature verification.", message.ConsumerKey);
+ return false;
+ }
+
+ var provider = (RSACryptoServiceProvider)cert.PublicKey.Key;
+ bool valid = provider.VerifyData(data, "SHA1", carriedSignature);
+ return valid;
+ }
+
+ /// <summary>
+ /// Calculates a signature for a given message.
+ /// </summary>
+ /// <param name="message">The message to sign.</param>
+ /// <returns>
+ /// The signature for the message.
+ /// </returns>
+ protected override string GetSignature(ITamperResistantOAuthMessage message) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Clones this instance.
+ /// </summary>
+ /// <returns>
+ /// A new instance of the binding element.
+ /// </returns>
+ protected override ITamperProtectionChannelBindingElement Clone() {
+ return new RsaSha1ServiceProviderSigningBindingElement(this.tokenManager);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/StandardTokenGenerator.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/StandardTokenGenerator.cs
new file mode 100644
index 0000000..d18f5fe
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/StandardTokenGenerator.cs
@@ -0,0 +1,64 @@
+//-----------------------------------------------------------------------
+// <copyright file="StandardTokenGenerator.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Security.Cryptography;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A cryptographically strong random string generator for tokens and secrets.
+ /// </summary>
+ internal class StandardTokenGenerator : ITokenGenerator {
+ #region ITokenGenerator Members
+
+ /// <summary>
+ /// Generates a new token to represent a not-yet-authorized request to access protected resources.
+ /// </summary>
+ /// <param name="consumerKey">The consumer that requested this token.</param>
+ /// <returns>The newly generated token.</returns>
+ /// <remarks>
+ /// This method should not store the newly generated token in any persistent store.
+ /// This will be done in <see cref="ITokenManager.StoreNewRequestToken"/>.
+ /// </remarks>
+ public string GenerateRequestToken(string consumerKey) {
+ return GenerateCryptographicallyStrongString();
+ }
+
+ /// <summary>
+ /// Generates a new token to represent an authorized request to access protected resources.
+ /// </summary>
+ /// <param name="consumerKey">The consumer that requested this token.</param>
+ /// <returns>The newly generated token.</returns>
+ /// <remarks>
+ /// This method should not store the newly generated token in any persistent store.
+ /// This will be done in <see cref="ITokenManager.ExpireRequestTokenAndStoreNewAccessToken"/>.
+ /// </remarks>
+ public string GenerateAccessToken(string consumerKey) {
+ return GenerateCryptographicallyStrongString();
+ }
+
+ /// <summary>
+ /// Returns a cryptographically strong random string for use as a token secret.
+ /// </summary>
+ /// <returns>The generated string.</returns>
+ public string GenerateSecret() {
+ return GenerateCryptographicallyStrongString();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Returns a new random string.
+ /// </summary>
+ /// <returns>The new random string.</returns>
+ private static string GenerateCryptographicallyStrongString() {
+ byte[] buffer = new byte[20];
+ MessagingUtilities.CryptoRandomDataGenerator.GetBytes(buffer);
+ return Convert.ToBase64String(buffer);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/TokenHandlingBindingElement.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/TokenHandlingBindingElement.cs
new file mode 100644
index 0000000..596336a
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ChannelElements/TokenHandlingBindingElement.cs
@@ -0,0 +1,201 @@
+//-----------------------------------------------------------------------
+// <copyright file="TokenHandlingBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth.Messages;
+
+ /// <summary>
+ /// A binding element for Service Providers to manage the
+ /// callbacks and verification codes on applicable messages.
+ /// </summary>
+ internal class TokenHandlingBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// The token manager offered by the service provider.
+ /// </summary>
+ private IServiceProviderTokenManager tokenManager;
+
+ /// <summary>
+ /// The security settings for this service provider.
+ /// </summary>
+ private ServiceProviderSecuritySettings securitySettings;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TokenHandlingBindingElement"/> class.
+ /// </summary>
+ /// <param name="tokenManager">The token manager.</param>
+ /// <param name="securitySettings">The security settings.</param>
+ [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contract"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "securitySettings", Justification = "Code contracts")]
+ internal TokenHandlingBindingElement(IServiceProviderTokenManager tokenManager, ServiceProviderSecuritySettings securitySettings) {
+ Requires.NotNull(tokenManager, "tokenManager");
+ Requires.NotNull(securitySettings, "securitySettings");
+
+ this.tokenManager = tokenManager;
+ this.securitySettings = securitySettings;
+ }
+
+ #region IChannelBindingElement Members
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ /// <remarks>
+ /// This property is set by the channel when it is first constructed.
+ /// </remarks>
+ public Channel Channel { get; set; }
+
+ /// <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 MessageProtections Protection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <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>
+ /// <returns>
+ /// The protections (if any) that this binding element applied to the message.
+ /// Null if this binding element did not even apply to this binding element.
+ /// </returns>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
+ var userAuthResponse = message as UserAuthorizationResponse;
+ if (userAuthResponse != null && userAuthResponse.Version >= Protocol.V10a.Version) {
+ var requestToken = this.tokenManager.GetRequestToken(userAuthResponse.RequestToken);
+ requestToken.VerificationCode = userAuthResponse.VerificationCode;
+ this.tokenManager.UpdateToken(requestToken);
+ return MessageProtections.None;
+ }
+
+ // Hook to store the token and secret on its way down to the Consumer.
+ var grantRequestTokenResponse = message as UnauthorizedTokenResponse;
+ if (grantRequestTokenResponse != null) {
+ this.tokenManager.StoreNewRequestToken(grantRequestTokenResponse.RequestMessage, grantRequestTokenResponse);
+
+ // The host may have already set these properties, but just to make sure...
+ var requestToken = this.tokenManager.GetRequestToken(grantRequestTokenResponse.RequestToken);
+ requestToken.ConsumerVersion = grantRequestTokenResponse.Version;
+ if (grantRequestTokenResponse.RequestMessage.Callback != null) {
+ requestToken.Callback = grantRequestTokenResponse.RequestMessage.Callback;
+ }
+ this.tokenManager.UpdateToken(requestToken);
+
+ return MessageProtections.None;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Performs any transformation on an incoming message that may be necessary and/or
+ /// validates an incoming message based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// The protections (if any) that this binding element applied to the message.
+ /// Null if this binding element did not even apply to this binding element.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) {
+ var authorizedTokenRequest = message as AuthorizedTokenRequest;
+ if (authorizedTokenRequest != null) {
+ if (authorizedTokenRequest.Version >= Protocol.V10a.Version) {
+ string expectedVerifier = this.tokenManager.GetRequestToken(authorizedTokenRequest.RequestToken).VerificationCode;
+ ErrorUtilities.VerifyProtocol(string.Equals(authorizedTokenRequest.VerificationCode, expectedVerifier, StringComparison.Ordinal), OAuthStrings.IncorrectVerifier);
+ return MessageProtections.None;
+ }
+
+ this.VerifyThrowTokenTimeToLive(authorizedTokenRequest);
+ }
+
+ var userAuthorizationRequest = message as UserAuthorizationRequest;
+ if (userAuthorizationRequest != null) {
+ this.VerifyThrowTokenTimeToLive(userAuthorizationRequest);
+ }
+
+ var accessResourceRequest = message as AccessProtectedResourceRequest;
+ if (accessResourceRequest != null) {
+ this.VerifyThrowTokenNotExpired(accessResourceRequest);
+ }
+
+ return null;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Ensures that access tokens have not yet expired.
+ /// </summary>
+ /// <param name="message">The incoming message carrying the access token.</param>
+ private void VerifyThrowTokenNotExpired(AccessProtectedResourceRequest message) {
+ Requires.NotNull(message, "message");
+
+ try {
+ IServiceProviderAccessToken token = this.tokenManager.GetAccessToken(message.AccessToken);
+ if (token.ExpirationDate.HasValue && DateTime.Now >= token.ExpirationDate.Value.ToLocalTimeSafe()) {
+ Logger.OAuth.ErrorFormat(
+ "OAuth access token {0} rejected because it expired at {1}, and it is now {2}.",
+ token.Token,
+ token.ExpirationDate.Value,
+ DateTime.Now);
+ ErrorUtilities.ThrowProtocol(OAuthStrings.TokenNotFound);
+ }
+ } catch (KeyNotFoundException ex) {
+ throw ErrorUtilities.Wrap(ex, OAuthStrings.TokenNotFound);
+ }
+ }
+
+ /// <summary>
+ /// Ensures that short-lived request tokens included in incoming messages have not expired.
+ /// </summary>
+ /// <param name="message">The incoming message.</param>
+ /// <exception cref="ProtocolException">Thrown when the token in the message has expired.</exception>
+ private void VerifyThrowTokenTimeToLive(ITokenContainingMessage message) {
+ ErrorUtilities.VerifyInternal(!(message is AccessProtectedResourceRequest), "We shouldn't be verifying TTL on access tokens.");
+ if (message == null || string.IsNullOrEmpty(message.Token)) {
+ return;
+ }
+
+ try {
+ IServiceProviderRequestToken token = this.tokenManager.GetRequestToken(message.Token);
+ TimeSpan ttl = this.securitySettings.MaximumRequestTokenTimeToLive;
+ if (DateTime.Now >= token.CreatedOn.ToLocalTimeSafe() + ttl) {
+ Logger.OAuth.ErrorFormat(
+ "OAuth request token {0} rejected because it was originally issued at {1}, expired at {2}, and it is now {3}.",
+ token.Token,
+ token.CreatedOn,
+ token.CreatedOn + ttl,
+ DateTime.Now);
+ ErrorUtilities.ThrowProtocol(OAuthStrings.TokenNotFound);
+ }
+ } catch (KeyNotFoundException ex) {
+ throw ErrorUtilities.Wrap(ex, OAuthStrings.TokenNotFound);
+ }
+ }
+ }
+}