summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs9
-rw-r--r--samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs19
-rw-r--r--samples/OAuthAuthorizationServer/Controllers/OAuthController.cs12
-rw-r--r--src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs11
-rw-r--r--src/DotNetOpenAuth/OAuth2/ChannelElements/AccessRequestBindingElement.cs24
-rw-r--r--src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs55
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs2
8 files changed, 85 insertions, 49 deletions
diff --git a/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs b/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs
index ee727f5..8c3f6fd 100644
--- a/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs
+++ b/projecttemplates/RelyingPartyLogic/OAuthAuthorizationServer.cs
@@ -59,11 +59,12 @@ namespace RelyingPartyLogic {
/// Creates the access token encryption key.
/// </summary>
/// <param name="request">The request.</param>
- public RSACryptoServiceProvider CreateAccessTokenEncryptionKey(IAccessTokenRequest request) {
+ public void PrepareAccessToken(IAccessTokenRequest accessTokenRequestMessage, out RSACryptoServiceProvider resourceServerEncryptionKey, out TimeSpan lifetime) {
// 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();
+ resourceServerEncryptionKey = OAuthResourceServer.CreateRSA();
+ lifetime = TimeSpan.FromHours(1);
}
/// <summary>
@@ -132,7 +133,7 @@ namespace RelyingPartyLogic {
// Default to not auto-approving.
return false;
}
-
+
private bool IsAuthorizationValid(HashSet<string> requestedScopes, string clientIdentifier, DateTime issuedUtc, string username) {
var grantedScopeStrings = from auth in Database.DataContext.ClientAuthorizations
where
@@ -140,7 +141,7 @@ namespace RelyingPartyLogic {
auth.CreatedOnUtc <= issuedUtc &&
(!auth.ExpirationDateUtc.HasValue || auth.ExpirationDateUtc.Value >= DateTime.UtcNow) &&
auth.User.AuthenticationTokens.Any(token => token.ClaimedIdentifier == username)
- select auth.Scope;
+ 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/Code/OAuth2AuthorizationServer.cs b/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs
index 07cdd9d..9e490b0 100644
--- a/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs
+++ b/samples/OAuthAuthorizationServer/Code/OAuth2AuthorizationServer.cs
@@ -45,14 +45,23 @@
get { return AsymmetricTokenSigningPrivateKey; }
}
- public RSACryptoServiceProvider CreateAccessTokenEncryptionKey(IAccessTokenRequest accessTokenRequestMessage) {
- var asymmetricTokenSigningServiceProvider = new RSACryptoServiceProvider();
+ public void PrepareAccessToken(IAccessTokenRequest accessTokenRequestMessage, out RSACryptoServiceProvider resourceServerEncryptionKey, out TimeSpan lifetime) {
+ resourceServerEncryptionKey = new RSACryptoServiceProvider();
// 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;
+ resourceServerEncryptionKey.ImportParameters(ResourceServerEncryptionPublicKey);
+
+ // Just for the sake of the sample, we use a short-lived token. This can be useful to mitigate the security risks
+ // of access tokens that are used over standard HTTP.
+ // But this is just the lifetime of the access token. The client can still renew it using their refresh token until
+ // the authorization itself expires.
+ lifetime = TimeSpan.FromMinutes(2);
+
+ // Also take into account the remaining life of the authorization and artificially shorten the access token's lifetime
+ // to account for that if necessary.
+ // TODO: code here
}
public IConsumerDescription GetClient(string clientIdentifier) {
@@ -77,7 +86,7 @@
}
// NEVER issue an auto-approval to a client that would end up getting an access token immediately
- // (without a client secret), as that would allow ANY client to spoof an approved client's identity
+ // (without a client secret), as that would allow arbitrary clients to masquarade as an approved client
// and obtain unauthorized access to user data.
if (authorizationRequest.ResponseType == EndUserAuthorizationResponseType.AuthorizationCode) {
// Never issue auto-approval if the client secret is blank, since that too makes it easy to spoof
diff --git a/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs b/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs
index 07dc8cc..a67c57b 100644
--- a/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs
+++ b/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs
@@ -23,18 +23,8 @@
public ActionResult Token() {
var request = this.authorizationServer.ReadAccessTokenRequest();
if (request != null) {
- // Just for the sake of the sample, we use a short-lived token. This can be useful to mitigate the security risks
- // of access tokens that are used over standard HTTP.
- // But this is just the lifetime of the access token. The client can still renew it using their refresh token until
- // the authorization itself expires.
- TimeSpan accessTokenLifetime = TimeSpan.FromMinutes(2);
-
- // Also take into account the remaining life of the authorization and artificially shorten the access token's lifetime
- // to account for that if necessary.
- // TODO: code here
-
// Prepare the refresh and access tokens.
- var response = this.authorizationServer.PrepareAccessTokenResponse(request, accessTokenLifetime);
+ var response = this.authorizationServer.PrepareAccessTokenResponse(request);
return this.authorizationServer.Channel.PrepareResponse(response).AsActionResult();
}
diff --git a/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs b/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs
index 0f57939..69768bc 100644
--- a/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs
+++ b/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs
@@ -208,14 +208,16 @@ namespace DotNetOpenAuth.OAuth2 {
/// Prepares the response to an access token request.
/// </summary>
/// <param name="request">The request for an access token.</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, TimeSpan? accessTokenLifetime = null, bool includeRefreshToken = true) {
+ public virtual IDirectResponseProtocolMessage PrepareAccessTokenResponse(AccessTokenRequestBase request, bool includeRefreshToken = true) {
Contract.Requires<ArgumentNullException>(request != null);
var tokenRequest = (IAuthorizationCarryingRequest)request;
- using (var resourceServerEncryptionKey = this.AuthorizationServerServices.CreateAccessTokenEncryptionKey(request)) {
+ RSACryptoServiceProvider resourceServerEncryptionKey;
+ TimeSpan accessTokenLifetime;
+ this.AuthorizationServerServices.PrepareAccessToken(request, out resourceServerEncryptionKey, out accessTokenLifetime);
+ try {
var accessTokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServerServices.AccessTokenSigningKey, resourceServerEncryptionKey);
var accessToken = new AccessToken(tokenRequest.AuthorizationDescription, accessTokenLifetime);
@@ -232,6 +234,9 @@ namespace DotNetOpenAuth.OAuth2 {
}
return response;
+ } finally {
+ IDisposable disposableKey = resourceServerEncryptionKey;
+ disposableKey.Dispose();
}
}
diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessRequestBindingElement.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessRequestBindingElement.cs
index 9c9ebb4..129a277 100644
--- a/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessRequestBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessRequestBindingElement.cs
@@ -13,6 +13,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OAuth2.Messages;
+ using System.Security.Cryptography;
/// <summary>
/// Decodes verification codes, refresh tokens and access tokens on incoming messages.
@@ -66,18 +67,25 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
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)) {
+ RSACryptoServiceProvider resourceServerKey;
+ TimeSpan lifetime;
+ this.AuthorizationServer.PrepareAccessToken(request, out resourceServerKey, out lifetime);
+ try {
+ response.AuthorizationDescription = new AccessToken {
+ ClientIdentifier = request.ClientIdentifier,
+ UtcCreationDate = DateTime.UtcNow,
+ User = ((EndUserAuthorizationSuccessResponseBase)response).AuthorizingUsername,
+ Lifetime = lifetime,
+ };
+ response.AuthorizationDescription.Scope.ResetContents(request.Scope);
+
var tokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServer.AccessTokenSigningKey, resourceServerKey);
var token = (AccessToken)response.AuthorizationDescription;
response.CodeOrToken = tokenFormatter.Serialize(token);
break;
+ } finally {
+ IDisposable disposableKey = resourceServerKey;
+ disposableKey.Dispose();
}
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 6458433..457ce65 100644
--- a/src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs
+++ b/src/DotNetOpenAuth/OAuth2/IAuthorizationServer.cs
@@ -49,17 +49,28 @@ namespace DotNetOpenAuth.OAuth2 {
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.
+ /// Obtains the encryption key and lifetime for an access token being created.
/// </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>
+ /// <param name="accessTokenRequestMessage">
+ /// Details regarding the resources that the access token will grant access to, and the identity of the client
+ /// that will receive that access.
+ /// Based on this information the receiving resource server can be determined and the lifetime of the access
+ /// token can be set based on the sensitivity of the resources.
+ /// </param>
+ /// <param name="resourceServerEncryptionKey">
+ /// Receives the crypto service provider with the asymmetric public key to use for encrypting access tokens for a specific resource server.
+ /// The caller is responsible to dispose of this value.
+ /// </param>
+ /// <param name="lifetime">
+ /// Receives the lifetime for this access token. Note that within this lifetime, authorization <i>may</i> not be revokable.
+ /// Short lifetimes are recommended (i.e. one hour), particularly when the client is not authenticated or
+ /// the resources to which access is being granted are sensitive.
+ /// If <c>null</c>, a preconfigured default lifetime will be used.
+ /// </param>
/// <remarks>
/// The caller is responsible to dispose of the returned value.
/// </remarks>
- RSACryptoServiceProvider CreateAccessTokenEncryptionKey(IAccessTokenRequest accessTokenRequestMessage);
+ void PrepareAccessToken(IAccessTokenRequest accessTokenRequestMessage, out RSACryptoServiceProvider resourceServerEncryptionKey, out TimeSpan lifetime);
/// <summary>
/// Gets the client with a given identifier.
@@ -142,18 +153,30 @@ namespace DotNetOpenAuth.OAuth2 {
}
/// <summary>
- /// Gets the crypto service provider with the asymmetric private key to use for signing access tokens.
+ /// Obtains the encryption key and lifetime for an access token being created.
/// </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>
+ /// <param name="accessTokenRequestMessage">
+ /// Details regarding the resources that the access token will grant access to, and the identity of the client
+ /// that will receive that access.
+ /// Based on this information the receiving resource server can be determined and the lifetime of the access
+ /// token can be set based on the sensitivity of the resources.
+ /// </param>
+ /// <param name="resourceServerEncryptionKey">
+ /// Receives the crypto service provider with the asymmetric public key to use for encrypting access tokens for a specific resource server.
+ /// The caller is responsible to dispose of this value.
+ /// </param>
+ /// <param name="lifetime">
+ /// Receives the lifetime for this access token. Note that within this lifetime, authorization <i>may</i> not be revokable.
+ /// Short lifetimes are recommended (i.e. one hour), particularly when the client is not authenticated or
+ /// the resources to which access is being granted are sensitive.
+ /// If <c>null</c>, a preconfigured default lifetime will be used.
+ /// </param>
/// <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 value.
/// </remarks>
- RSACryptoServiceProvider IAuthorizationServer.CreateAccessTokenEncryptionKey(IAccessTokenRequest accessTokenRequestMessage) {
- Contract.Ensures(Contract.Result<RSACryptoServiceProvider>() != null);
+ void IAuthorizationServer.PrepareAccessToken(IAccessTokenRequest accessTokenRequestMessage, out RSACryptoServiceProvider resourceServerEncryptionKey, out TimeSpan lifetime) {
+ Contract.Requires<ArgumentNullException>(accessTokenRequestMessage != null);
+ Contract.Ensures(Contract.ValueAtReturn<RSACryptoServiceProvider>(out resourceServerEncryptionKey) != null);
throw new NotImplementedException();
}
diff --git a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs
index 4a3c534..a752a04 100644
--- a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs
+++ b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs
@@ -45,7 +45,7 @@ namespace DotNetOpenAuth.OAuth2.Messages {
this.TokenType = Protocol.AccessTokenTypes.Bearer;
}
- #region ITokenCarryingRequest Members
+ #region IAuthorizationCarryingRequest Members
/// <summary>
/// Gets or sets the verification code or refresh/access token.
diff --git a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs
index 37d4cc2..65965ef 100644
--- a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs
+++ b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs
@@ -40,7 +40,7 @@ namespace DotNetOpenAuth.OAuth2.Messages {
((IMessageWithClientState)this).ClientState = request.ClientState;
}
- #region ITokenCarryingRequest Members
+ #region IAuthorizationCarryingRequest Members
/// <summary>
/// Gets or sets the verification code or refresh/access token.