Search code examples
c#asp.netauthenticationasp.net-coregoogle-oauth

How best to implement Google social sign-in authentication in ASP.NET Core?


I want to implement an authentication system in ASP .NET Core where:

  1. The user clicks a button which looks like the standard Google sign-in button.

  2. The user is then prompted to sign in to Google Accounts and signs in.

  3. A http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier claim with the value equal to that of the user_id of the signed-in Google account is added to the User variable in the RazorBasePage class.

  4. The server adds the user to a user table with user_id as the primary key.

I originally investigated a solution using the built-in ASP .NET Identity system. However, I soon realised it was far more functionality than what I needed.

Next, I followed this article to implement a system where the user must authenticate with their Google account when attempting to use controllers or actions tagged with the [Authorize] attribute.

Meanwhile, I also investigated a login system using this article. This article implements a system where developer can implement their own custom authorisation system, e.g. check against a hard-coded password.

And I also investigated some of Google's developer pages on identity This system allows the developer to easily implement an authentication system on the client side - additional steps are required to pass the authentication to the server.

This collection of images should help to communicate the aforementioned authorisation systems. My current ConfigureServices method in StartUp.cs contains the following code:

services.AddAuthentication(options => {
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
    .AddCookie()
    .AddGoogle(options => {
        options.ClientId = Configuration["Authentication:Google:ClientId"];
        options.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
        options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.SaveTokens = true;
    });

Any tips on how to implement such a system would be greatly appreciated. Thanks!


Solution

  • Looks like Google deprecated use of Google+ for retrieving user information: https://github.com/aspnet/AspNetCore/issues/6486

    In ASP.Net Core MVC 2.0 I ended up doing this in Startup.cs:ConfigureServices

                services.AddAuthentication(options => {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
            })
                .AddCookie()
                .AddGoogle(options => {
                    options.ClientId = Configuration["Authentication:Google:ClientId"];
                    options.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
                    options.SaveTokens = true;
                    options.UserInformationEndpoint = "https://www.googleapis.com/oauth2/v2/userinfo";
                    options.ClaimActions.Clear();
                    options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
                    options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
                    options.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
                    options.ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
                    options.ClaimActions.MapJsonKey("urn:google:profile", "link");
                    options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
                    options.ClaimActions.MapJsonKey("picture", "picture");
                })
                ;
    

    Don't forget to add the following line before app.UseMvc()

    app.UseAuthentication();
    

    Also, you will need to configure Google API for cloud identity for your app.

    To display the information you can do something like:

    @Context.User.Identity.Name
    <img src="@Context.User.Claims.SingleOrDefault(c => c.Type == "picture")?.Value" />
    

    Finally: Please consider privacy of this information. It's not ethical (and in most jurisdictions not legal) to store private information without telling the user that you are storing it and for what purpose.