Search code examples
c#asp.netoauth-2.0google-oauth

Asp .Net5 with Google Oauth2 Redirection Error


I have a problem with implementation of google oauth2. I get a redirection missmatch.

I have in google console https://localhost:4200/signin-google.

My confusion is this. If I do https://localhost:4200/api/google/response in google console and in options.CallbackPath. Then I get succesfuly redirect of the challenge but the /api/google/response never gets trigger during the google's middleware authentication.(I have various consoles that will not print). It also is succesful cause I get a cookie with my google infos like name , email etc.

A lot of tutorials just set google console to https://localhost:4200/signin-google. They never set the options.CallbackPath or any http:localhost/signin-google route and they play succesfully while I'm getting Error 400: redirect_uri_mismatch (which is correct because my application does not have https://localhost:4200/signin-google).

Should I go back to https://localhost:4200/api/google/response on both google console and set CallbackPath to /api/google/response and see why it does not trigger?

Should I add route to /singin-google on my application?

Why in many tutorials without routing and CallbackPath work while in my case just do not?

Thank you in advance.

The startup.cs

 public void ConfigureServices(IServiceCollection services)
 {
   // configurations
   services.Configure<EnvironmentVariables>(Configuration.GetSection(EnvironmentVariables.EnvironmentVariable));
   services.Configure<RabbitMQSettingsOptions>(Configuration.
    GetSection(RabbitMQSettingsOptions.RabbitMQSettings));

   services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
   .AddCookie(options =>
   {
     this.SetUpCookieAuthentication(options);
   });

   services.AddAuthentication().AddGoogle(options =>
   {
     this.SetUpGoogleAuthentication(options);
   });

   services.AddCors(options =>
   {
     this.SetUpCorsPolicy(options);
   });

  ...
 }
private void SetUpGoogleAuthentication(GoogleOptions options)
{
  ...
  // set GoogleOptions
  //options.CallbackPath = new PathString(path);
  options.ClientSecret = googleAuthentication.ClientSecret;
  options.ClientId = googleAuthentication.ClientId;
  
}

  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  {
    if (env.IsDevelopment())
    {
      app.UseDeveloperExceptionPage();
    }
    //app.UseHttpsRedirection();

    app.UseRouting();

    app.UseCors("CorsPolicy");
    // app.UseSession();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseCookiePolicy();

    app.UseEndpoints(endpoints =>
    {
      endpoints.MapControllers();
    });
  }

GoogleController.cs

using AutoMapper.Internal;
using Domain.DDtos.Users;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using ServiceLayer.Dtos.Users;
using ServiceLayer.Interfaces;
using ServiceLayer.Services;
using ServiceLayer.Types;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace Eshop.Controllers
{
  [Route("api/google")]
  [ApiController]
  public class GoogleController : ControllerBase
  {
    private IOptions<EnvironmentVariables> _envOptions;
    private readonly IEnvironmentVariableService _envService;
    private readonly IUserService _userService;

    public GoogleController(IOptions<EnvironmentVariables> envOptions,
      IEnvironmentVariableService envService, IUserService userService)
    {
      _envOptions = envOptions;
      _envService = envService;
      _userService = userService;
    }

    [HttpGet("signin")]
    public async Task<ActionResult> GetGoogleSignInAsync()
    {
      var googleAuthenticationFile = await _envService.GetEnvironmentVariableValueAsync(_envOptions.Value.GoogleAuthentication);
      if (googleAuthenticationFile == null)
        throw new Exception("Could not find the actual google authentication file.");

      var googleAuthentication = JsonConvert.DeserializeObject<OAuth2Credentials>(googleAuthenticationFile);
      var redirectUri = googleAuthentication.RedirectUris.FirstOrDefault();
      var properties = new AuthenticationProperties
      {
        RedirectUri = Url.Action("GoogleResponseAsync")
      };
      return Challenge(properties, GoogleDefaults.AuthenticationScheme);
    }

    [HttpGet("response")]
    public async Task<ActionResult> GoogleResponseAsync()
    {
      Console.WriteLine("in response");
      var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
      if (!result.Succeeded)
        return Unauthorized();
      Debug.WriteLine(JsonConvert.SerializeObject(result));
      var userLinkDto = new UserLinkDto();
      var claims = result.Principal.Identities.FirstOrDefault().Claims.Select(claim => new
      {
        claim.Issuer,
        claim.OriginalIssuer,
        claim.Type,
        claim.Value,
      });
      claims.ForAll(claim =>
      {
        var claimType = Constants.GoogleClaimsDict.GetValueOrDefault(claim.Type);
        var dtoProp = claimType != null ? userLinkDto.GetType().GetProperty(claimType) : null;
        if (dtoProp != null)
          dtoProp.SetValue(userLinkDto, claim.Value);
      });
      return Ok();
    }


Solution

  • Okay the problem was splited into 2 parts. The first one is that I needed to wait hours before google console accept changes of authorized redirect urls to https://localhost:4200/signin-google. That's why I was getting redirection missmatch.

    The second problem that appeared after the above fix, which it was an infinite times of /api/google/signin requesting to google was because of this part

    RedirectUri = Url.Action("GoogleResponseAsync")

    In actions which ends in Async I needed to give the action without the Async part, otherwise I dont get a valid url.

    Fix:

    RedirectUri = Url.Action("GoogleResponse")

    link of the bug: https://github.com/dotnet/aspnetcore/issues/14606

    That's why I had many problems following the tutorials without CallbackPath and I was getting weird infinity requests. Also the final url that you want to run after access token is created, must be set in the challenge and not in your google console -> authorized_redirect_url or your CallbackPath because CallbackPath is for internal use.