diff options
19 files changed, 258 insertions, 109 deletions
diff --git a/projecttemplates/RelyingPartyLogic/OAuthAuthenticationModule.cs b/projecttemplates/RelyingPartyLogic/OAuthAuthenticationModule.cs index 2c9dc66..130702a 100644 --- a/projecttemplates/RelyingPartyLogic/OAuthAuthenticationModule.cs +++ b/projecttemplates/RelyingPartyLogic/OAuthAuthenticationModule.cs @@ -49,7 +49,7 @@ namespace RelyingPartyLogic { return; } - using (var crypto = OAuthAuthorizationServer.CreateAsymmetricKeyServiceProvider()) { + using (var crypto = OAuthResourceServer.CreateRSA()) { var tokenAnalyzer = new SpecialAccessTokenAnalyzer(crypto, crypto); var resourceServer = new ResourceServer(tokenAnalyzer); diff --git a/projecttemplates/RelyingPartyLogic/OAuthAuthorizationManager.cs b/projecttemplates/RelyingPartyLogic/OAuthAuthorizationManager.cs index c4bcbba..ea16c76 100644 --- a/projecttemplates/RelyingPartyLogic/OAuthAuthorizationManager.cs +++ b/projecttemplates/RelyingPartyLogic/OAuthAuthorizationManager.cs @@ -32,7 +32,7 @@ namespace RelyingPartyLogic { var httpDetails = operationContext.RequestContext.RequestMessage.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; var requestUri = operationContext.RequestContext.RequestMessage.Properties.Via; - using (var crypto = OAuthAuthorizationServer.CreateAsymmetricKeyServiceProvider()) { + using (var crypto = OAuthResourceServer.CreateRSA()) { var tokenAnalyzer = new SpecialAccessTokenAnalyzer(crypto, crypto); var resourceServer = new ResourceServer(tokenAnalyzer); diff --git a/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs b/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs index f0608d5..ee727f5 100644 --- a/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs +++ b/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs @@ -21,7 +21,7 @@ namespace RelyingPartyLogic { /// Provides OAuth 2.0 authorization server information to DotNetOpenAuth. /// </summary> public class OAuthAuthorizationServer : IAuthorizationServer { - private static readonly RSAParameters AsymmetricKey = CreateRSAKey(); + private static readonly RSACryptoServiceProvider SigningKey = new RSACryptoServiceProvider(); private readonly INonceStore nonceStore = new NonceDbStore(); @@ -44,8 +44,26 @@ namespace RelyingPartyLogic { get { return this.nonceStore; } } - public RSACryptoServiceProvider CreateAccessTokenSigningCryptoServiceProvider() { - return CreateAsymmetricKeyServiceProvider(); + /// <summary> + /// Gets the crypto service provider with the asymmetric private key to use for signing access tokens. + /// </summary> + /// <value> + /// Must not be null, and must contain the private key. + /// </value> + /// <returns>A crypto service provider instance that contains the private key.</returns> + public RSACryptoServiceProvider AccessTokenSigningKey { + get { return SigningKey; } + } + + /// <summary> + /// Creates the access token encryption key. + /// </summary> + /// <param name="request">The request.</param> + public RSACryptoServiceProvider CreateAccessTokenEncryptionKey(IAccessTokenRequest request) { + // For this sample, we assume just one resource server. + // If this authorization server needs to mint access tokens for more than one resource server, + // we'd look at the request message passed to us and decide which public key to return. + return OAuthResourceServer.CreateRSA(); } /// <summary> @@ -114,35 +132,7 @@ namespace RelyingPartyLogic { // Default to not auto-approving. return false; } - - /// <summary> - /// Creates the asymmetric crypto service provider. - /// </summary> - /// <returns>An RSA crypto service provider.</returns> - /// <remarks> - /// Since <see cref="RSACryptoServiceProvider"/> are not thread-safe, one must be created for each thread. - /// In this sample we just create one for each incoming request. Be sure to call Dispose on them to release native handles. - /// </remarks> - internal static RSACryptoServiceProvider CreateAsymmetricKeyServiceProvider() { - var serviceProvider = new RSACryptoServiceProvider(); - serviceProvider.ImportParameters(AsymmetricKey); - return serviceProvider; - } - - /// <summary> - /// Creates the RSA key used by all the crypto service provider instances we create. - /// </summary> - /// <returns>RSA data that includes the private key.</returns> - private static RSAParameters CreateRSAKey() { - // As we generate a new random key, we need to set the UseMachineKeyStore flag so that this doesn't - // crash on IIS. For more information: - // http://social.msdn.microsoft.com/Forums/en-US/clr/thread/7ea48fd0-8d6b-43ed-b272-1a0249ae490f?prof=required - var cspParameters = new CspParameters(); - cspParameters.Flags = CspProviderFlags.UseArchivableKey | CspProviderFlags.UseMachineKeyStore; - var asymmetricKey = new RSACryptoServiceProvider(cspParameters); - return asymmetricKey.ExportParameters(true); - } - + private bool IsAuthorizationValid(HashSet<string> requestedScopes, string clientIdentifier, DateTime issuedUtc, string username) { var grantedScopeStrings = from auth in Database.DataContext.ClientAuthorizations where diff --git a/projecttemplates/RelyingPartyLogic/OAuthResourceServer.cs b/projecttemplates/RelyingPartyLogic/OAuthResourceServer.cs new file mode 100644 index 0000000..d935d56 --- /dev/null +++ b/projecttemplates/RelyingPartyLogic/OAuthResourceServer.cs @@ -0,0 +1,31 @@ +namespace RelyingPartyLogic { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Security.Cryptography; + + public static class OAuthResourceServer { + private static readonly RSAParameters ResourceServerKeyPair = CreateRSAKey(); + + internal static RSACryptoServiceProvider CreateRSA() { + var rsa = new RSACryptoServiceProvider(); + rsa.ImportParameters(ResourceServerKeyPair); + return rsa; + } + + /// <summary> + /// Creates the RSA key used by all the crypto service provider instances we create. + /// </summary> + /// <returns>RSA data that includes the private key.</returns> + private static RSAParameters CreateRSAKey() { + // As we generate a new random key, we need to set the UseMachineKeyStore flag so that this doesn't + // crash on IIS. For more information: + // http://social.msdn.microsoft.com/Forums/en-US/clr/thread/7ea48fd0-8d6b-43ed-b272-1a0249ae490f?prof=required + var cspParameters = new CspParameters(); + cspParameters.Flags = CspProviderFlags.UseArchivableKey | CspProviderFlags.UseMachineKeyStore; + var asymmetricKey = new RSACryptoServiceProvider(cspParameters); + return asymmetricKey.ExportParameters(true); + } + } +} diff --git a/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj b/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj index 6ff2740..129b278 100644 --- a/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj +++ b/projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj @@ -123,6 +123,7 @@ <Compile Include="OAuthAuthenticationModule.cs" /> <Compile Include="OAuthAuthorizationManager.cs" /> <Compile Include="OAuthPrincipalAuthorizationPolicy.cs" /> + <Compile Include="OAuthResourceServer.cs" /> <Compile Include="OAuthServiceProvider.cs" /> <Compile Include="Policies.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> @@ -177,4 +178,4 @@ </Target> --> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " /> -</Project> +</Project>
\ No newline at end of file diff --git a/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs b/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs index e2e4325..07cdd9d 100644 --- a/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs +++ b/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs @@ -11,7 +11,25 @@ using DotNetOpenAuth.OAuth2.Messages; internal class OAuth2AuthorizationServer : IAuthorizationServer { - private static readonly RSAParameters AsymmetricTokenSigningPrivateKey = CreateRSAKey(); + private static readonly RSACryptoServiceProvider AsymmetricTokenSigningPrivateKey = CreateRSA(); + +#if SAMPLESONLY + /// <summary> + /// This is the FOR SAMPLE ONLY hard-coded public key of the complementary OAuthResourceServer sample. + /// </summary> + /// <remarks> + /// In a real app, the authorization server would need to determine which resource server the access token needs to be encoded for + /// based on the authorization request. It would then need to look up the public key for that resource server and use that in + /// preparing the access token for the client to use against that resource server. + /// </remarks> + private static readonly RSAParameters ResourceServerEncryptionPublicKey = new RSAParameters { + Exponent = new byte[] { 1, 0, 1 }, + Modulus = new byte[] { 166, 175, 117, 169, 211, 251, 45, 215, 55, 53, 202, 65, 153, 155, 92, 219, 235, 243, 61, 170, 101, 250, 221, 214, 239, 175, 238, 175, 239, 20, 144, 72, 227, 221, 4, 219, 32, 225, 101, 96, 18, 33, 117, 176, 110, 123, 109, 23, 29, 85, 93, 50, 129, 163, 113, 57, 122, 212, 141, 145, 17, 31, 67, 165, 181, 91, 117, 23, 138, 251, 198, 132, 188, 213, 10, 157, 116, 229, 48, 168, 8, 127, 28, 156, 239, 124, 117, 36, 232, 100, 222, 23, 52, 186, 239, 5, 63, 207, 185, 16, 137, 73, 137, 147, 252, 71, 9, 239, 113, 27, 88, 255, 91, 56, 192, 142, 210, 21, 34, 81, 204, 239, 57, 60, 140, 249, 15, 101 }, + }; +#else + [Obsolete("You must use a real key for a real app.", true)] + private static readonly RSAParameters ResourceServerEncryptionPublicKey; +#endif #region Implementation of IAuthorizationServer @@ -23,9 +41,17 @@ get { return MvcApplication.KeyNonceStore; } } - public RSACryptoServiceProvider CreateAccessTokenSigningCryptoServiceProvider() { + public RSACryptoServiceProvider AccessTokenSigningKey { + get { return AsymmetricTokenSigningPrivateKey; } + } + + public RSACryptoServiceProvider CreateAccessTokenEncryptionKey(IAccessTokenRequest accessTokenRequestMessage) { var asymmetricTokenSigningServiceProvider = new RSACryptoServiceProvider(); - asymmetricTokenSigningServiceProvider.ImportParameters(AsymmetricTokenSigningPrivateKey); + + // For this sample, we assume just one resource server. + // If this authorization server needs to mint access tokens for more than one resource server, + // we'd look at the request message passed to us and decide which public key to return. + asymmetricTokenSigningServiceProvider.ImportParameters(ResourceServerEncryptionPublicKey); return asymmetricTokenSigningServiceProvider; } @@ -107,18 +133,24 @@ #endif } + private static RSACryptoServiceProvider CreateRSA() { + var rsa = new RSACryptoServiceProvider(); + rsa.ImportParameters(CreateRSAKey()); + return rsa; + } + private bool IsAuthorizationValid(HashSet<string> requestedScopes, string clientIdentifier, DateTime issuedUtc, string username) { // If db precision exceeds token time precision (which is common), the following query would // often disregard a token that is minted immediately after the authorization record is stored in the db. // To compensate for this, we'll increase the timestamp on the token's issue date by 1 second. issuedUtc += TimeSpan.FromSeconds(1); var grantedScopeStrings = from auth in MvcApplication.DataContext.ClientAuthorizations - where - auth.Client.ClientIdentifier == clientIdentifier && - auth.CreatedOnUtc <= issuedUtc && - (!auth.ExpirationDateUtc.HasValue || auth.ExpirationDateUtc.Value >= DateTime.UtcNow) && - auth.User.OpenIDClaimedIdentifier == username - select auth.Scope; + where + auth.Client.ClientIdentifier == clientIdentifier && + auth.CreatedOnUtc <= issuedUtc && + (!auth.ExpirationDateUtc.HasValue || auth.ExpirationDateUtc.Value >= DateTime.UtcNow) && + auth.User.OpenIDClaimedIdentifier == username + select auth.Scope; if (!grantedScopeStrings.Any()) { // No granted authorizations prior to the issuance of this token, so it must have been revoked. diff --git a/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs b/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs index fb836a6..07dc8cc 100644 --- a/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs +++ b/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs @@ -16,24 +16,6 @@ public class OAuthController : Controller {
private readonly AuthorizationServer authorizationServer = new AuthorizationServer(new OAuth2AuthorizationServer());
-#if SAMPLESONLY
- /// <summary>
- /// This is the FOR SAMPLE ONLY hard-coded public key of the complementary OAuthResourceServer sample.
- /// </summary>
- /// <remarks>
- /// In a real app, the authorization server would need to determine which resource server the access token needs to be encoded for
- /// based on the authorization request. It would then need to look up the public key for that resource server and use that in
- /// preparing the access token for the client to use against that resource server.
- /// </remarks>
- private static readonly RSAParameters ResourceServerEncryptionPublicKey = new RSAParameters {
- Exponent = new byte[] { 1, 0, 1 },
- Modulus = new byte[] { 166, 175, 117, 169, 211, 251, 45, 215, 55, 53, 202, 65, 153, 155, 92, 219, 235, 243, 61, 170, 101, 250, 221, 214, 239, 175, 238, 175, 239, 20, 144, 72, 227, 221, 4, 219, 32, 225, 101, 96, 18, 33, 117, 176, 110, 123, 109, 23, 29, 85, 93, 50, 129, 163, 113, 57, 122, 212, 141, 145, 17, 31, 67, 165, 181, 91, 117, 23, 138, 251, 198, 132, 188, 213, 10, 157, 116, 229, 48, 168, 8, 127, 28, 156, 239, 124, 117, 36, 232, 100, 222, 23, 52, 186, 239, 5, 63, 207, 185, 16, 137, 73, 137, 147, 252, 71, 9, 239, 113, 27, 88, 255, 91, 56, 192, 142, 210, 21, 34, 81, 204, 239, 57, 60, 140, 249, 15, 101 },
- };
-#else
- [Obsolete("You must use a real key for a real app.", true)]
- private static readonly RSAParameters ResourceServerEncryptionPublicKey;
-#endif
-
/// <summary>
/// The OAuth 2.0 token endpoint.
/// </summary>
@@ -52,10 +34,8 @@ // TODO: code here
// Prepare the refresh and access tokens.
- using (var crypto = CreateResourceServerEncryptionServiceProvider()) {
- var response = this.authorizationServer.PrepareAccessTokenResponse(request, crypto, accessTokenLifetime);
- return this.authorizationServer.Channel.PrepareResponse(response).AsActionResult();
- }
+ var response = this.authorizationServer.PrepareAccessTokenResponse(request, accessTokenLifetime);
+ return this.authorizationServer.Channel.PrepareResponse(response).AsActionResult();
}
throw new HttpException((int)HttpStatusCode.BadRequest, "Missing OAuth 2.0 request message.");
@@ -124,15 +104,5 @@ return this.authorizationServer.Channel.PrepareResponse(response).AsActionResult();
}
-
- /// <summary>
- /// Creates the resource server's encryption service provider with private key.
- /// </summary>
- /// <returns>An RSA crypto service provider.</returns>
- internal static RSACryptoServiceProvider CreateResourceServerEncryptionServiceProvider() {
- var resourceServerEncryptionServiceProvider = new RSACryptoServiceProvider();
- resourceServerEncryptionServiceProvider.ImportParameters(ResourceServerEncryptionPublicKey);
- return resourceServerEncryptionServiceProvider;
- }
}
}
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 60f2b8e..d9573f9 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -396,6 +396,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OAuth2\Messages\GrantType.cs" /> <Compile Include="OAuth2\Messages\AccessTokenRefreshRequest.cs" /> <Compile Include="OAuth2\Messages\EndUserAuthorizationResponseType.cs" /> + <Compile Include="OAuth2\Messages\IAccessTokenRequest.cs" /> <Compile Include="OAuth2\Messages\IMessageWithClientState.cs" /> <Compile Include="OAuth2\Messages\ScopedAccessTokenRequest.cs" /> <Compile Include="OAuth2\Messages\UnauthorizedResponse.cs" /> diff --git a/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs b/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs index e95835c..0f57939 100644 --- a/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs @@ -122,11 +122,7 @@ namespace DotNetOpenAuth.OAuth2 { var request = this.ReadAccessTokenRequest(httpRequestInfo); if (request != null) { - // This convenience method only encrypts access tokens assuming that this auth server - // doubles as the resource server. - using (var resourceServerPublicKey = this.AuthorizationServerServices.CreateAccessTokenSigningCryptoServiceProvider()) { - response = this.PrepareAccessTokenResponse(request, resourceServerPublicKey); - } + response = this.PrepareAccessTokenResponse(request); return true; } @@ -212,17 +208,15 @@ namespace DotNetOpenAuth.OAuth2 { /// Prepares the response to an access token request. /// </summary> /// <param name="request">The request for an access token.</param> - /// <param name="accessTokenEncryptingPublicKey">The crypto service provider with the public key to encrypt the access token to, such that the resource server will be able to decrypt it.</param> /// <param name="accessTokenLifetime">The access token's lifetime.</param> /// <param name="includeRefreshToken">If set to <c>true</c>, the response will include a long-lived refresh token.</param> /// <returns>The response message to send to the client.</returns> - public virtual IDirectResponseProtocolMessage PrepareAccessTokenResponse(AccessTokenRequestBase request, RSACryptoServiceProvider accessTokenEncryptingPublicKey, TimeSpan? accessTokenLifetime = null, bool includeRefreshToken = true) { + public virtual IDirectResponseProtocolMessage PrepareAccessTokenResponse(AccessTokenRequestBase request, TimeSpan? accessTokenLifetime = null, bool includeRefreshToken = true) { Contract.Requires<ArgumentNullException>(request != null); - Contract.Requires<ArgumentNullException>(accessTokenEncryptingPublicKey != null); var tokenRequest = (IAuthorizationCarryingRequest)request; - using (var crypto = this.AuthorizationServerServices.CreateAccessTokenSigningCryptoServiceProvider()) { - var accessTokenFormatter = AccessToken.CreateFormatter(crypto, accessTokenEncryptingPublicKey); + using (var resourceServerEncryptionKey = this.AuthorizationServerServices.CreateAccessTokenEncryptionKey(request)) { + var accessTokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServerServices.AccessTokenSigningKey, resourceServerEncryptionKey); var accessToken = new AccessToken(tokenRequest.AuthorizationDescription, accessTokenLifetime); var response = new AccessTokenSuccessResponse(request) { diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessRequestBindingElement.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessRequestBindingElement.cs index 0210bd1..9c9ebb4 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessRequestBindingElement.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessRequestBindingElement.cs @@ -61,6 +61,24 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { var code = (AuthorizationCode)response.AuthorizationDescription; response.CodeOrToken = codeFormatter.Serialize(code); break; + case CodeOrTokenType.AccessToken: + var responseWithOriginatingRequest = (IDirectResponseProtocolMessage)message; + var request = (IAccessTokenRequest)responseWithOriginatingRequest.OriginatingRequest; + + // TODO: consider moving this AccessToken construction to its own binding element. + response.AuthorizationDescription = new AccessToken { + ClientIdentifier = request.ClientIdentifier, + UtcCreationDate = DateTime.UtcNow, + User = ((EndUserAuthorizationSuccessResponseBase)response).AuthorizingUsername, + }; + response.AuthorizationDescription.Scope.ResetContents(request.Scope); + + using (var resourceServerKey = this.AuthorizationServer.CreateAccessTokenEncryptionKey(request)) { + var tokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServer.AccessTokenSigningKey, resourceServerKey); + var token = (AccessToken)response.AuthorizationDescription; + response.CodeOrToken = tokenFormatter.Serialize(token); + break; + } default: throw ErrorUtilities.ThrowInternal(string.Format(CultureInfo.CurrentCulture, "Unexpected outgoing code or token type: {0}", response.CodeOrTokenType)); } diff --git a/src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs b/src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs index 67fad8b..6458433 100644 --- a/src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs @@ -13,6 +13,8 @@ namespace DotNetOpenAuth.OAuth2 { using System.Text; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OAuth2.ChannelElements; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth2.Messages; /// <summary> /// Provides host-specific authorization server services needed by this library. @@ -36,16 +38,28 @@ namespace DotNetOpenAuth.OAuth2 { INonceStore VerificationCodeNonceStore { get; } /// <summary> - /// Creates a new instance of the crypto service provider with the asymmetric private key to use for signing access tokens. + /// Gets the crypto service provider with the asymmetric private key to use for signing access tokens. /// </summary> /// <returns>A crypto service provider instance that contains the private key.</returns> /// <value>Must not be null, and must contain the private key.</value> /// <remarks> /// The public key in the private/public key pair will be used by the resource /// servers to validate that the access token is minted by a trusted authorization server. - /// The caller is responsible to dispose of the returned instance. /// </remarks> - RSACryptoServiceProvider CreateAccessTokenSigningCryptoServiceProvider(); + RSACryptoServiceProvider AccessTokenSigningKey { get; } + + /// <summary> + /// Gets the crypto service provider with the asymmetric public key to use for encrypting access tokens for a specific resource server. + /// </summary> + /// <param name="accessTokenRequestMessage">The access token request message.</param> + /// <returns> + /// A crypto service provider instance that contains the public key. + /// </returns> + /// <value>Must not be null.</value> + /// <remarks> + /// The caller is responsible to dispose of the returned value. + /// </remarks> + RSACryptoServiceProvider CreateAccessTokenEncryptionKey(IAccessTokenRequest accessTokenRequestMessage); /// <summary> /// Gets the client with a given identifier. @@ -115,6 +129,21 @@ namespace DotNetOpenAuth.OAuth2 { /// <summary> /// Gets the crypto service provider with the asymmetric private key to use for signing access tokens. /// </summary> + /// <value> + /// Must not be null, and must contain the private key. + /// </value> + /// <returns>A crypto service provider instance that contains the private key.</returns> + RSACryptoServiceProvider IAuthorizationServer.AccessTokenSigningKey { + get { + Contract.Ensures(Contract.Result<RSACryptoServiceProvider>() != null); + Contract.Ensures(!Contract.Result<RSACryptoServiceProvider>().PublicOnly); + throw new NotImplementedException(); + } + } + + /// <summary> + /// Gets the crypto service provider with the asymmetric private key to use for signing access tokens. + /// </summary> /// <returns> /// A crypto service provider instance that contains the private key. /// </returns> @@ -123,9 +152,8 @@ namespace DotNetOpenAuth.OAuth2 { /// The public key in the private/public key pair will be used by the resource /// servers to validate that the access token is minted by a trusted authorization server. /// </remarks> - RSACryptoServiceProvider IAuthorizationServer.CreateAccessTokenSigningCryptoServiceProvider() { + RSACryptoServiceProvider IAuthorizationServer.CreateAccessTokenEncryptionKey(IAccessTokenRequest accessTokenRequestMessage) { Contract.Ensures(Contract.Result<RSACryptoServiceProvider>() != null); - Contract.Ensures(!Contract.Result<RSACryptoServiceProvider>().PublicOnly); throw new NotImplementedException(); } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs index b45b7ad..7ada347 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs @@ -15,7 +15,8 @@ namespace DotNetOpenAuth.OAuth2.Messages { using DotNetOpenAuth.OAuth2.ChannelElements; /// <summary> - /// A request from a Client to an Authorization Server to exchange an authorization code for an access token. + /// A request from a Client to an Authorization Server to exchange an authorization code for an access token, + /// and (at the authorization server's option) a refresh token. /// </summary> internal class AccessTokenAuthorizationCodeRequest : AccessTokenRequestBase, IAuthorizationCarryingRequest { /// <summary> @@ -32,8 +33,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// </summary> /// <param name="authorizationServer">The authorization server.</param> internal AccessTokenAuthorizationCodeRequest(AuthorizationServerDescription authorizationServer) - : this(authorizationServer.TokenEndpoint, authorizationServer.Version) - { + : this(authorizationServer.TokenEndpoint, authorizationServer.Version) { Contract.Requires<ArgumentNullException>(authorizationServer != null); } @@ -60,6 +60,13 @@ namespace DotNetOpenAuth.OAuth2.Messages { IAuthorizationDescription IAuthorizationCarryingRequest.AuthorizationDescription { get; set; } /// <summary> + /// Gets the scope of operations the client is allowed to invoke. + /// </summary> + protected override HashSet<string> RequestedScope { + get { return ((IAuthorizationCarryingRequest)this).AuthorizationDescription.Scope; } + } + + /// <summary> /// Gets the type of the grant. /// </summary> /// <value>The type of the grant.</value> diff --git a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRequestBase.cs b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRequestBase.cs index a71dc70..7609862 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRequestBase.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRequestBase.cs @@ -16,7 +16,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// <summary> /// A message sent from the client to the authorization server to exchange a previously obtained grant for an access token. /// </summary> - public abstract class AccessTokenRequestBase : AuthenticatedClientRequestBase { + public abstract class AccessTokenRequestBase : AuthenticatedClientRequestBase, IAccessTokenRequest { /// <summary> /// Initializes a new instance of the <see cref="AccessTokenRequestBase"/> class. /// </summary> @@ -52,5 +52,27 @@ namespace DotNetOpenAuth.OAuth2.Messages { DotNetOpenAuthSection.Configuration.Messaging.RelaxSslRequirements || this.Recipient.IsTransportSecure(), OAuthStrings.HttpsRequired); } + + /// <summary> + /// Gets a value indicating whether the client requesting the access token has authenticated itself. + /// </summary> + /// <value> + /// Always true, because of our base class. + /// </value> + bool IAccessTokenRequest.ClientAuthenticated { + get { return true; } + } + + /// <summary> + /// Gets the scope of operations the client is allowed to invoke. + /// </summary> + HashSet<string> IAccessTokenRequest.Scope { + get { return this.RequestedScope; } + } + + /// <summary> + /// Gets the scope of operations the client is allowed to invoke. + /// </summary> + protected abstract HashSet<string> RequestedScope { get; } } } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationRequest.cs b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationRequest.cs index bee1cae..856fe22 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationRequest.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationRequest.cs @@ -19,7 +19,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// to issue an access token to the Consumer if permission is granted. /// </summary> [Serializable] - public class EndUserAuthorizationRequest : MessageBase { + public class EndUserAuthorizationRequest : MessageBase, IAccessTokenRequest { /// <summary> /// Initializes a new instance of the <see cref="EndUserAuthorizationRequest"/> class. /// </summary> @@ -59,6 +59,16 @@ namespace DotNetOpenAuth.OAuth2.Messages { public string ClientIdentifier { get; set; } /// <summary> + /// Gets a value indicating whether the client requesting the access token has authenticated itself. + /// </summary> + /// <value> + /// Always false because authorization requests only include the client_id, without a secret. + /// </value> + bool IAccessTokenRequest.ClientAuthenticated { + get { return false; } + } + + /// <summary> /// Gets or sets the callback URL. /// </summary> /// <value> diff --git a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs index f5edfa8..4a3c534 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs @@ -29,6 +29,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { : base(clientCallback, version) { Contract.Requires<ArgumentNullException>(version != null); Contract.Requires<ArgumentNullException>(clientCallback != null); + this.TokenType = Protocol.AccessTokenTypes.Bearer; } /// <summary> @@ -41,6 +42,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { Contract.Requires<ArgumentNullException>(clientCallback != null); Contract.Requires<ArgumentNullException>(request != null); ((IMessageWithClientState)this).ClientState = request.ClientState; + this.TokenType = Protocol.AccessTokenTypes.Bearer; } #region ITokenCarryingRequest Members diff --git a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs index af7f913..37d4cc2 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs @@ -72,12 +72,5 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// <value>The authorization code.</value> [MessagePart(Protocol.code, IsRequired = true)] internal string AuthorizationCode { get; set; } - - /// <summary> - /// Gets or sets the access token. - /// </summary> - /// <value>The access token.</value> - [MessagePart(Protocol.access_token, IsRequired = false)] - internal string AccessToken { get; set; } } } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/IAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuth2/Messages/IAccessTokenRequest.cs new file mode 100644 index 0000000..59670a1 --- /dev/null +++ b/src/DotNetOpenAuth/OAuth2/Messages/IAccessTokenRequest.cs @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------- +// <copyright file="IAccessTokenRequest.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.Messages { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuth2.ChannelElements; + using DotNetOpenAuth.Messaging; + + public interface IAccessTokenRequest : IMessage { + /// <summary> + /// Gets a value indicating whether the client requesting the access token has authenticated itself. + /// </summary> + /// <value> + /// <c>false</c> for implicit grant requests; otherwise, <c>true</c>. + /// </value> + bool ClientAuthenticated { get; } + + /// <summary> + /// Gets the identifier of the client authorized to access protected data. + /// </summary> + string ClientIdentifier { get; } + + /// <summary> + /// Gets the scope of operations the client is allowed to invoke. + /// </summary> + HashSet<string> Scope { get; } + } +} diff --git a/src/DotNetOpenAuth/OAuth2/Messages/OAuth 2 Messages.cd b/src/DotNetOpenAuth/OAuth2/Messages/OAuth 2 Messages.cd index 9ddd0cc..05e3ad9 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/OAuth 2 Messages.cd +++ b/src/DotNetOpenAuth/OAuth2/Messages/OAuth 2 Messages.cd @@ -9,9 +9,9 @@ <Lollipop Position="0.2" /> </Class> <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessTokenAuthorizationCodeRequest" Collapsed="true"> - <Position X="6.75" Y="6.75" Width="3" /> + <Position X="8.5" Y="6.75" Width="3" /> <TypeIdentifier> - <HashCode>ACAAEAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAgAAAARAA=</HashCode> + <HashCode>ACAAEAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAgAAAATAA=</HashCode> <FileName>OAuth2\Messages\AccessTokenAuthorizationCodeRequest.cs</FileName> </TypeIdentifier> <Lollipop Position="0.2" /> @@ -42,9 +42,10 @@ <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessTokenRequestBase" Collapsed="true"> <Position X="5.75" Y="5.75" Width="2" /> <TypeIdentifier> - <HashCode>AAAAAAAAQAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAA=</HashCode> + <HashCode>AAAAAAAAQABAAAAAAAAAAAAQAAAAAAAAAAAAAAAACAA=</HashCode> <FileName>OAuth2\Messages\AccessTokenRequestBase.cs</FileName> </TypeIdentifier> + <Lollipop Position="0.2" /> </Class> <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessTokenResourceOwnerPasswordCredentialsRequest" Collapsed="true"> <Position X="8.5" Y="10.5" Width="4" /> @@ -79,9 +80,10 @@ <Class Name="DotNetOpenAuth.OAuth2.Messages.EndUserAuthorizationRequest" Collapsed="true"> <Position X="3.25" Y="0.5" Width="2.25" /> <TypeIdentifier> - <HashCode>AAAAAAAAQAAAACAAAAAAAACAAAQAAAQAAAAAAAAAQAA=</HashCode> + <HashCode>AAAAAAAAQABAACAAAAAAAACAAAQAAAQAAAAAAAAAQAA=</HashCode> <FileName>OAuth2\Messages\EndUserAuthorizationRequest.cs</FileName> </TypeIdentifier> + <Lollipop Position="0.2" /> </Class> <Class Name="DotNetOpenAuth.OAuth2.Messages.EndUserAuthorizationSuccessAccessTokenResponse" Collapsed="true"> <Position X="6.25" Y="3.75" Width="3.75" /> @@ -110,7 +112,7 @@ <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessProtectedResourceRequest" Collapsed="true"> <Position X="3.25" Y="9.75" Width="2.5" /> <TypeIdentifier> - <HashCode>AAAAEAAAQAAAAAAAAAACAAAAEAAAAAAAAAEgAAQADAE=</HashCode> + <HashCode>AAAAEAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAgAAAABgA=</HashCode> <FileName>OAuth2\Messages\AccessProtectedResourceRequest.cs</FileName> </TypeIdentifier> <Lollipop Position="0.2" /> @@ -126,7 +128,7 @@ <Class Name="DotNetOpenAuth.OAuth2.Messages.ScopedAccessTokenRequest" Collapsed="true"> <Position X="6.75" Y="7.75" Width="2.25" /> <TypeIdentifier> - <HashCode>AAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode> + <HashCode>AAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAACAA=</HashCode> <FileName>OAuth2\Messages\ScopedAccessTokenRequest.cs</FileName> </TypeIdentifier> </Class> @@ -137,6 +139,13 @@ <FileName>OAuth2\Messages\IMessageWithClientState.cs</FileName> </TypeIdentifier> </Interface> + <Interface Name="DotNetOpenAuth.OAuth2.ChannelElements.IAuthorizationCarryingRequest"> + <Position X="11.75" Y="2" Width="2.5" /> + <TypeIdentifier> + <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAEAAAAA=</HashCode> + <FileName>OAuth2\ChannelElements\IAuthorizationCarryingRequest.cs</FileName> + </TypeIdentifier> + </Interface> <Enum Name="DotNetOpenAuth.OAuth2.Messages.EndUserAuthorizationResponseType"> <Position X="8" Y="0.5" Width="3" /> <TypeIdentifier> diff --git a/src/DotNetOpenAuth/OAuth2/Messages/ScopedAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuth2/Messages/ScopedAccessTokenRequest.cs index 0e0329b..5568b1c 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/ScopedAccessTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/ScopedAccessTokenRequest.cs @@ -30,5 +30,12 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// <value>A set of scopes. Never null.</value> [MessagePart(Protocol.scope, IsRequired = false, Encoder = typeof(ScopeEncoder))] internal HashSet<string> Scope { get; private set; } + + /// <summary> + /// Gets the scope of operations the client is allowed to invoke. + /// </summary> + protected override HashSet<string> RequestedScope { + get { return this.Scope; } + } } } |