diff options
Diffstat (limited to 'src')
19 files changed, 201 insertions, 218 deletions
diff --git a/src/DotNetOpenAuth.Core/Messaging/DataBag.cs b/src/DotNetOpenAuth.Core/Messaging/DataBag.cs index f09bcea..0800840 100644 --- a/src/DotNetOpenAuth.Core/Messaging/DataBag.cs +++ b/src/DotNetOpenAuth.Core/Messaging/DataBag.cs @@ -105,7 +105,7 @@ namespace DotNetOpenAuth.Messaging { /// </remarks> [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Accessed by reflection")] [MessagePart("t", IsRequired = true, AllowEmpty = false)] - private Type BagType { + protected virtual Type BagType { get { return this.GetType(); } } diff --git a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs index 746efb1..c9ceb81 100644 --- a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs +++ b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs @@ -22,7 +22,7 @@ namespace DotNetOpenAuth.Messaging { /// A serializer for <see cref="DataBag"/>-derived types /// </summary> /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam> - internal abstract class DataBagFormatterBase<T> : IDataBagFormatter<T> where T : DataBag, new() { + internal abstract class DataBagFormatterBase<T> : IDataBagFormatter<T> where T : DataBag { /// <summary> /// The message description cache to use for data bag types. /// </summary> @@ -192,14 +192,13 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Deserializes a <see cref="DataBag"/>, including decompression, decryption, signature and nonce validation where applicable. /// </summary> + /// <param name="message">The instance to initialize with deserialized data.</param> /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be null.</param> /// <param name="value">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param> /// <param name="messagePartName">The name of the parameter whose value is to be deserialized. Used for error message generation.</param> - /// <returns> - /// The deserialized value. Never null. - /// </returns> [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")] - public T Deserialize(IProtocolMessage containingMessage, string value, string messagePartName) { + public void Deserialize(T message, IProtocolMessage containingMessage, string value, string messagePartName) { + Requires.NotNull(message, "message"); Requires.NotNull(containingMessage, "containingMessage"); Requires.NotNullOrEmpty(value, "value"); Requires.NotNullOrEmpty(messagePartName, "messagePartName"); @@ -211,7 +210,7 @@ namespace DotNetOpenAuth.Messaging { value = valueWithoutHandle; } - var message = new T { ContainingMessage = containingMessage }; + message.ContainingMessage = containingMessage; byte[] data = MessagingUtilities.FromBase64WebSafeString(value); byte[] signature = null; @@ -256,8 +255,6 @@ namespace DotNetOpenAuth.Messaging { } ((IMessage)message).EnsureValidMessage(); - - return message; } /// <summary> diff --git a/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs b/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs index 9086ee9..923773e 100644 --- a/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs +++ b/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs @@ -13,7 +13,7 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam> [ContractClass(typeof(IDataBagFormatterContract<>))] - internal interface IDataBagFormatter<T> where T : DataBag, new() { + internal interface IDataBagFormatter<in T> where T : DataBag { /// <summary> /// Serializes the specified message. /// </summary> @@ -24,13 +24,11 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Deserializes a <see cref="DataBag"/>. /// </summary> + /// <param name="message">The instance to deserialize into</param> /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be null.</param> /// <param name="data">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param> /// <param name="messagePartName">The name of the parameter whose value is to be deserialized. Used for error message generation.</param> - /// <returns> - /// The deserialized value. Never null. - /// </returns> - T Deserialize(IProtocolMessage containingMessage, string data, string messagePartName); + void Deserialize(T message, IProtocolMessage containingMessage, string data, string messagePartName); } /// <summary> @@ -62,13 +60,12 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Deserializes a <see cref="DataBag"/>. /// </summary> + /// <param name="message">The instance to deserialize into</param> /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be nulll.</param> /// <param name="data">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param> /// <param name="messagePartName">Name of the message part whose value is to be deserialized. Used for exception messages.</param> - /// <returns> - /// The deserialized value. Never null. - /// </returns> - T IDataBagFormatter<T>.Deserialize(IProtocolMessage containingMessage, string data, string messagePartName) { + void IDataBagFormatter<T>.Deserialize(T message, IProtocolMessage containingMessage, string data, string messagePartName) { + Requires.NotNull(message, "message"); Requires.NotNull(containingMessage, "containingMessage"); Requires.NotNullOrEmpty(data, "data"); Requires.NotNullOrEmpty(messagePartName, "messagePartName"); diff --git a/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs b/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs index 92b1928..242175e 100644 --- a/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs +++ b/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs @@ -20,7 +20,7 @@ namespace DotNetOpenAuth.Messaging { /// A serializer for <see cref="DataBag"/>-derived types /// </summary> /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam> - internal class UriStyleMessageFormatter<T> : DataBagFormatterBase<T> where T : DataBag, new() { + internal class UriStyleMessageFormatter<T> : DataBagFormatterBase<T> where T : DataBag { /// <summary> /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> class. /// </summary> diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj index 11b1cb2..34d59ee 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj @@ -20,6 +20,7 @@ <ItemGroup> <Compile Include="Configuration\OAuth2AuthorizationServerSection.cs" /> <Compile Include="OAuth2\AuthorizationServer.cs" /> + <Compile Include="OAuth2\AuthorizationServerAccessToken.cs" /> <Compile Include="OAuth2\AuthServerStrings.Designer.cs"> <AutoGen>True</AutoGen> <DesignTime>True</DesignTime> diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs index ab20971..6a96c2d 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs @@ -143,30 +143,30 @@ namespace DotNetOpenAuth.OAuth2 { IProtocolMessage responseMessage; try { if (this.Channel.TryReadFromRequest(request, out requestMessage)) { + var accessTokenResult = this.AuthorizationServerServices.CreateAccessToken(requestMessage); + ErrorUtilities.VerifyHost(accessTokenResult != null, "IAuthorizationServerHost.CreateAccessToken must not return null."); + IAccessTokenRequestInternal accessRequestInternal = requestMessage; - accessRequestInternal.AccessTokenCreationParameters = this.AuthorizationServerServices.GetAccessTokenParameters(requestMessage); - ErrorUtilities.VerifyHost(accessRequestInternal.AccessTokenCreationParameters != null, "IAuthorizationServerHost.GetAccessTokenParameters must not return null."); + accessRequestInternal.AccessTokenResult = accessTokenResult; - var successResponseMessage = this.PrepareAccessTokenResponse(requestMessage, accessRequestInternal.AccessTokenCreationParameters.IncludeRefreshToken); - successResponseMessage.Lifetime = accessRequestInternal.AccessTokenCreationParameters.AccessTokenLifetime; + var successResponseMessage = this.PrepareAccessTokenResponse(requestMessage, accessTokenResult.AllowRefreshToken); + successResponseMessage.Lifetime = accessTokenResult.AccessToken.Lifetime; var authCarryingRequest = requestMessage as IAuthorizationCarryingRequest; if (authCarryingRequest != null) { + accessTokenResult.AccessToken.ApplyAuthorization(authCarryingRequest.AuthorizationDescription); IAccessTokenIssuingResponse accessTokenIssuingResponse = successResponseMessage; - accessTokenIssuingResponse.AuthorizationDescription = new AccessToken(authCarryingRequest.AuthorizationDescription, successResponseMessage.Lifetime); - accessTokenIssuingResponse.AuthorizationDescription.ExtraData.AddRange(accessRequestInternal.AccessTokenCreationParameters.ExtraClaims); + accessTokenIssuingResponse.AuthorizationDescription = accessTokenResult.AccessToken; } responseMessage = successResponseMessage; } else { - responseMessage = new AccessTokenFailedResponse() { Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest, }; + responseMessage = new AccessTokenFailedResponse() { Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest }; } } catch (TokenEndpointProtocolException ex) { responseMessage = ex.GetResponse(); } catch (ProtocolException) { - responseMessage = new AccessTokenFailedResponse() { - Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest, - }; + responseMessage = new AccessTokenFailedResponse() { Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest }; } return this.Channel.PrepareResponse(responseMessage); @@ -212,16 +212,17 @@ namespace DotNetOpenAuth.OAuth2 { switch (authorizationRequest.ResponseType) { case EndUserAuthorizationResponseType.AccessToken: IAccessTokenRequestInternal accessRequestInternal = (EndUserAuthorizationImplicitRequest)authorizationRequest; - accessRequestInternal.AccessTokenCreationParameters = this.AuthorizationServerServices.GetAccessTokenParameters(accessRequestInternal); + var accessTokenResult = this.AuthorizationServerServices.CreateAccessToken(accessRequestInternal); + ErrorUtilities.VerifyHost(accessTokenResult != null, "IAuthorizationServerHost.CreateAccessToken must not return null."); + + accessRequestInternal.AccessTokenResult = accessTokenResult; var implicitGrantResponse = new EndUserAuthorizationSuccessAccessTokenResponse(callback, authorizationRequest); - implicitGrantResponse.Lifetime = accessRequestInternal.AccessTokenCreationParameters.AccessTokenLifetime; + implicitGrantResponse.Lifetime = accessTokenResult.AccessToken.Lifetime; + accessTokenResult.AccessToken.ApplyAuthorization(implicitGrantResponse.Scope, userName, implicitGrantResponse.Lifetime); + IAccessTokenCarryingRequest tokenCarryingResponse = implicitGrantResponse; - tokenCarryingResponse.AuthorizationDescription = new AccessToken( - implicitGrantResponse.Scope, - userName, - implicitGrantResponse.Lifetime); - tokenCarryingResponse.AuthorizationDescription.ExtraData.AddRange(accessRequestInternal.AccessTokenCreationParameters.ExtraClaims); + tokenCarryingResponse.AuthorizationDescription = accessTokenResult.AccessToken; response = implicitGrantResponse; break; @@ -279,24 +280,24 @@ namespace DotNetOpenAuth.OAuth2 { /// Prepares the response to an access token request. /// </summary> /// <param name="request">The request for an access token.</param> - /// <param name="includeRefreshToken">If set to <c>true</c>, the response will include a long-lived refresh token.</param> + /// <param name="allowRefreshToken">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> - private AccessTokenSuccessResponse PrepareAccessTokenResponse(AccessTokenRequestBase request, bool includeRefreshToken = true) { + private AccessTokenSuccessResponse PrepareAccessTokenResponse(AccessTokenRequestBase request, bool allowRefreshToken = true) { Requires.NotNull(request, "request"); - if (includeRefreshToken) { + if (allowRefreshToken) { if (request is AccessTokenClientCredentialsRequest) { // Per OAuth 2.0 section 4.4.3 (draft 23), refresh tokens should never be included // in a response to an access token request that used the client credential grant type. Logger.OAuth.Debug("Suppressing refresh token in access token response because the grant type used by the client disallows it."); - includeRefreshToken = false; + allowRefreshToken = false; } } var tokenRequest = (IAuthorizationCarryingRequest)request; var accessTokenRequest = (IAccessTokenRequestInternal)request; var response = new AccessTokenSuccessResponse(request) { - HasRefreshToken = includeRefreshToken, + HasRefreshToken = allowRefreshToken, }; response.Scope.ResetContents(tokenRequest.AuthorizationDescription.Scope); return response; diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServerAccessToken.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServerAccessToken.cs new file mode 100644 index 0000000..c577a0a --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServerAccessToken.cs @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------- +// <copyright file="AuthorizationServerAccessToken.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2 { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Security.Cryptography; + using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth2.ChannelElements; + + /// <summary> + /// An access token minted by the authorization server that can be serialized for transmission to the client. + /// </summary> + public class AuthorizationServerAccessToken : AccessToken { + /// <summary> + /// Initializes a new instance of the <see cref="AuthorizationServerAccessToken"/> class. + /// </summary> + public AuthorizationServerAccessToken() { + } + + /// <summary> + /// Gets or sets 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. + /// </remarks> + public RSACryptoServiceProvider AccessTokenSigningKey { get; set; } + + /// <summary> + /// Gets or sets the key to encrypt the access token. + /// </summary> + public RSACryptoServiceProvider ResourceServerEncryptionKey { get; set; } + + /// <summary> + /// Serializes this instance to a simple string for transmission to the client. + /// </summary> + /// <returns>A non-empty string.</returns> + protected internal override string Serialize() { + var formatter = CreateFormatter(this.AccessTokenSigningKey, this.ResourceServerEncryptionKey); + return formatter.Serialize(this); + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/TokenCodeSerializationBindingElement.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/TokenCodeSerializationBindingElement.cs index 41bc609..494a10b 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/TokenCodeSerializationBindingElement.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/TokenCodeSerializationBindingElement.cs @@ -71,10 +71,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { var accessTokenResponse = message as IAccessTokenIssuingResponse; if (accessTokenResponse != null && accessTokenResponse.AuthorizationDescription != null) { ErrorUtilities.VerifyInternal(request != null, "We should always have a direct request message for this case."); - var accessTokenFormatter = AccessToken.CreateFormatter( - request.AccessTokenCreationParameters.AccessTokenSigningKey, - request.AccessTokenCreationParameters.ResourceServerEncryptionKey); - accessTokenResponse.AccessToken = accessTokenFormatter.Serialize(accessTokenResponse.AuthorizationDescription); + accessTokenResponse.AccessToken = accessTokenResponse.AuthorizationDescription.Serialize(); } return null; @@ -105,14 +102,16 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { var authCodeCarrier = message as IAuthorizationCodeCarryingRequest; if (authCodeCarrier != null) { var authorizationCodeFormatter = AuthorizationCode.CreateFormatter(this.AuthorizationServer); - var authorizationCode = authorizationCodeFormatter.Deserialize(message, authCodeCarrier.Code, Protocol.code); + var authorizationCode = new AuthorizationCode(); + authorizationCodeFormatter.Deserialize(authorizationCode, message, authCodeCarrier.Code, Protocol.code); authCodeCarrier.AuthorizationDescription = authorizationCode; } var refreshTokenCarrier = message as IRefreshTokenCarryingRequest; if (refreshTokenCarrier != null) { var refreshTokenFormatter = RefreshToken.CreateFormatter(this.AuthorizationServer.CryptoKeyStore); - var refreshToken = refreshTokenFormatter.Deserialize(message, refreshTokenCarrier.RefreshToken, Protocol.refresh_token); + var refreshToken = new RefreshToken(); + refreshTokenFormatter.Deserialize(refreshToken, message, refreshTokenCarrier.RefreshToken, Protocol.refresh_token); refreshTokenCarrier.AuthorizationDescription = refreshToken; } diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/IAuthorizationServerHost.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/IAuthorizationServerHost.cs index c31ec81..4c25c16 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/IAuthorizationServerHost.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/IAuthorizationServerHost.cs @@ -38,14 +38,15 @@ namespace DotNetOpenAuth.OAuth2 { INonceStore NonceStore { get; } /// <summary> - /// Obtains parameters to go into the formulation of an access token. + /// Acquires the access token and related parameters that go into the formulation of the token endpoint's response to a client. /// </summary> /// <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> + /// token can be set based on the sensitivity of the resources. + /// </param> /// <returns>A non-null parameters instance that DotNetOpenAuth will dispose after it has been used.</returns> - AccessTokenParameters GetAccessTokenParameters(IAccessTokenRequest accessTokenRequestMessage); + AccessTokenResult CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage); /// <summary> /// Gets the client with a given identifier. @@ -205,9 +206,9 @@ namespace DotNetOpenAuth.OAuth2 { /// <returns> /// A non-null parameters instance that DotNetOpenAuth will dispose after it has been used. /// </returns> - AccessTokenParameters IAuthorizationServerHost.GetAccessTokenParameters(IAccessTokenRequest accessTokenRequestMessage) { + AccessTokenResult IAuthorizationServerHost.CreateAccessToken(IAccessTokenRequest accessTokenRequestMessage) { Contract.Requires(accessTokenRequestMessage != null); - Contract.Ensures(Contract.Result<AccessTokenParameters>() != null); + Contract.Ensures(Contract.Result<AccessTokenResult>() != null); throw new NotImplementedException(); } } diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj index 3fe6e27..4a0e344 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj @@ -18,7 +18,6 @@ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> </PropertyGroup> <ItemGroup> - <Compile Include="OAuth2\AccessTokenParameters.cs" /> <Compile Include="OAuth2\ChannelElements\EndUserAuthorizationResponseTypeEncoder.cs" /> <Compile Include="OAuth2\ChannelElements\GrantTypeEncoder.cs" /> <Compile Include="OAuth2\ChannelElements\OAuth2ChannelBase.cs" /> @@ -35,6 +34,7 @@ <Compile Include="OAuth2\Messages\AccessTokenRefreshRequest.cs" /> <Compile Include="OAuth2\Messages\AccessTokenRequestBase.cs" /> <Compile Include="OAuth2\Messages\AccessTokenResourceOwnerPasswordCredentialsRequest.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenResult.cs" /> <Compile Include="OAuth2\Messages\AccessTokenSuccessResponse.cs" /> <Compile Include="OAuth2\Messages\AuthenticatedClientRequestBase.cs" /> <Compile Include="OAuth2\Messages\EndUserAuthorizationFailedResponse.cs" /> diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/AccessTokenParameters.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/AccessTokenParameters.cs deleted file mode 100644 index 8f383cd..0000000 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/AccessTokenParameters.cs +++ /dev/null @@ -1,102 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="AccessTokenParameters.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OAuth2 { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Security.Cryptography; - using System.Text; - - /// <summary> - /// Describes the parameters to be fed into creating a response to an access token request. - /// </summary> - public class AccessTokenParameters : IDisposable { - /// <summary> - /// Initializes a new instance of the <see cref="AccessTokenParameters"/> class. - /// </summary> - public AccessTokenParameters() { - this.IncludeRefreshToken = true; - this.AccessTokenLifetime = TimeSpan.FromHours(1); - this.ExtraClaims = new Dictionary<string, string>(); - } - - /// <summary> - /// Gets or sets the access token lifetime. - /// </summary> - /// <value> - /// A positive timespan. - /// </value> - /// <remarks> - /// Note that within this lifetime, authorization <i>may</i> not be revokable. - /// Short lifetimes are recommended (e.g. one hour), particularly when the client is not authenticated or - /// the resources to which access is being granted are sensitive. - /// </remarks> - public TimeSpan AccessTokenLifetime { get; set; } - - /// <summary> - /// Gets or sets 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. - /// </remarks> - public RSACryptoServiceProvider AccessTokenSigningKey { get; set; } - - /// <summary> - /// Gets or sets the key to encrypt the access token. - /// </summary> - public RSACryptoServiceProvider ResourceServerEncryptionKey { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether to provide the client with a refresh token, when applicable. - /// </summary> - /// <value>The default value is <c>true</c>.</value> - /// <remarks>> - /// The refresh token will never be provided when this value is false. - /// The refresh token <em>may</em> be provided when this value is true. - /// </remarks> - public bool IncludeRefreshToken { get; set; } - - /// <summary> - /// Gets or sets a dictionary of additional claims to include in the <see cref="AccessToken"/>. - /// </summary> - public IDictionary<string, string> ExtraClaims { get; set; } - - #region Implementation of IDisposable - - /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> - /// <filterpriority>2</filterpriority> - public void Dispose() { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// Releases unmanaged and - optionally - managed resources - /// </summary> - /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool disposing) { - if (disposing) { - if (this.ResourceServerEncryptionKey != null) { - IDisposable value = this.ResourceServerEncryptionKey; - value.Dispose(); - } - - if (this.AccessTokenSigningKey != null) { - IDisposable value = this.AccessTokenSigningKey; - value.Dispose(); - } - } - } - - #endregion - } -} diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRequestBase.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRequestBase.cs index c2ab347..e6bbc34 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRequestBase.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/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, IAccessTokenRequestInternal, IDisposable { + public abstract class AccessTokenRequestBase : AuthenticatedClientRequestBase, IAccessTokenRequestInternal { /// <summary> /// Initializes a new instance of the <see cref="AccessTokenRequestBase"/> class. /// </summary> @@ -43,12 +43,9 @@ namespace DotNetOpenAuth.OAuth2.Messages { public bool ClientAuthenticated { get; internal set; } /// <summary> - /// Gets or sets the access token creation parameters. + /// Gets or sets the result of calling the authorization server host's access token creation method. /// </summary> - /// <remarks> - /// This property's value is set by a binding element in the OAuth 2 channel. - /// </remarks> - AccessTokenParameters IAccessTokenRequestInternal.AccessTokenCreationParameters { get; set; } + AccessTokenResult IAccessTokenRequestInternal.AccessTokenResult { get; set; } /// <summary> /// Gets the type of the grant. @@ -63,25 +60,6 @@ namespace DotNetOpenAuth.OAuth2.Messages { protected abstract HashSet<string> RequestedScope { get; } /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> - public void Dispose() { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// Releases unmanaged and - optionally - managed resources - /// </summary> - /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool disposing) { - IAccessTokenRequestInternal self = this; - if (self.AccessTokenCreationParameters != null) { - self.AccessTokenCreationParameters.Dispose(); - } - } - - /// <summary> /// Checks the message state for conformity to the protocol specification /// and throws an exception if the message is invalid. /// </summary> diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResult.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResult.cs new file mode 100644 index 0000000..11e486b --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResult.cs @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenResult.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2 { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Security.Cryptography; + using System.Text; + + /// <summary> + /// Describes the parameters to be fed into creating a response to an access token request. + /// </summary> + public class AccessTokenResult { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenResult"/> class. + /// </summary> + /// <param name="accessToken">The access token to include in this result.</param> + public AccessTokenResult(AccessToken accessToken) { + Requires.NotNull(accessToken, "accessToken"); + this.AllowRefreshToken = true; + this.AccessToken = accessToken; + } + + /// <summary> + /// Gets or sets a value indicating whether to provide the client with a refresh token, when applicable. + /// </summary> + /// <value>The default value is <c>true</c>.</value> + /// <remarks>> + /// The refresh token will never be provided when this value is false. + /// The refresh token <em>may</em> be provided when this value is true. + /// </remarks> + public bool AllowRefreshToken { get; set; } + + /// <summary> + /// Gets the access token. + /// </summary> + public AccessToken AccessToken { get; private set; } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs index 661e2ae..4b662cd 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs @@ -43,12 +43,9 @@ namespace DotNetOpenAuth.OAuth2.Messages { } /// <summary> - /// Gets or sets the access token creation parameters. + /// Gets or sets the result of calling the authorization server host's access token creation method. /// </summary> - /// <remarks> - /// This property's value is set by a binding element in the OAuth 2 channel. - /// </remarks> - AccessTokenParameters IAccessTokenRequestInternal.AccessTokenCreationParameters { get; set; } + AccessTokenResult IAccessTokenRequestInternal.AccessTokenResult { get; set; } /// <summary> /// Gets a value indicating whether the client requesting the access token has authenticated itself. diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequestInternal.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequestInternal.cs index e218462..44af074 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequestInternal.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequestInternal.cs @@ -15,11 +15,8 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// </summary> public interface IAccessTokenRequestInternal : IAccessTokenRequest { /// <summary> - /// Gets or sets the access token creation parameters. + /// Gets or sets the result of calling the authorization server host's access token creation method. /// </summary> - /// <remarks> - /// This property's value is set by a binding element in the OAuth 2 channel. - /// </remarks> - AccessTokenParameters AccessTokenCreationParameters { get; set; } + AccessTokenResult AccessTokenResult { get; set; } } } diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs index 992e93c..54d86ff 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs @@ -50,7 +50,8 @@ namespace DotNetOpenAuth.OAuth2 { /// <exception cref="ProtocolException">Thrown if the access token is expired, invalid, or from an untrusted authorization server.</exception> public virtual AccessToken DeserializeAccessToken(IDirectedProtocolMessage message, string accessToken) { var accessTokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServerPublicSigningKey, this.ResourceServerPrivateEncryptionKey); - var token = accessTokenFormatter.Deserialize(message, accessToken, Protocol.access_token); + var token = new AccessToken(); + accessTokenFormatter.Deserialize(token, message, accessToken, Protocol.access_token); return token; } } diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/AccessToken.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/AccessToken.cs index 3a12faa..5890d93 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/AccessToken.cs +++ b/src/DotNetOpenAuth.OAuth2/OAuth2/AccessToken.cs @@ -24,22 +24,55 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> - /// Initializes a new instance of the <see cref="AccessToken"/> class. + /// Gets or sets the lifetime of the access token. + /// </summary> + /// <value>The lifetime.</value> + [MessagePart(Encoder = typeof(TimespanSecondsEncoder))] + public TimeSpan? Lifetime { get; set; } + + /// <summary> + /// Gets the type of this instance. + /// </summary> + /// <value>The type of the bag.</value> + /// <remarks> + /// This ensures that one token cannot be misused as another kind of token. + /// </remarks> + protected override Type BagType { + get { + // different roles (authorization server vs. Client) may derive from AccessToken, but they are all interoperable. + return typeof(AccessToken); + } + } + + /// <summary> + /// Creates a formatter capable of serializing/deserializing an access token. + /// </summary> + /// <param name="signingKey">The crypto service provider with the authorization server's private key used to asymmetrically sign the access token.</param> + /// <param name="encryptingKey">The crypto service provider with the resource server's public key used to encrypt the access token.</param> + /// <returns>An access token serializer.</returns> + internal static IDataBagFormatter<AccessToken> CreateFormatter(RSACryptoServiceProvider signingKey, RSACryptoServiceProvider encryptingKey) { + Contract.Requires(signingKey != null || !signingKey.PublicOnly); + Contract.Requires(encryptingKey != null); + Contract.Ensures(Contract.Result<IDataBagFormatter<AccessToken>>() != null); + + return new UriStyleMessageFormatter<AccessToken>(signingKey, encryptingKey); + } + + /// <summary> + /// Initializes this instance of the <see cref="AccessToken"/> class. /// </summary> - /// <param name="authorization">The authorization to be described by the access token.</param> - /// <param name="lifetime">The lifetime of the access token.</param> - internal AccessToken(IAuthorizationDescription authorization, TimeSpan? lifetime) { + /// <param name="authorization">The authorization to apply to this access token.</param> + internal void ApplyAuthorization(IAuthorizationDescription authorization) { Requires.NotNull(authorization, "authorization"); this.ClientIdentifier = authorization.ClientIdentifier; this.UtcCreationDate = authorization.UtcIssued; this.User = authorization.User; this.Scope.ResetContents(authorization.Scope); - this.Lifetime = lifetime; } /// <summary> - /// Initializes a new instance of the <see cref="AccessToken"/> class. + /// Initializes this instance of the <see cref="AccessToken"/> class. /// </summary> /// <param name="scopes">The scopes.</param> /// <param name="username">The username of the account that authorized this token.</param> @@ -49,7 +82,7 @@ namespace DotNetOpenAuth.OAuth2 { /// is invoked in the case where the client is <em>not</em> authenticated, and therefore no /// trust in the client_id is appropriate. /// </remarks> - internal AccessToken(IEnumerable<string> scopes, string username, TimeSpan? lifetime) { + internal void ApplyAuthorization(IEnumerable<string> scopes, string username, TimeSpan? lifetime) { this.Scope.ResetContents(scopes); this.User = username; this.Lifetime = lifetime; @@ -57,24 +90,12 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> - /// Gets or sets the lifetime of the access token. - /// </summary> - /// <value>The lifetime.</value> - [MessagePart(Encoder = typeof(TimespanSecondsEncoder))] - public TimeSpan? Lifetime { get; set; } - - /// <summary> - /// Creates a formatter capable of serializing/deserializing an access token. + /// Serializes this instance to a simple string for transmission to the client. /// </summary> - /// <param name="signingKey">The crypto service provider with the authorization server's private key used to asymmetrically sign the access token.</param> - /// <param name="encryptingKey">The crypto service provider with the resource server's public key used to encrypt the access token.</param> - /// <returns>An access token serializer.</returns> - internal static IDataBagFormatter<AccessToken> CreateFormatter(RSACryptoServiceProvider signingKey, RSACryptoServiceProvider encryptingKey) { - Contract.Requires(signingKey != null || !signingKey.PublicOnly); - Contract.Requires(encryptingKey != null); - Contract.Ensures(Contract.Result<IDataBagFormatter<AccessToken>>() != null); - - return new UriStyleMessageFormatter<AccessToken>(signingKey, encryptingKey); + /// <returns>A non-empty string.</returns> + protected internal virtual string Serialize() { + Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); + throw new NotSupportedException(); } /// <summary> diff --git a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/ProviderAssociationHandleEncoder.cs b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/ProviderAssociationHandleEncoder.cs index 594803d..5c39c5e 100644 --- a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/ProviderAssociationHandleEncoder.cs +++ b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/ProviderAssociationHandleEncoder.cs @@ -68,9 +68,9 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <exception cref="ProtocolException">Thrown if the association is not of the expected type.</exception> public Association Deserialize(IProtocolMessage containingMessage, bool privateAssociation, string handle) { var formatter = AssociationDataBag.CreateFormatter(this.cryptoKeyStore, AssociationHandleEncodingSecretBucket); - AssociationDataBag bag; + AssociationDataBag bag = new AssociationDataBag(); try { - bag = formatter.Deserialize(containingMessage, handle, Protocol.Default.openid.assoc_handle); + formatter.Deserialize(bag, containingMessage, handle, Protocol.Default.openid.assoc_handle); } catch (ProtocolException ex) { Logger.OpenId.Error("Rejecting an association because deserialization of the encoded handle failed.", ex); return null; diff --git a/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs b/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs index e6fd81e..41bfaa0 100644 --- a/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs +++ b/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs @@ -55,7 +55,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { MessagingUtilities.AreEquivalent(d.Scope, TestScopes)))).Returns(true); string canonicalUserName = ResourceOwnerUsername; authHostMock.Setup(m => m.IsResourceOwnerCredentialValid(ResourceOwnerUsername, ResourceOwnerPassword, It.IsAny<IAccessTokenRequest>(), out canonicalUserName)).Returns(true); - authHostMock.Setup(m => m.GetAccessTokenParameters(It.IsAny<IAccessTokenRequest>())).Returns(new AccessTokenParameters()); + authHostMock.Setup(m => m.CreateAccessToken(It.IsAny<IAccessTokenRequest>())).Returns(new AccessTokenResult(new AuthorizationServerAccessToken())); return authHostMock; } } |