Google Only authentication with ASP.net Identity

Recently I was working on a project where by I needed to authenticate out users against their Google apps login.

That is to say, the on authentication method would be Google. And only ever Google.

Most of the examples I found offered Google as an additional or supplementary authentication mechanism. I wanted it to be the only mechanism to authenticate with our application.

Also, I wanted the users to have ‘roles’ within our application – for example ‘Admin’, ‘CustomerService’ etc…

So, to summarise the requirements:

  • Users will only sign in to our application with their Google Account.
  • They will not have a username / password
  • Once signed in, if they had an account with our application (using ASP.net Identity) authenticate, and continue as normal.
    • If not, create an account (in the sense of ASP.net Identity), then authenticate the user, and continue to our application

For this to work, you’ll need a Google application to authenticate against.

I wrote a short separate post about how to do that here:
http://www.alexjamesbrown.com/blog/development/create-a-google-application-for-authenticating-against-with-asp-net-identity/

Take special note of the Google+ API requirement, and ensure your redirect URL is set appropriately to http://localhost:<port>/signin-google if testing locally

You’ll then need to configure your authentication to use the credentials provided by the above:

app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions
{
    ClientId = “xxx.apps.googleusercontent.com",
    ClientSecret = “xxxxxxxxxxxx",
});

Most of the code in my example follows on from the usual tutorials on the subject of ASP.net Identity with Google –

We have a  ExternalLoginCallback method in our AuthenticationController,  which is called by Google when authenticated.

In here, we retrieve the user information. We then check if a user exists in our database.
If it does, using our  SignInManager, we sign the user in, and proceed.
If we don’t have a user, we create one, with the information we have about the successful login with the third party (in this case, Google)

Apparently, if you don’t want to have the endpoint of /signin-google as shown in the post, you can change >the redirect url value in GoogleOAuth2AuthenticationOptions:

app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
   //clientid client secret goes here
   CallbackPath = “<path>” //put your path here
}

Inside our AuthenticationController is where the real magic happens

I’ve commented what’s going on below;

[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();

//One potentially useful thing we could do here is throw a 403 if the authenticated user
//is not part of our corporate domain
//if(!loginInfo.Email.EndsWith("@mydomain.com")
//    throw new HttpUnauthorizedResult();

//See if the user exists in our database
var user = await UserManager.FindByEmailAsync(loginInfo.Email);

if (user == null)
{
    //user doesn't exist, so the user needs to be created
    user = new ApplicationUser
    {
        UserName = loginInfo.Email,
        Email = loginInfo.Email,
        EmailConfirmed = true,
        FirstName = loginInfo.ExternalIdentity.Claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.GivenName)).Value,
        LastName = loginInfo.ExternalIdentity.Claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.Surname)).Value
    };

    //create the user
    await UserManager.CreateAsync(user);
    //add the google login to the newly created user
    await UserManager.AddLoginAsync(user.Id, loginInfo.Login);
    //add user to roles if required here....
}

//if user logins doesn't contain Google, then add it
if (!user.Logins.Any(x => x.LoginProvider.Equals("Google")))
    await UserManager.AddLoginAsync(user.Id, loginInfo.Login);

//successfully authenticated with google, so sign them in to our app
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

return RedirectToLocal(returnUrl);
}

For a full working example, please checkout:
https://github.com/alexjamesbrown/aspnet-google-only-authentication