summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorunknown <andarno@.redmond.corp.microsoft.com>2011-06-15 22:04:26 -0700
committerunknown <andarno@.redmond.corp.microsoft.com>2011-06-15 22:04:26 -0700
commit4ad66d2d6aaa6c82ed3606e1c7134aeb960b6890 (patch)
treeb7a91568d26488ff7fb0be117775bb7acb5d1b98
parentc349a02d747f8a02ac0497ac19b21e177415b963 (diff)
downloadDotNetOpenAuth-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.
-rw-r--r--projecttemplates/RelyingPartyLogic/OAuthAuthenticationModule.cs2
-rw-r--r--projecttemplates/RelyingPartyLogic/OAuthAuthorizationManager.cs2
-rw-r--r--projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs54
-rw-r--r--projecttemplates/RelyingPartyLogic/OAuthResourceServer.cs31
-rw-r--r--projecttemplates/RelyingPartyLogic/RelyingPartyLogic.csproj3
-rw-r--r--samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs50
-rw-r--r--samples/OAuthAuthorizationServer/Controllers/OAuthController.cs34
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj1
-rw-r--r--src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs14
-rw-r--r--src/DotNetOpenAuth/OAuth2/ChannelElements/AccessRequestBindingElement.cs18
-rw-r--r--src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs38
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs13
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRequestBase.cs24
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationRequest.cs12
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs7
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/IAccessTokenRequest.cs34
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/OAuth 2 Messages.cd21
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/ScopedAccessTokenRequest.cs7
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; }
+ }
}
}