I tried everything and I keep getting 401 error when I try to reach my api from a react App.
My Api is a asp.net core protected with Identity server.
From Postman, I can request an access token and call api and it works fine.
From the the browser, I can get a valid access token, but any api calls failed with 401.
The exact request that failed in the browser, works fine on postman (copy as cUrl from dev tool, then import in postman).
I'Ve tried both http and https, I'Ve check the order of element in startup, I'Ve configured the CORS...nothing.
I'm using the same web app both as Identity Server and Api server.
This is my startup file:
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Pesabooks.Api.Common;
using Pesabooks.Application;
using Pesabooks.Application.Common.Interfaces;
using Pesabooks.Domain.Session;
using Pesabooks.Infrastructure;
using System.Threading.Tasks;
namespace Pesabooks.Api
{
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment environment)
{
Configuration = configuration;
Environment = environment;
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; }
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://localhost:3000").AllowAnyMethod().AllowAnyHeader();
});
});
services.AddControllersWithViews()
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<IPesabooksDbContext>()); ;
services.AddApplication();
services.AddInfrastructure(Configuration, Environment);
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie("Cookies", options =>
{
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax;
});
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "http://localhost:5000";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
};
//options.Audience = "pesabooks";
options.RequireHttpsMetadata = false;
});
services.ConfigureApplicationCookie(options =>
{
options.Events.OnRedirectToLogin = context =>
{
context.Response.Headers["Location"] = context.RedirectUri;
context.Response.StatusCode = 401;
return Task.CompletedTask;
};
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Pesabooks.Api", Version = "v1" });
c.OperationFilter<AddRequiredHeaderParameter>();
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCookiePolicy(new CookiePolicyOptions { MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.Strict });
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Pesabooks.Api v1"));
}
app.UseCustomExceptionHandler();
// app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthentication();
app.UseAuthorization();
app.UseIdentityServer();
app.UseTenantMiddleware();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
endpoints.MapControllers();
});
}
}
}
this is my identityserver client settings:
{
"IdentityServer": {
"Clients": [
{
"ClientId": "Pesabooks.WebApp",
"ClientName": "Pesabooks Web App",
"AllowedGrantTypes": [ "implicit" ],
"AllowedScopes": [ "openid", "profile", "api" ],
"RequireClientSecret": false,
"RedirectUris": [ "http://localhost:3000/signin-oidc" ],
"PostLogoutRedirectUris": [ "http://localhost:3000/signout-oidc" ],
"AllowedCorsOrigins": [ "http://localhost:3000" ],
"AllowAccessTokensViaBrowser": true,
"Enabled": true
},
{
"ClientName": "Pesabooks.IntegrationTests",
"ClientId": "Pesabooks.IntegrationTests",
"AllowedGrantTypes": [ "password" ],
"AllowedScopes": [ "openid", "profile" ],
"ClientSecrets": [ { "Value": "*****" } ],
"Enabled": true
},
{
"ClientName": "Postman",
"ClientId": "postman",
"RequirePkce": false,
"AllowedGrantTypes": [ "authorization_code" ],
"AllowOfflineAccess": true,
"IdentityTokenLifetime": 86400,
"AccessTokenLifetime": 86400,
"RedirectUris": [ "https://www.getpostman.com/oauth2/callback" ],
"PostLogoutRedirectUris": [ "https://www.getpostman.com" ],
"AllowedCorsOrigins": [ "https://www.getpostman.com" ],
"AllowedScopes": [ "openid", "profile", "email", "api" ],
"ClientSecrets": [ { "Value": "*****" } ],
"AllowAccessTokensViaBrowser": true,
"RequireConsent": false,
"EnableLocalLogin": true,
"Enabled": true
}
]
}
}
This is an example of a request that fail in the browser but works in postman:
curl 'http://localhost:5000/Account/Login?ReturnUrl=%2Fapi%2FAccounts%3FOnlySavings%3Dfalse%26IncludeDeactivated%3Dfalse'
-H 'Connection: keep-alive'
-H 'Accept: application/json'
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjdBQjI4QUQzNEQ3OUMwOTNCMkY2NjZEQUU5MTYwQzczIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2MTE1MDA4NDgsImV4cCI6MTYxMTUwNDQ0OCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwL3Jlc291cmNlcyIsImNsaWVudF9pZCI6IlBlc2Fib29rcy5XZWJBcHAiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNjExNTAwODE4LCJpZHAiOiJsb2NhbCIsImp0aSI6IkYzRkE5MUIxRUM0MzIyNkRBNEZCNDU1QjQ2MTE1MzI5Iiwic2lkIjoiQTE3RDVDQjY3QTIxMDc0MjE4QzgyNTE1RjI5QjdGQUYiLCJpYXQiOjE2MTE1MDA4NDgsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJhcGkiXSwiYW1yIjpbInB3ZCJdfQ.gGmUUmZNZLjhyY47diPXQHGXO0k1B8mss8FIdQuecFmp-SxRFls6nF7fU3bqQdQcadpIrZEjDGG2wth8IZIqg82K5L4jr9Nt8Rp8JMdvC-zn7TbHKV670QSYF6NIq5AKWNMi86oNfk9QYEIjM7p5DWjzSjH5LAmINQjwCNG0QhXccSqbUkmVxxsYQkCOfZnlsshrswP41ocNs1s_363v070Tc6ayrxBSYiqM5AbLs_5y4iSXgP1TzWdwxcavBng-vEA0lp5hU0ZNggmBid_GOrgKRfHNzv8518RfHtpa1VwptdkRMiba18t-HNezMw7WMh432Q6RxRvJGW6iq7S8eg'
-H 'psbk-tenant: 5'
-H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36'
-H 'Sec-GPC: 1'
-H 'Origin: http://localhost:3000'
-H 'Sec-Fetch-Site: same-site'
-H 'Sec-Fetch-Mode: cors' `
-H 'Sec-Fetch-Dest: empty'
-H 'Referer: http://localhost:3000/'
-H 'Accept-Language: en-US,en;q=0.9'
--compressed
This is what I got in the logs:
[11:28:48 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
AuthenticationScheme: Identity.Application was not authenticated.
[11:28:48 Debug] IdentityServer4.Hosting.CorsPolicyProvider
CORS request made for path: /.well-known/openid-configuration from origin: http://localhost:3000
[11:28:48 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
AuthenticationScheme: Identity.Application was not authenticated.
[11:28:48 Debug] IdentityServer4.Services.InMemoryCorsPolicyService
Client list checked and origin: http://localhost:3000 is allowed
[11:28:48 Information] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
AuthenticationScheme: Identity.Application was challenged.
[11:28:48 Debug] IdentityServer4.Hosting.CorsPolicyProvider
CorsPolicyService allowed origin: http://localhost:3000
[11:28:48 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
AuthenticationScheme: Identity.Application was not authenticated.
[11:28:48 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
AuthenticationScheme: Identity.Application was not authenticated.
[11:28:48 Debug] IdentityServer4.Hosting.EndpointRouter
Request path /.well-known/openid-configuration matched to endpoint type Discovery
[11:28:48 Debug] IdentityServer4.Hosting.EndpointRouter
Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryEndpoint
[11:28:48 Information] IdentityServer4.Hosting.IdentityServerMiddleware
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration
[11:28:48 Debug] IdentityServer4.Endpoints.DiscoveryEndpoint
Start discovery request
[11:28:48 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
AuthenticationScheme: Identity.Application was successfully authenticated.
[11:28:48 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
AuthenticationScheme: Identity.Application was successfully authenticated.
[11:28:48 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler
AuthenticationScheme: Identity.Application was successfully authenticated.
[11:28:48 Debug] IdentityServer4.Hosting.EndpointRouter
Request path /connect/checksession matched to endpoint type Checksession
[11: