Search code examples
c#asp.net-coreauthenticationasp.net-web-apiopenid-connect

OIDC Flow in ASP.NET Core 7.0 Web API against Google


I want to authenticate users in my Web API using the OIDC flow and Google as the ID provider.

In a nutshell, my application is composed of multiple microservices where each is a Web API. The authNZ to the REST endpoints in all the services is through JWT. I have one identity microservice that I want it to implement the OIDC flow, particularly implementing the following three REST endpoints.

  • login that returns a Challenge (or its URL);
  • logout endpoint.
  • callback that is called by Google and should extract user information from the OIDC code (including ID and Access tokens);

Most Microsoft templates for AuthNZ are either mostly built with UI elements or leverage third-party libraries such as Duende, which I cannot use.

I can redirect to Google using the Singin endpoint, though code is null when Google call's back the redirect URI. So, I am not sure what is missing in my configuration.

// Register services
services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = GoogleDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
.AddGoogle(options =>
{
    options.ClientId = "...";
    options.ClientSecret = "...";
});

// Configure App
app.UseAuthentication();
app.UseAuthorization();

The controller.

[Route("api/v1/[controller]/[action]")]
[ApiController]
[Authorize]
public class IdentityController : ControllerBase
{
    [AllowAnonymous]
    [HttpGet]
    public IActionResult SignIn()
    {
        return new ChallengeResult(
            "Google",
            new AuthenticationProperties
            {
                IsPersistent = true,
                RedirectUri = Url.Action("callback", "Identity") 
            });
    }

    [AllowAnonymous]
    [HttpGet(Name = "callback")]
    public async Task<IActionResult> Callback(object code = null)
    {
        // code is null here.
    }
}

Solution

  • The Callback endpoint should be implemented like the following.

    [AllowAnonymous]
    [HttpGet(Name = "callback")]
    public async Task<IActionResult> callback()
    {
        var authResult = await HttpContext.AuthenticateAsync(
            GoogleDefaults.AuthenticationScheme);
        var claims = authResult.Principal.Identities
            .FirstOrDefault().Claims.Select(claim => new
            {
                claim.Issuer,
                claim.OriginalIssuer,
                claim.Type,
                claim.Value
            });
        return Content(claims.ToString());
    }
    

    I have been using cookies to get the authentication results, as suggested by some blogs, as the following, though that did not work for me.

    // An incorrect method of getting 
    // authentication results in my use-case.
    var authResult = await HttpContext.AuthenticateAsync(
        CookieAuthenticationDefaults.AuthenticationScheme);