diff options
Diffstat (limited to 'src')
12 files changed, 158 insertions, 33 deletions
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; } + } } } |