Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
666 views
in Technique[技术] by (71.8m points)

authorization - Getting 401 from browser but 200 from postman with Asp.net core and Identityserver 4

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:

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To help you out debugging you should configure your logs and show the debug output from them. In IdentityServer solutions you typically control that in the program.cs class, like this example below taken from here:

Just set the log level to Debug to see more insights in your operation.

  Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
            .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
            .MinimumLevel.Override("System", LogEventLevel.Warning)
            .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
            .Enrich.FromLogContext()
            // uncomment to write to Azure diagnostics stream
            //.WriteTo.File(
            //    @"D:homeLogFilesApplicationidentityserver.txt",
            //    fileSizeLimitBytes: 1_000_000,
            //    rollOnFileSizeLimit: true,
            //    shared: true,
            //    flushToDiskInterval: TimeSpan.FromSeconds(1))
            .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code)
            .CreateLogger();

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...