Home > database >  IdentityServer4 blocking CORS request to a local api
IdentityServer4 blocking CORS request to a local api

Time:09-14

enter image description here

Config.cs Using an ApiResource here doesn't work as shown in the docs, but using an ApiScope like in the quickstart works fine. The scope exists in the end access token.

public static IEnumerable<IdentityResource> Ids =>
    new IdentityResource[]
    {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
    };

public static IEnumerable<ApiScope> ApiScopes =>
    new ApiScope[]
    {
                new ApiScope(IdentityServerConstants.LocalApi.ScopeName)

    };

public static IEnumerable<Client> Clients =>
    new Client[]
    {
                new Client()
                {
                    ClientName = "Test Angular Client",
                    ClientId = "testclient",
                    AllowedGrantTypes = GrantTypes.Code,
                    AllowedCorsOrigins = { "https://localhost:44459" },
                    RequireConsent = false,
                    AllowAccessTokensViaBrowser = true,
                    RedirectUris = { "https://localhost:44459/authentication/login-callback" },
                    PostLogoutRedirectUris = { "https://localhost:44459/authentication/logout-callback" },
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        IdentityServerConstants.LocalApi.ScopeName,
                        "testapi",
                    },
                    RequirePkce = true,
                    RequireClientSecret = false
                },
    };

In Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddApiVersioning();

    var builder = services.AddIdentityServer()
        .AddInMemoryIdentityResources(Config.Ids)
        .AddInMemoryApiScopes(Config.ApiScopes)
        .AddInMemoryClients(Config.Clients)
        .AddAspNetIdentity<User>();

    builder.AddDeveloperSigningCredential();

    services.AddRazorPages()
        .AddRazorPagesOptions(o =>
        {
            o.Conventions.AddPageRoute("/home/index", "");
        })
        .AddRazorRuntimeCompilation();

    services.AddLocalApiAuthentication();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/api/error");
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();

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

    app.UseEndpoints(e =>
    {
        e.MapRazorPages();
        e.MapControllers();
    });
}

and the controller trying to be called here, which can be called directly from the browser once authenticated (not through the client).

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
[Authorize(LocalApi.PolicyName)]
public class UsersController : ControllerBase
{
    [HttpGet("{userId}/organizations")]
    public async Task<IActionResult> GetOrganizations(Guid userId)
    {
         // do things
    }
}

The Angular client uses the code provided by the enter image description here

EDIT: So, two things here fixed the problem. Firstly, adding in a default CORS policy in ASP.NET Core's default middleware is necessary like the accepted answer. Here is what I added to Startup.cs

services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
        {
            policy.WithOrigins("https://localhost:44459")
            .AllowAnyHeader()
            .AllowAnyMethod();
        });
});

and

app.UseCors();

Secondly, the default angular code from Microsoft doesn't work for my specific use case. In the template the idp, api, and client share the same origin. In my case it's separate so I changed the code to include the access token if the token exists and the request is for the idp. In the future I'll change it to include the api when that time comes.

authorize.interceptor.ts

// snipped code, unchanged from above
private processRequestWithToken(token: string | null, req: HttpRequest<any>, next: HttpHandler) {
    if (!!token && req.url.startsWith("https://localhost:5100")) {
      req = req.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`
        }
      });
    }

    return next.handle(req);
  }
}

The problem was the default template code was not including the access token with the request to the api on the idp. The IdentityServer4 middleware was then checking if the request path belonged to one of the authentication or information endpoints before blocking it.

CodePudding user response:

In general I recommend that you always try to put IdentityServer, the client and the API in separate services, because it is really hard to reason and troubleshoot when you mix them together.

In your case you need to realise that CORS in IdentityServer and ASP.NET Core is configured separately and the CORS settings in IdentityServer is only for IdentityServer and not for ASP.NET core in general.

You need to also configure CORS for ASP.NET Core then see this tutorial:

Hope this helps

  • Related