Search code examples
c#google-oauthasp.net-core-webapiasp.net-core-identity

Google OAuth is showing oauth state was missing or invalid on my ASP.NET Core 7 WEB Api project


I am trying to use IdentityServer on my asp.net core web api project with Google OAuth. So, my service configuration is:

 public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration)
    {
        if (configuration.GetValue<bool>("UseInMemoryDatabase"))
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseInMemoryDatabase("DreamBookDb"));
        }
        else
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
                    builder => builder.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
        }

        services.AddScoped<IApplicationDbContext>(provider => provider.GetRequiredService<ApplicationDbContext>());
        services.AddScoped<ApplicationDbContextInitialiser>();

        services
            .AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddIdentityServer()
            .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = IdentityServerJwtConstants.IdentityServerJwtBearerScheme;
            x.DefaultChallengeScheme = IdentityServerJwtConstants.IdentityServerJwtBearerScheme;
        })
            .AddIdentityServerJwt()
            .AddGoogle(googleOptions =>
        {
            googleOptions.ClientId = configuration["Authentication:Google:ClientId"];
            googleOptions.ClientSecret = configuration["Authentication:Google:ClientSecret"];
            googleOptions.SignInScheme = IdentityServerJwtConstants.IdentityServerJwtBearerScheme;
        });

        return services;
    }

I am using swagger to test it:

static void AddSwaggerServices(IServiceCollection services, IConfiguration configuration)
{
    services.AddSwaggerGen(setup =>
    {
        setup.SwaggerDoc("v1", new OpenApiInfo { Title = "DreamBook", Version = "v1" });
        setup.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
        {
            Type = SecuritySchemeType.OAuth2,
            Flows = new OpenApiOAuthFlows()
            {
                AuthorizationCode = new OpenApiOAuthFlow()
                {
                    AuthorizationUrl = new Uri("https://accounts.google.com/o/oauth2/v2/auth"),
                    TokenUrl = new Uri("https://oauth2.googleapis.com/token")
                }
            }
        });
        setup.AddSecurityRequirement(
               new OpenApiSecurityRequirement
               {
                {
                    new OpenApiSecurityScheme{
                        Reference = new OpenApiReference{
                            Id = "oauth2", //The name of the previously defined security scheme.
                            Type = ReferenceType.SecurityScheme
                        },
                        Type=SecuritySchemeType.OAuth2
                    },
                    new List<string>()
                }
               });
    });
}

So, building configuration like this:

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.OAuthClientId(builder.Configuration["Authentication:Google:ClientId"]);
        c.OAuthClientSecret(builder.Configuration["Authentication:Google:ClientSecret"]);
        c.OAuthScopes("https://www.googleapis.com/auth/userinfo.profile");
        c.OAuth2RedirectUrl("https://localhost:44385/signin-google");
        c.OAuthUsePkce();
    });
    // Initialize and seed database
    using (var scope = app.Services.CreateScope())
    {
        var initialiser = scope.ServiceProvider.GetRequiredService<ApplicationDbContextInitialiser>();
        await initialiser.InitialiseAsync();
        await initialiser.SeedAsync();
    }
}
app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

My project type is Web Api based on Clean Architecture (Jayson Taylor). It doesn't have controller for managing account since account (creating, deleting, sign in, sign up...) will be managed by the identity server (or maybe not. I am not sure here):

project

Now, I am trying testing it with swagger after sign in to Google Account and calling back it is showing me error oauth state was missing or invalid: error

I am not sure, what I did wrong here. Also, the same problem is happening with PostMan

I tested my client Id and client secret with Asp.Net Core Web App using induvidual accounts authentication and it is working fine. So, what I am doing wrong here and how to implement Google Oauth in asp.net core Web Api project with Identity server?


Solution

  • So, it looks like in my case request should be sent from my api. So I created auth controller and put google login action:

    [HttpGet("google")]
    public IActionResult Login()
    {
        var redirectUrl = Url.Action(nameof(GoogleCallBack), "Auth");
        var properties = _signInManager.ConfigureExternalAuthenticationProperties(GoogleDefaults.AuthenticationScheme, redirectUrl);
        return Challenge(properties, GoogleDefaults.AuthenticationScheme);
    }
    

    This will redirect to authenticate with google, after finishing it will be back on my call back action:

    [AllowAnonymous]
    [HttpGet("google-callBack")]
    public async Task<IActionResult> GoogleCallBack()
    {
        return Ok(await _authService.SignInWithGoogleAsync());
    }
    

    This action will create a JWT token for Bearer Auth which I will use it to auth. I added another auth for my swagger as well. My swagger configuration looks like this:

     static void AddSwaggerServices(IServiceCollection services, IConfiguration configuration)
        {
            services.AddSwaggerGen(setup =>
            {
                setup.SwaggerDoc("v1", new OpenApiInfo { Title = "DreamBook", Version = "v1" });
                setup.AddSecurityDefinition("OAuth", new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.OAuth2,
                    Flows = new OpenApiOAuthFlows()
                    {
                        Implicit = new OpenApiOAuthFlow()
                        {
                            AuthorizationUrl = new Uri($"https://localhost:44385/api/Auth/google"),
                        }
                    }
                });
                setup.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Description = "JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345abcdef'",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey,
                    Scheme = "Bearer"
                });
                var securityRequirement = new OpenApiSecurityRequirement();
                var securityScheme = new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference
                    {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Bearer"
                    },
                    Scheme = "Bearer",
                    Name = "Bearer",
                    In = ParameterLocation.Header,
    
                };
                securityRequirement.Add(securityScheme, new List<string>());
                setup.AddSecurityRequirement(securityRequirement);
            });
        }