diff options
Diffstat (limited to 'src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs')
-rw-r--r-- | src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs new file mode 100644 index 0000000..719e84e --- /dev/null +++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs @@ -0,0 +1,576 @@ +//----------------------------------------------------------------------- +// <copyright file="ServiceProvider.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth { + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Globalization; + using System.Security.Principal; + using System.ServiceModel.Channels; + using System.Web; + using DotNetOpenAuth.Configuration; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.Messaging.Bindings; + using DotNetOpenAuth.OAuth.ChannelElements; + using DotNetOpenAuth.OAuth.Messages; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Extensions.OAuth; + using DotNetOpenAuth.OpenId.Messages; + using DotNetOpenAuth.OpenId.Provider; + + /// <summary> + /// A web application that allows access via OAuth. + /// </summary> + /// <remarks> + /// <para>The Service Provider’s documentation should include:</para> + /// <list> + /// <item>The URLs (Request URLs) the Consumer will use when making OAuth requests, and the HTTP methods (i.e. GET, POST, etc.) used in the Request Token URL and Access Token URL.</item> + /// <item>Signature methods supported by the Service Provider.</item> + /// <item>Any additional request parameters that the Service Provider requires in order to obtain a Token. Service Provider specific parameters MUST NOT begin with oauth_.</item> + /// </list> + /// </remarks> + public class ServiceProvider : IDisposable { + /// <summary> + /// The name of the key to use in the HttpApplication cache to store the + /// instance of <see cref="NonceMemoryStore"/> to use. + /// </summary> + private const string ApplicationStoreKey = "DotNetOpenAuth.OAuth.ServiceProvider.HttpApplicationStore"; + + /// <summary> + /// The length of the verifier code (in raw bytes before base64 encoding) to generate. + /// </summary> + private const int VerifierCodeLength = 5; + + /// <summary> + /// The field behind the <see cref="OAuthChannel"/> property. + /// </summary> + private OAuthChannel channel; + + /// <summary> + /// Initializes a new instance of the <see cref="ServiceProvider"/> class. + /// </summary> + /// <param name="serviceDescription">The endpoints and behavior on the Service Provider.</param> + /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param> + public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager) + : this(serviceDescription, tokenManager, new OAuthServiceProviderMessageFactory(tokenManager)) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="ServiceProvider"/> class. + /// </summary> + /// <param name="serviceDescription">The endpoints and behavior on the Service Provider.</param> + /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param> + /// <param name="messageTypeProvider">An object that can figure out what type of message is being received for deserialization.</param> + public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider) + : this(serviceDescription, tokenManager, OAuthElement.Configuration.ServiceProvider.ApplicationStore.CreateInstance(HttpApplicationStore), messageTypeProvider) { + Requires.NotNull(serviceDescription, "serviceDescription"); + Requires.NotNull(tokenManager, "tokenManager"); + Requires.NotNull(messageTypeProvider, "messageTypeProvider"); + } + + /// <summary> + /// Initializes a new instance of the <see cref="ServiceProvider"/> class. + /// </summary> + /// <param name="serviceDescription">The endpoints and behavior on the Service Provider.</param> + /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param> + /// <param name="nonceStore">The nonce store.</param> + public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, INonceStore nonceStore) + : this(serviceDescription, tokenManager, nonceStore, new OAuthServiceProviderMessageFactory(tokenManager)) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="ServiceProvider"/> class. + /// </summary> + /// <param name="serviceDescription">The endpoints and behavior on the Service Provider.</param> + /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param> + /// <param name="nonceStore">The nonce store.</param> + /// <param name="messageTypeProvider">An object that can figure out what type of message is being received for deserialization.</param> + public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, INonceStore nonceStore, OAuthServiceProviderMessageFactory messageTypeProvider) { + Requires.NotNull(serviceDescription, "serviceDescription"); + Requires.NotNull(tokenManager, "tokenManager"); + Requires.NotNull(nonceStore, "nonceStore"); + Requires.NotNull(messageTypeProvider, "messageTypeProvider"); + + var signingElement = serviceDescription.CreateTamperProtectionElement(); + this.ServiceDescription = serviceDescription; + this.SecuritySettings = OAuthElement.Configuration.ServiceProvider.SecuritySettings.CreateSecuritySettings(); + this.OAuthChannel = new OAuthServiceProviderChannel(signingElement, nonceStore, tokenManager, this.SecuritySettings, messageTypeProvider); + this.TokenGenerator = new StandardTokenGenerator(); + + OAuthReporting.RecordFeatureAndDependencyUse(this, serviceDescription, tokenManager, nonceStore); + } + + /// <summary> + /// Gets the standard state storage mechanism that uses ASP.NET's + /// HttpApplication state dictionary to store associations and nonces. + /// </summary> + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static INonceStore HttpApplicationStore { + get { + Contract.Ensures(Contract.Result<INonceStore>() != null); + + HttpContext context = HttpContext.Current; + ErrorUtilities.VerifyOperation(context != null, Strings.StoreRequiredWhenNoHttpContextAvailable, typeof(INonceStore).Name); + var store = (INonceStore)context.Application[ApplicationStoreKey]; + if (store == null) { + context.Application.Lock(); + try { + if ((store = (INonceStore)context.Application[ApplicationStoreKey]) == null) { + context.Application[ApplicationStoreKey] = store = new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge); + } + } finally { + context.Application.UnLock(); + } + } + + return store; + } + } + + /// <summary> + /// Gets the description of this Service Provider. + /// </summary> + public ServiceProviderDescription ServiceDescription { get; private set; } + + /// <summary> + /// Gets or sets the generator responsible for generating new tokens and secrets. + /// </summary> + public ITokenGenerator TokenGenerator { get; set; } + + /// <summary> + /// Gets the persistence store for tokens and secrets. + /// </summary> + public IServiceProviderTokenManager TokenManager { + get { return (IServiceProviderTokenManager)this.OAuthChannel.TokenManager; } + } + + /// <summary> + /// Gets the channel to use for sending/receiving messages. + /// </summary> + public Channel Channel { + get { return this.OAuthChannel; } + } + + /// <summary> + /// Gets the security settings for this service provider. + /// </summary> + public ServiceProviderSecuritySettings SecuritySettings { get; private set; } + + /// <summary> + /// Gets or sets the channel to use for sending/receiving messages. + /// </summary> + internal OAuthChannel OAuthChannel { + get { + return this.channel; + } + + set { + Requires.NotNull(value, "value"); + this.channel = value; + } + } + + /// <summary> + /// Creates a cryptographically strong random verification code. + /// </summary> + /// <param name="format">The desired format of the verification code.</param> + /// <param name="length">The length of the code. + /// When <paramref name="format"/> is <see cref="VerificationCodeFormat.IncludedInCallback"/>, + /// this is the length of the original byte array before base64 encoding rather than the actual + /// length of the final string.</param> + /// <returns>The verification code.</returns> + public static string CreateVerificationCode(VerificationCodeFormat format, int length) { + Requires.InRange(length >= 0, "length"); + + switch (format) { + case VerificationCodeFormat.IncludedInCallback: + return MessagingUtilities.GetCryptoRandomDataAsBase64(length); + case VerificationCodeFormat.AlphaNumericNoLookAlikes: + return MessagingUtilities.GetRandomString(length, MessagingUtilities.AlphaNumericNoLookAlikes); + case VerificationCodeFormat.AlphaUpper: + return MessagingUtilities.GetRandomString(length, MessagingUtilities.UppercaseLetters); + case VerificationCodeFormat.AlphaLower: + return MessagingUtilities.GetRandomString(length, MessagingUtilities.LowercaseLetters); + case VerificationCodeFormat.Numeric: + return MessagingUtilities.GetRandomString(length, MessagingUtilities.Digits); + default: + throw new ArgumentOutOfRangeException("format"); + } + } + + /// <summary> + /// Reads any incoming OAuth message. + /// </summary> + /// <returns>The deserialized message.</returns> + /// <remarks> + /// Requires HttpContext.Current. + /// </remarks> + public IDirectedProtocolMessage ReadRequest() { + return this.Channel.ReadFromRequest(); + } + + /// <summary> + /// Reads any incoming OAuth message. + /// </summary> + /// <param name="request">The HTTP request to read the message from.</param> + /// <returns>The deserialized message.</returns> + public IDirectedProtocolMessage ReadRequest(HttpRequestInfo request) { + return this.Channel.ReadFromRequest(request); + } + + /// <summary> + /// Gets the incoming request for an unauthorized token, if any. + /// </summary> + /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + /// <remarks> + /// Requires HttpContext.Current. + /// </remarks> + public UnauthorizedTokenRequest ReadTokenRequest() { + return this.ReadTokenRequest(this.Channel.GetRequestFromContext()); + } + + /// <summary> + /// Reads a request for an unauthorized token from the incoming HTTP request. + /// </summary> + /// <param name="request">The HTTP request to read from.</param> + /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + public UnauthorizedTokenRequest ReadTokenRequest(HttpRequestInfo request) { + UnauthorizedTokenRequest message; + if (this.Channel.TryReadFromRequest(request, out message)) { + ErrorUtilities.VerifyProtocol(message.Version >= Protocol.Lookup(this.SecuritySettings.MinimumRequiredOAuthVersion).Version, OAuthStrings.MinimumConsumerVersionRequirementNotMet, this.SecuritySettings.MinimumRequiredOAuthVersion, message.Version); + } + return message; + } + + /// <summary> + /// Prepares a message containing an unauthorized token for the Consumer to use in a + /// user agent redirect for subsequent authorization. + /// </summary> + /// <param name="request">The token request message the Consumer sent that the Service Provider is now responding to.</param> + /// <returns>The response message to send using the <see cref="Channel"/>, after optionally adding extra data to it.</returns> + public UnauthorizedTokenResponse PrepareUnauthorizedTokenMessage(UnauthorizedTokenRequest request) { + Requires.NotNull(request, "request"); + + string token = this.TokenGenerator.GenerateRequestToken(request.ConsumerKey); + string secret = this.TokenGenerator.GenerateSecret(); + UnauthorizedTokenResponse response = new UnauthorizedTokenResponse(request, token, secret); + + return response; + } + + /// <summary> + /// Gets the incoming request for the Service Provider to authorize a Consumer's + /// access to some protected resources. + /// </summary> + /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + /// <remarks> + /// Requires HttpContext.Current. + /// </remarks> + public UserAuthorizationRequest ReadAuthorizationRequest() { + return this.ReadAuthorizationRequest(this.Channel.GetRequestFromContext()); + } + + /// <summary> + /// Reads in a Consumer's request for the Service Provider to obtain permission from + /// the user to authorize the Consumer's access of some protected resource(s). + /// </summary> + /// <param name="request">The HTTP request to read from.</param> + /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + public UserAuthorizationRequest ReadAuthorizationRequest(HttpRequestInfo request) { + UserAuthorizationRequest message; + this.Channel.TryReadFromRequest(request, out message); + return message; + } + + /// <summary> + /// Gets the OAuth authorization request included with an OpenID authentication + /// request, if there is one. + /// </summary> + /// <param name="openIdRequest">The OpenID authentication request.</param> + /// <returns> + /// The scope of access the relying party is requesting, or null if no OAuth request + /// is present. + /// </returns> + /// <remarks> + /// <para>Call this method rather than simply extracting the OAuth extension + /// out from the authentication request directly to ensure that the additional + /// security measures that are required are taken.</para> + /// </remarks> + public AuthorizationRequest ReadAuthorizationRequest(IHostProcessedRequest openIdRequest) { + Requires.NotNull(openIdRequest, "openIdRequest"); + Requires.ValidState(this.TokenManager is ICombinedOpenIdProviderTokenManager); + var openidTokenManager = this.TokenManager as ICombinedOpenIdProviderTokenManager; + ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName); + + var authzRequest = openIdRequest.GetExtension<AuthorizationRequest>(); + if (authzRequest == null) { + return null; + } + + // OpenID+OAuth spec section 9: + // The Combined Provider SHOULD verify that the consumer key passed in the + // request is authorized to be used for the realm passed in the request. + string expectedConsumerKey = openidTokenManager.GetConsumerKey(openIdRequest.Realm); + ErrorUtilities.VerifyProtocol( + string.Equals(expectedConsumerKey, authzRequest.Consumer, StringComparison.Ordinal), + OAuthStrings.OpenIdOAuthRealmConsumerKeyDoNotMatch); + + return authzRequest; + } + + /// <summary> + /// Attaches the authorization response to an OpenID authentication response. + /// </summary> + /// <param name="openIdAuthenticationRequest">The OpenID authentication request.</param> + /// <param name="consumerKey">The consumer key. Must be <c>null</c> if and only if <paramref name="scope"/> is null.</param> + /// <param name="scope">The approved access scope. Use <c>null</c> to indicate no access was granted. The empty string will be interpreted as some default level of access is granted.</param> + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "We want to take IAuthenticationRequest because that's the only supported use case.")] + [Obsolete("Call the overload that doesn't take a consumerKey instead.")] + public void AttachAuthorizationResponse(IHostProcessedRequest openIdAuthenticationRequest, string consumerKey, string scope) { + Requires.NotNull(openIdAuthenticationRequest, "openIdAuthenticationRequest"); + Requires.True((consumerKey == null) == (scope == null), null); + Requires.ValidState(this.TokenManager is ICombinedOpenIdProviderTokenManager); + var openidTokenManager = (ICombinedOpenIdProviderTokenManager)this.TokenManager; + ErrorUtilities.VerifyArgument(consumerKey == null || consumerKey == openidTokenManager.GetConsumerKey(openIdAuthenticationRequest.Realm), OAuthStrings.OpenIdOAuthRealmConsumerKeyDoNotMatch); + + this.AttachAuthorizationResponse(openIdAuthenticationRequest, scope); + } + + /// <summary> + /// Attaches the authorization response to an OpenID authentication response. + /// </summary> + /// <param name="openIdAuthenticationRequest">The OpenID authentication request.</param> + /// <param name="scope">The approved access scope. Use <c>null</c> to indicate no access was granted. The empty string will be interpreted as some default level of access is granted.</param> + [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "We want to take IAuthenticationRequest because that's the only supported use case.")] + public void AttachAuthorizationResponse(IHostProcessedRequest openIdAuthenticationRequest, string scope) { + Requires.NotNull(openIdAuthenticationRequest, "openIdAuthenticationRequest"); + Requires.ValidState(this.TokenManager is ICombinedOpenIdProviderTokenManager); + + var openidTokenManager = this.TokenManager as ICombinedOpenIdProviderTokenManager; + IOpenIdMessageExtension response; + if (scope != null) { + // Generate an authorized request token to return to the relying party. + string consumerKey = openidTokenManager.GetConsumerKey(openIdAuthenticationRequest.Realm); + var approvedResponse = new AuthorizationApprovedResponse { + RequestToken = this.TokenGenerator.GenerateRequestToken(consumerKey), + Scope = scope, + }; + openidTokenManager.StoreOpenIdAuthorizedRequestToken(consumerKey, approvedResponse); + response = approvedResponse; + } else { + response = new AuthorizationDeclinedResponse(); + } + + openIdAuthenticationRequest.AddResponseExtension(response); + } + + /// <summary> + /// Prepares the message to send back to the consumer following proper authorization of + /// a token by an interactive user at the Service Provider's web site. + /// </summary> + /// <param name="request">The Consumer's original authorization request.</param> + /// <returns> + /// The message to send to the Consumer using <see cref="Channel"/> if one is necessary. + /// Null if the Consumer did not request a callback as part of the authorization request. + /// </returns> + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Consistent user experience with instance.")] + public UserAuthorizationResponse PrepareAuthorizationResponse(UserAuthorizationRequest request) { + Requires.NotNull(request, "request"); + + // It is very important for us to ignore the oauth_callback argument in the + // UserAuthorizationRequest if the Consumer is a 1.0a consumer or else we + // open up a security exploit. + IServiceProviderRequestToken token = this.TokenManager.GetRequestToken(request.RequestToken); + Uri callback; + if (request.Version >= Protocol.V10a.Version) { + // In OAuth 1.0a, we'll prefer the token-specific callback to the pre-registered one. + if (token.Callback != null) { + callback = token.Callback; + } else { + IConsumerDescription consumer = this.TokenManager.GetConsumer(token.ConsumerKey); + callback = consumer.Callback; + } + } else { + // In OAuth 1.0, we'll prefer the pre-registered callback over the token-specific one + // since 1.0 has a security weakness for user-modified callback URIs. + IConsumerDescription consumer = this.TokenManager.GetConsumer(token.ConsumerKey); + callback = consumer.Callback ?? request.Callback; + } + + return callback != null ? this.PrepareAuthorizationResponse(request, callback) : null; + } + + /// <summary> + /// Prepares the message to send back to the consumer following proper authorization of + /// a token by an interactive user at the Service Provider's web site. + /// </summary> + /// <param name="request">The Consumer's original authorization request.</param> + /// <param name="callback">The callback URI the consumer has previously registered + /// with this service provider or that came in the <see cref="UnauthorizedTokenRequest"/>.</param> + /// <returns> + /// The message to send to the Consumer using <see cref="Channel"/>. + /// </returns> + [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Consistent user experience with instance.")] + public UserAuthorizationResponse PrepareAuthorizationResponse(UserAuthorizationRequest request, Uri callback) { + Requires.NotNull(request, "request"); + Requires.NotNull(callback, "callback"); + + var authorization = new UserAuthorizationResponse(callback, request.Version) { + RequestToken = request.RequestToken, + }; + + if (authorization.Version >= Protocol.V10a.Version) { + authorization.VerificationCode = CreateVerificationCode(VerificationCodeFormat.IncludedInCallback, VerifierCodeLength); + } + + return authorization; + } + + /// <summary> + /// Gets the incoming request to exchange an authorized token for an access token. + /// </summary> + /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + /// <remarks> + /// Requires HttpContext.Current. + /// </remarks> + public AuthorizedTokenRequest ReadAccessTokenRequest() { + return this.ReadAccessTokenRequest(this.Channel.GetRequestFromContext()); + } + + /// <summary> + /// Reads in a Consumer's request to exchange an authorized request token for an access token. + /// </summary> + /// <param name="request">The HTTP request to read from.</param> + /// <returns>The incoming request, or null if no OAuth message was attached.</returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + public AuthorizedTokenRequest ReadAccessTokenRequest(HttpRequestInfo request) { + AuthorizedTokenRequest message; + this.Channel.TryReadFromRequest(request, out message); + return message; + } + + /// <summary> + /// Prepares and sends an access token to a Consumer, and invalidates the request token. + /// </summary> + /// <param name="request">The Consumer's message requesting an access token.</param> + /// <returns>The HTTP response to actually send to the Consumer.</returns> + public AuthorizedTokenResponse PrepareAccessTokenMessage(AuthorizedTokenRequest request) { + Requires.NotNull(request, "request"); + + ErrorUtilities.VerifyProtocol(this.TokenManager.IsRequestTokenAuthorized(request.RequestToken), OAuthStrings.AccessTokenNotAuthorized, request.RequestToken); + + string accessToken = this.TokenGenerator.GenerateAccessToken(request.ConsumerKey); + string tokenSecret = this.TokenGenerator.GenerateSecret(); + this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(request.ConsumerKey, request.RequestToken, accessToken, tokenSecret); + var grantAccess = new AuthorizedTokenResponse(request) { + AccessToken = accessToken, + TokenSecret = tokenSecret, + }; + + return grantAccess; + } + + /// <summary> + /// Gets the authorization (access token) for accessing some protected resource. + /// </summary> + /// <returns>The authorization message sent by the Consumer, or null if no authorization message is attached.</returns> + /// <remarks> + /// This method verifies that the access token and token secret are valid. + /// It falls on the caller to verify that the access token is actually authorized + /// to access the resources being requested. + /// </remarks> + /// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception> + public AccessProtectedResourceRequest ReadProtectedResourceAuthorization() { + return this.ReadProtectedResourceAuthorization(this.Channel.GetRequestFromContext()); + } + + /// <summary> + /// Gets the authorization (access token) for accessing some protected resource. + /// </summary> + /// <param name="request">HTTP details from an incoming WCF message.</param> + /// <param name="requestUri">The URI of the WCF service endpoint.</param> + /// <returns>The authorization message sent by the Consumer, or null if no authorization message is attached.</returns> + /// <remarks> + /// This method verifies that the access token and token secret are valid. + /// It falls on the caller to verify that the access token is actually authorized + /// to access the resources being requested. + /// </remarks> + /// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception> + public AccessProtectedResourceRequest ReadProtectedResourceAuthorization(HttpRequestMessageProperty request, Uri requestUri) { + return this.ReadProtectedResourceAuthorization(new HttpRequestInfo(request, requestUri)); + } + + /// <summary> + /// Gets the authorization (access token) for accessing some protected resource. + /// </summary> + /// <param name="request">The incoming HTTP request.</param> + /// <returns>The authorization message sent by the Consumer, or null if no authorization message is attached.</returns> + /// <remarks> + /// This method verifies that the access token and token secret are valid. + /// It falls on the caller to verify that the access token is actually authorized + /// to access the resources being requested. + /// </remarks> + /// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception> + public AccessProtectedResourceRequest ReadProtectedResourceAuthorization(HttpRequestInfo request) { + Requires.NotNull(request, "request"); + + AccessProtectedResourceRequest accessMessage; + if (this.Channel.TryReadFromRequest<AccessProtectedResourceRequest>(request, out accessMessage)) { + if (this.TokenManager.GetTokenType(accessMessage.AccessToken) != TokenType.AccessToken) { + throw new ProtocolException( + string.Format( + CultureInfo.CurrentCulture, + OAuthStrings.BadAccessTokenInProtectedResourceRequest, + accessMessage.AccessToken)); + } + } + + return accessMessage; + } + + /// <summary> + /// Creates a security principal that may be used. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>The <see cref="IPrincipal"/> instance that can be used for access control of resources.</returns> + public OAuthPrincipal CreatePrincipal(AccessProtectedResourceRequest request) { + Requires.NotNull(request, "request"); + + IServiceProviderAccessToken accessToken = this.TokenManager.GetAccessToken(request.AccessToken); + return new OAuthPrincipal(accessToken); + } + + #region IDisposable Members + + /// <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) { + if (disposing) { + this.Channel.Dispose(); + } + } + + #endregion + } +} |