//-----------------------------------------------------------------------
//
// 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);
}
}
}