//----------------------------------------------------------------------- // // Copyright (c) Outercurve Foundation. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth { using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; 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; using Validation; /// /// A web application that allows access via OAuth and can respond to OpenID+OAuth requests. /// /// /// The Service Provider’s documentation should include: /// /// 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. /// Signature methods supported by the Service Provider. /// Any additional request parameters that the Service Provider requires in order to obtain a Token. Service Provider specific parameters MUST NOT begin with oauth_. /// /// public class ServiceProviderOpenIdProvider : ServiceProvider { /// /// Initializes a new instance of the class. /// /// The endpoints and behavior on the Service Provider. /// The host's method of storing and recalling tokens and secrets. public ServiceProviderOpenIdProvider(ServiceProviderHostDescription serviceDescription, IServiceProviderTokenManager tokenManager) : base(serviceDescription, tokenManager) { } /// /// Initializes a new instance of the class. /// /// The service description. /// The token manager. /// The message type provider. public ServiceProviderOpenIdProvider(ServiceProviderHostDescription serviceDescription, IServiceProviderTokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider) : base(serviceDescription, tokenManager, messageTypeProvider) { } /// /// Initializes a new instance of the class. /// /// The service description. /// The token manager. /// The nonce store. public ServiceProviderOpenIdProvider(ServiceProviderHostDescription serviceDescription, IServiceProviderTokenManager tokenManager, INonceStore nonceStore) : base(serviceDescription, tokenManager, nonceStore) { } /// /// Initializes a new instance of the class. /// /// The service description. /// The token manager. /// The nonce store. /// The message type provider. public ServiceProviderOpenIdProvider(ServiceProviderHostDescription serviceDescription, IServiceProviderTokenManager tokenManager, INonceStore nonceStore, OAuthServiceProviderMessageFactory messageTypeProvider) : base(serviceDescription, tokenManager, nonceStore, messageTypeProvider) { } /// /// Gets the OAuth authorization request included with an OpenID authentication /// request, if there is one. /// /// The OpenID authentication request. /// /// The scope of access the relying party is requesting, or null if no OAuth request /// is present. /// /// /// 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. /// public AuthorizationRequest ReadAuthorizationRequest(IHostProcessedRequest openIdRequest) { Requires.NotNull(openIdRequest, "openIdRequest"); RequiresEx.ValidState(this.TokenManager is ICombinedOpenIdProviderTokenManager); var openidTokenManager = this.TokenManager as ICombinedOpenIdProviderTokenManager; ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName); var authzRequest = openIdRequest.GetExtension(); 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; } /// /// Attaches the authorization response to an OpenID authentication response. /// /// The OpenID authentication request. /// The consumer key. Must be null if and only if is null. /// The approved access scope. Use null to indicate no access was granted. The empty string will be interpreted as some default level of access is granted. [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.That((consumerKey == null) == (scope == null), null, "consumerKey and scope must either be both provided or both omitted."); RequiresEx.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); } /// /// Attaches the authorization response to an OpenID authentication response. /// /// The OpenID authentication request. /// The approved access scope. Use null to indicate no access was granted. The empty string will be interpreted as some default level of access is granted. [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"); RequiresEx.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); } } }