diff options
author | unknown <andarno@.redmond.corp.microsoft.com> | 2011-06-15 22:04:26 -0700 |
---|---|---|
committer | unknown <andarno@.redmond.corp.microsoft.com> | 2011-06-15 22:04:26 -0700 |
commit | 4ad66d2d6aaa6c82ed3606e1c7134aeb960b6890 (patch) | |
tree | b7a91568d26488ff7fb0be117775bb7acb5d1b98 | |
parent | c349a02d747f8a02ac0497ac19b21e177415b963 (diff) | |
download | DotNetOpenAuth-4ad66d2d6aaa6c82ed3606e1c7134aeb960b6890.zip DotNetOpenAuth-4ad66d2d6aaa6c82ed3606e1c7134aeb960b6890.tar.gz DotNetOpenAuth-4ad66d2d6aaa6c82ed3606e1c7134aeb960b6890.tar.bz2 |
Implicit grants are now sort of working on the authorization server side.
Still to do:
* Ensure no auto-authorize of access tokens based on previous authorizations for the unauthenticated client.
* Provide the authorization server with a way to indicate access token lifetime, and to veto the request based on the requested scopes being too dangerous for the less secure implicit grant type.
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; } + } } } |