Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google authentication in ASP.NET Core without ASP.NET Identity

Is it any way to implement authentication via Google in ASP.Net core without ASP.NET Identity? I have implemented authentication based on cookies like this:

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?tabs=aspnetcore2x

And I need to add Google authentication to project.

like image 793
michals Avatar asked Sep 07 '25 02:09

michals


2 Answers

Yes of course. Here is a summary of one way you could go about this.

You can call the OAuth2 authentication server APIs directly, and write custom attributes / handlers / filters in .NET to integrate with that functionality and control access to your resources accordingly.

https://developers.google.com/identity/protocols/OAuth2WebServer

I believe the current endpoints that Google provide for their explicit grant / server authentication flow are as follows:

  • Login: https://accounts.google.com/o/oauth2/v2/auth
  • Token: https://www.googleapis.com/oauth2/v4/token
  • Details: https://www.googleapis.com/plus/v1/people/me

You can discover more details about the specific HTTP request and responses in the above link, but that's the general gist of OAuth2 for web server, as I understand it :)

Back in your core application you can then write custom handler / filter / attribute code to handle the authentication and redirection. Some example .NET core code can be found here:

https://ignas.me/tech/custom-authentication-asp-net-core-20/

like image 124
ne1410s Avatar answered Sep 09 '25 17:09

ne1410s


I wanted to know the same thing and couldn't find any answers on the web, so I cloned the Microsoft.AspNetCore.Security (v2.1) repo to see if I could figure out how it worked. I believe this is what you are looking for. Add this to your ConfigureServices method in the Startup.cs file.

      services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(o => o.LoginPath = new PathString("/login"))
            // You must first create an app with Google and add its ID and Secret to your user-secrets.
            // https://console.developers.google.com/project
            .AddGoogle(o =>
            {
                o.ClientId = "YOUR_GOOGLE_CLIENT_ID";
                o.ClientSecret = "YOUR_GOOGLE_CLIENT_SECRET";
                o.AuthorizationEndpoint += "?prompt=consent"; // Hack so we always get a refresh token, it only comes on the first authorization response
                o.AccessType = "offline";
                o.SaveTokens = true;
                o.Events = new OAuthEvents()
                {
                    // There are a number of (optional) events you may want to connect into and add your own claims to the user, or handle a remote failure, etc.
                    OnRemoteFailure = HandleOnRemoteFailure,
                    OnCreatingTicket = HandleOnCreatingTicket
                };
                o.ClaimActions.MapJsonSubKey("urn:google:image", "image", "url");
            });

If you want to intercept some of the events to add additional claims to a user, or store them off in a database to apply app level authorization, you can connect in to various OAuthEvents. I placed the following methods at the bottom of the Startup.cs file, inside the Startup Class.

    private async Task HandleOnCreatingTicket(OAuthCreatingTicketContext context)
    {
        var user = context.Identity;
        // please use better logic than GivenName. Demonstration purposes only.
        if(user.Claims.FirstOrDefault(m=>m.Type==ClaimTypes.GivenName).Value == "MY_FAVORITE_USER")
        {
            user.AddClaim(new Claim(ClaimTypes.Role, "Administrator"));
        }

        await Task.CompletedTask;
    }

    private async Task HandleOnRemoteFailure(RemoteFailureContext context)
    {
        // add your logic here.
        await Task.CompletedTask;
    }

Finally, you will need to add a couple of controller actions. I put mine in an AccountController.cs file like this:

public class AccountController : Controller
{
    [AllowAnonymous]
    [Route("/login")]
    public async Task<IActionResult> Login()
    {
        if (User == null || !User.Identities.Any(identity => identity.IsAuthenticated))
        {

            // By default the client will be redirect back to the URL that issued the challenge (/login?authtype=foo),
            // send them to the home page instead (/).
            string returnUrl = HttpContext.Request.Query["ReturnUrl"];
            returnUrl = string.IsNullOrEmpty(returnUrl) ? "/" : returnUrl;
            await HttpContext.ChallengeAsync("Google", new AuthenticationProperties() { RedirectUri = returnUrl });
        }

        return View();
    }

    [Authorize]
    [Route("/logout")]
    public async Task<IActionResult> Logout()
    {
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties() { RedirectUri="/" });

        return View();
    }
}

I also put a couple of simple views so that once logged-out, a user could click a link to return to home, or to login again or as another user, etc.

This seemed to work for me.

like image 40
mobiletonster Avatar answered Sep 09 '25 17:09

mobiletonster