// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Security;
using DotNetOpenAuth.AspNet;
using DotNetOpenAuth.AspNet.Clients;
using Microsoft.Web.WebPages.OAuth.Properties;
using WebMatrix.WebData;
namespace Microsoft.Web.WebPages.OAuth
{
///
/// Contains APIs to manage authentication against OAuth & OpenID service providers
///
public static class OAuthWebSecurity
{
internal static IOpenAuthDataProvider OAuthDataProvider = new WebPagesOAuthDataProvider();
// contains all registered authentication clients
private static readonly AuthenticationClientCollection _authenticationClients = new AuthenticationClientCollection();
///
/// Gets a value indicating whether the current user is authenticated by an OAuth provider.
///
public static bool IsAuthenticatedWithOAuth
{
get
{
if (HttpContext.Current == null)
{
throw new InvalidOperationException(WebResources.HttpContextNotAvailable);
}
return GetIsAuthenticatedWithOAuthCore(new HttpContextWrapper(HttpContext.Current));
}
}
///
/// Registers a supported OAuth client with the specified consumer key and consumer secret.
///
/// One of the supported OAuth clients.
/// The consumer key.
/// The consumer secret.
public static void RegisterOAuthClient(BuiltInOAuthClient client, string consumerKey, string consumerSecret)
{
IAuthenticationClient authenticationClient;
switch (client)
{
case BuiltInOAuthClient.LinkedIn:
authenticationClient = new LinkedInClient(consumerKey, consumerSecret);
break;
case BuiltInOAuthClient.Twitter:
authenticationClient = new TwitterClient(consumerKey, consumerSecret);
break;
case BuiltInOAuthClient.Facebook:
authenticationClient = new FacebookClient(consumerKey, consumerSecret);
break;
case BuiltInOAuthClient.WindowsLive:
authenticationClient = new WindowsLiveClient(consumerKey, consumerSecret);
break;
default:
throw new ArgumentOutOfRangeException("client");
}
RegisterClient(authenticationClient);
}
///
/// Registers a supported OpenID client
///
public static void RegisterOpenIDClient(BuiltInOpenIDClient openIDClient)
{
IAuthenticationClient client;
switch (openIDClient)
{
case BuiltInOpenIDClient.Google:
client = new GoogleOpenIdClient();
break;
case BuiltInOpenIDClient.Yahoo:
client = new YahooOpenIdClient();
break;
default:
throw new ArgumentOutOfRangeException("openIDClient");
}
RegisterClient(client);
}
///
/// Registers an authentication client.
///
[CLSCompliant(false)]
public static void RegisterClient(IAuthenticationClient client)
{
if (client == null)
{
throw new ArgumentNullException("client");
}
if (String.IsNullOrEmpty(client.ProviderName))
{
throw new ArgumentException(WebResources.InvalidServiceProviderName, "client");
}
if (_authenticationClients.Contains(client))
{
throw new ArgumentException(WebResources.ServiceProviderNameExists, "client");
}
_authenticationClients.Add(client);
}
///
/// Requests the specified provider to start the authentication by directing users to an external website
///
/// The provider.
public static void RequestAuthentication(string provider)
{
RequestAuthentication(provider, returnUrl: null);
}
///
/// Requests the specified provider to start the authentication by directing users to an external website
///
/// The provider.
/// The return url after user is authenticated.
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#", Justification = "We want to allow relative app path, and support ~/")]
public static void RequestAuthentication(string provider, string returnUrl)
{
if (HttpContext.Current == null)
{
throw new InvalidOperationException(WebResources.HttpContextNotAvailable);
}
RequestAuthenticationCore(new HttpContextWrapper(HttpContext.Current), provider, returnUrl);
}
internal static void RequestAuthenticationCore(HttpContextBase context, string provider, string returnUrl)
{
IAuthenticationClient client = GetOAuthClient(provider);
var securityManager = new OpenAuthSecurityManager(context, client, OAuthDataProvider);
securityManager.RequestAuthentication(returnUrl);
}
///
/// Checks if user is successfully authenticated when user is redirected back to this user.
///
[CLSCompliant(false)]
public static AuthenticationResult VerifyAuthentication()
{
if (HttpContext.Current == null)
{
throw new InvalidOperationException(WebResources.HttpContextNotAvailable);
}
return VerifyAuthenticationCore(new HttpContextWrapper(HttpContext.Current));
}
internal static AuthenticationResult VerifyAuthenticationCore(HttpContextBase context)
{
string providerName = OpenAuthSecurityManager.GetProviderName(context);
if (String.IsNullOrEmpty(providerName))
{
return AuthenticationResult.Failed;
}
IAuthenticationClient client;
if (TryGetOAuthClient(providerName, out client))
{
var securityManager = new OpenAuthSecurityManager(context, client, OAuthDataProvider);
return securityManager.VerifyAuthentication();
}
else
{
throw new InvalidOperationException(WebResources.InvalidServiceProviderName);
}
}
///
/// Checks if the specified provider user id represents a valid account.
/// If it does, log user in.
///
/// Name of the provider.
/// The provider user id.
/// if set to true create persistent cookie as part of the login.
///
/// true if the login is successful.
///
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "Login is used more consistently in ASP.Net")]
public static bool Login(string providerName, string providerUserId, bool createPersistentCookie)
{
if (HttpContext.Current == null)
{
throw new InvalidOperationException(WebResources.HttpContextNotAvailable);
}
return LoginCore(new HttpContextWrapper(HttpContext.Current), providerName, providerUserId, createPersistentCookie);
}
internal static bool LoginCore(HttpContextBase context, string providerName, string providerUserId, bool createPersistentCookie)
{
var provider = GetOAuthClient(providerName);
var securityManager = new OpenAuthSecurityManager(context, provider, OAuthDataProvider);
return securityManager.Login(providerUserId, createPersistentCookie);
}
internal static bool GetIsAuthenticatedWithOAuthCore(HttpContextBase context)
{
return new OpenAuthSecurityManager(context).IsAuthenticatedWithOpenAuth;
}
///
/// Creates or update the account with the specified provider, provider user id and associate it with the specified user name.
///
/// Name of the provider.
/// The provider user id.
/// The user name.
public static void CreateOrUpdateAccount(string providerName, string providerUserId, string userName)
{
ExtendedMembershipProvider provider = VerifyProvider();
provider.CreateOrUpdateOAuthAccount(providerName, providerUserId, userName);
}
///
/// Gets the registered user name corresponding to the specified provider and provider user id.
///
/// Name of the provider.
/// The provider user id.
///
public static string GetUserName(string providerName, string providerUserId)
{
return OAuthDataProvider.GetUserNameFromOpenAuth(providerName, providerUserId);
}
///
/// Gets all OAuth & OpenID accounts which are associted with the specified user name.
///
/// The user name.
public static ICollection GetAccountsFromUserName(string userName)
{
if (String.IsNullOrEmpty(userName))
{
throw new ArgumentException(
String.Format(CultureInfo.CurrentCulture, WebResources.Argument_Cannot_Be_Null_Or_Empty, "userName"),
"userName");
}
ExtendedMembershipProvider provider = VerifyProvider();
return provider.GetAccountsForUser(userName).Select(p => new OAuthAccount(p.Provider, p.ProviderUserId)).ToList();
}
///
/// Delete the specified OAuth & OpenID account
///
/// Name of the provider.
/// The provider user id.
public static bool DeleteAccount(string providerName, string providerUserId)
{
ExtendedMembershipProvider provider = VerifyProvider();
string username = GetUserName(providerName, providerUserId);
if (String.IsNullOrEmpty(username))
{
// account doesn't exist
return false;
}
provider.DeleteOAuthAccount(providerName, providerName);
return true;
}
internal static IAuthenticationClient GetOAuthClient(string providerName)
{
if (!_authenticationClients.Contains(providerName))
{
throw new ArgumentException(WebResources.ServiceProviderNotFound, "providerName");
}
return _authenticationClients[providerName];
}
internal static bool TryGetOAuthClient(string provider, out IAuthenticationClient client)
{
if (_authenticationClients.Contains(provider))
{
client = _authenticationClients[provider];
return true;
}
else
{
client = null;
return false;
}
}
///
/// for unit tests
///
internal static void ClearProviders()
{
_authenticationClients.Clear();
}
private static ExtendedMembershipProvider VerifyProvider()
{
var provider = Membership.Provider as ExtendedMembershipProvider;
if (provider == null)
{
throw new InvalidOperationException();
}
return provider;
}
}
}