In an era of Social Media, the web has developed so much and so the authentication systems to access services on the web. People nowadays are more comfortable using their social media identities or any other identities from providers like Gmail, Microsoft, etc. to login to various other services across the web. This is generally called as Single Sign-on. Using these types of identity providers to login to an external services has benefits to users as wells the service providers. Users need not maintain or remember separate username/passwords of different services. Nowadays, almost everyone has membership in either social media platforms or Gmail or Microsoft or any other Identity providers (OpenID, etc.). On the other hand, when a service provider is allowing users to use a third party login then it relieves them securing user details and they are tend to get more customers to use their service. Moving forward, let us see how to integrate GMAIL authentication using OAuth 2.0 in our Asp.Net MVC 5.0 application. Read here to know more about OAuth.
Note – This article uses Asp.Net MVC 5.0 without OWIN and Asp.Net Identity. We will see how to integrate these external identity providers in another article.
OAuth Integration Components
All the providers including Google maintains a developer site to help us integrate their OAuth service into our application. In this article, let us use a commonly used component called DotnetOpenAuth to integrate Google authentication into our Asp.Net MVC application. Visual Studio project templates by default include the DotNetOpenAuth Nuget packages and a default AccountController implementation to start using the common OAuth providers like Google, FaceBook, Microsoft and Twitter. You need to select “Internet Application” or “Individual User Accounts” as project type when creating the project to get this default implementation. So, using the starter project will give you a good start to integrating these authentication providers. For many reasons, the project templates default implementation will not work and it is not complete since there were many changes on the provider side now. Also, the DotnetOpenAuth package that is included will not work for Google and requires a different package which we will be using in this article.
Just to understand clearly, we will use an Empty project template and include the Nuget packages ourselves to integrate Google authentication in this article.
Before moving into the implementation, let’s understand the application flow to integrate OAuth authentication.
Authentication Flow
-
Users should be redirected to provider to authenticate using provider userid and password. Here, it is Gmail userid and password.
-
On successful authentication, the provider will present a consent screen to allow the third party application (or service provider or relying party) to access the basic user information from the provider. This is one time process, if the user allows access first time then subsequent request will by-pass this step.
-
The provider will call the landing page or redirect page on the client application by posting access token and some default public user details for application to use.
-
The application will create a local authentication ticket (a forms authentication ticket) using the access token and user details the provider passes.
Pre-Requisite
Below things are required to integrate OAuth authentication into our applications. This is a common step for every OAuth provider.
-
Create OAuth Application on provider (Google). Once done, we will get a ClientId and ClientSecret key for using in our application.
-
A Landing page or Redirect page on the application which the provider uses to redirect users after successful authentication. This is where we will authenticate users locally.
Creating OAuth Application
-
Go to https://console.developers.google.com. Sign-in with your gmail id.
-
You will be presented with Google API Manager Dashboard. Click Credentials on the left navigation.
-
Click Create Credentials > OAuth ClientID.
-
Select “Web application” Application type. Type a name for your application. Authorized Javascript Origins is your application root domain. Authorized redirect URIs is the landing page url on your application. You can add multiple urls here. Click Create.
This will create your application and will present the ClientId and Secret in a pop-up. Note down this to configure in the application.
Note – You can also access/edit this details from “Credentials” tab. All created application will be listed here for managing it.
Now, let’s create an Asp.Net MVC 5.0 project and integrate Google authentication using the above details.
Creating Visual Studio Project
-
Open Visual Studio (2012/2015) and create a new Asp.Net MVC project. Select Empty project template to add the required Nuget package ourselves. I have used “Asp.Net MVC 5 Empty Project”
-
Add the below Nuget packages into your solution.
-
DotNetOpenAuth.GoogleOAuth2 (The one that comes with default template does not support OAuth 2.0)
-
Microsoft.AspNet.Membership.OpenAuth
-
Microsoft.AspNet.Providers.LocalDB
-
Note – For simplicity, I will use Microsoft.AspNet.Providers.LocalDB Asp.Net Universal Provider to create and persist user in localDb under App_data. You can call your User data access code to create user once the provider calls the landing page with user details.
-
Add a class file called AuthConfig.cs under App_Start Folder. Let’s configure and add the Google OAuth 2.0 client into the OpenAuth AuthenticationClients collection. This is where we will put the clientId and client secret to connect Google OAuth 2.0 API.
public static class AuthConfig
{
public static void RegisterAuth()
{
GoogleOAuth2Client clientGoog = new GoogleOAuth2Client("[Your ClientId]", "[Secret key]");
IDictionary<string, string> extraData = new Dictionary<string, string>();
OpenAuth.AuthenticationClients.Add("google", () => clientGoog, extraData);
}
}
Include DotNetOpenAuth.GoogleOAuth2 and Microsoft.AspNet.Membership.OpenAuth namespace in using section for the above code to work.
Note – You actually multiple Authentication clients to this collection like Twitter,FaceBook, etc and access it across the application. For this article, I have added only Google client for demonstration.
-
Call this method from Global.asax Application_Start event.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
AuthConfig.RegisterAuth();
}
-
Let’s add AccountController into Controller folder and add Login methods. For simplicity, let’s not add local registration and FormsAuthentication local login process here. We will just add a “Login Using Google” button in the login page. So the final page looks like,
Login view
<h3>Login Using External Provider</h3>
@Html.ActionLink("Login Using Google", "RedirectToGoogle")
Note – I have added a simple Layout page with simple header text “Google OAuth Demo”.
-
Add an action method to redirect user to Google login URL on click of the above “Login Using Google” link.
public ActionResult RedirectToGoogle()
{
string provider = "google";
string returnUrl = "";
return new ExternalLoginResult(provider, Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
}
internal class ExternalLoginResult : ActionResult
{
public ExternalLoginResult(string provider, string returnUrl)
{
Provider = provider;
ReturnUrl = returnUrl;
}
public string Provider { get; private set; }
public string ReturnUrl { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
OpenAuth.RequestAuthentication(Provider, ReturnUrl);
}
}
When user clicks the link, he or she will be redirected to Google login page by specifying “/Account/ExternalLoginCallback” as the redirect URL. This URL should match the URL we saved when creating OAuth application on Google developer console.
-
Next, let’s add the landing page or redirect URL action method “ExternalLoginCallback” to get back access token and user details Google is forwarding.
[AllowAnonymous]
public ActionResult ExternalLoginCallback(string returnUrl)
{
string ProviderName = OpenAuth.GetProviderNameFromCurrentRequest();
if (ProviderName == null || ProviderName == "")
{
NameValueCollection nvs = Request.QueryString;
if (nvs.Count > 0)
{
if (nvs["state"] != null)
{
NameValueCollection provideritem = HttpUtility.ParseQueryString(nvs["state"]);
if (provideritem["__provider__"] != null)
{
ProviderName = provideritem["__provider__"];
}
}
}
}
GoogleOAuth2Client.RewriteRequest();
var redirectUrl = Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl });
var retUrl = returnUrl;
var authResult = OpenAuth.VerifyAuthentication(redirectUrl);
if (!authResult.IsSuccessful)
{
return Redirect(Url.Action("Account", "Login"));
}
// User has logged in with provider successfully
// Check if user is already registered locally
//You can call you user data access method to check and create users based on your model
if (OpenAuth.Login(authResult.Provider, authResult.ProviderUserId, createPersistentCookie: false))
{
return Redirect(Url.Action("Index","Home"));
}
//Get provider user details
string ProviderUserId = authResult.ProviderUserId;
string ProviderUserName = authResult.UserName;
string Email = null;
if (Email == null && authResult.ExtraData.ContainsKey("email"))
{
Email = authResult.ExtraData["email"];
}
if (User.Identity.IsAuthenticated)
{
// User is already authenticated, add the external login and redirect to return url
OpenAuth.AddAccountToExistingUser(ProviderName, ProviderUserId, ProviderUserName, User.Identity.Name);
return Redirect(Url.Action("Index", "Home"));
}
else
{
// User is new, save email as username
string membershipUserName = Email ?? ProviderUserId;
var createResult = OpenAuth.CreateUser(ProviderName, ProviderUserId, ProviderUserName, membershipUserName);
if (!createResult.IsSuccessful)
{
ViewBag.Message = "User cannot be created";
return View();
}
else
{
// User created
if (OpenAuth.Login(ProviderName, ProviderUserId, createPersistentCookie: false))
{
return Redirect(Url.Action("Index", "Home"));
}
}
}
return View();
}
The above code will create user and login locally. On successful login, the user will be created by the provider in localDb under App_Data and it will redirect the user to Home page.
Note – I have used default membership database to store the user details here. Once you got the user details from google you can call your data access method to store into your application user table and call FormsAuthentication.SetAuthCookie() to authenticate user.
-
Finally, we will add LogOff action to logoff user.
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
//Call https://www.google.com/accounts/Logout if you want to logoff at provider
return Redirect(Url.Action("Index","Home"));
}
-
That’s it. Press F5 and see it in action.
On clicking “Login Using Google”, we will be redirected to Google login page. After successful login, we will be presented with user consent screen similar to below,
Click “Allow” we will be redirected to our application landing page. This would have created user details in localDb database created under App_Data folder. You can view the table using server explorer in Visual Studio.
Download the source attached with this article and see it in action.
I have also included a WebForms implementation which fairly follows the same approach but uses the default project template implementation. Make sure to update the redirect URL on the Google console appropriately before running the application.