32

We have an ASP.NET MVC application that is authenticating without issue against IdentityServer3, however the web API part of the application using ApiController's start to fail if the user waits before proceeding with AJAX functionality after about 3 minutes (before 3 mins everything seems fine).

The errors seen in Chrome are:

XMLHttpRequest cannot load https://test-auth.myauthapp.com/auth/connect/authorize?client_id=ecan-farmda…gwLTk5ZjMtN2QxZjUyMjgxNGE4MDg2NjFhZTAtOTEzNi00MDE3LTkzNGQtNTc5ODAzZTE1Mzgw. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://test.myapp.com' is therefore not allowed access.

On IE I get the following errors:

SCRIPT7002: XMLHttpRequest: Network Error 0x4c7, The operation was canceled by the user.

Looking at IdentityServer3's logs I'm seeing entries like so:

2015-08-10 16:42 [Warning] (Thinktecture.IdentityServer.Core.Configuration.Hosting.CorsPolicyProvider) CORS request made for path: /connect/authorize from origin: http://test.myapp.com but rejected because invalid CORS path

In the IdentityServer3 web application I'm giving clients AllowedCorsOrigins:

Thinktecture.IdentityServer.Core.Models.Client client = new Thinktecture.IdentityServer.Core.Models.Client()
{
    Enabled = configClient.Enabled,
    ClientId = configClient.Id,
    ClientName = configClient.Name,
    RedirectUris = new List<string>(),
    PostLogoutRedirectUris = new List<string>(),
    AllowedCorsOrigins = new List<string>(),
    RequireConsent = false, // Don't show consents screen to user
    RefreshTokenExpiration = Thinktecture.IdentityServer.Core.Models.TokenExpiration.Sliding
};

foreach (Configuration.RegisteredUri uri in configClient.RedirectUris)
{
    client.RedirectUris.Add(uri.Uri);
}

foreach (Configuration.RegisteredUri uri in configClient.PostLogoutRedirectUris)
{
    client.PostLogoutRedirectUris.Add(uri.Uri);
}

// Quick hack to try and get CORS working
client.AllowedCorsOrigins.Add("http://test.myapp.com");
client.AllowedCorsOrigins.Add("http://test.myapp.com/"); // Don't think trailing / needed, but added just in case

clients.Add(client);

And when registering the service I add a InMemoryCorsPolicyService:

app.Map("/auth", idsrvApp =>
{
    var factory = new IdentityServerServiceFactory();

    factory.Register(new Registration<AuthContext>(resolver => AuthObjects.AuthContext));
    factory.Register(new Registration<AuthUserStore>());
    factory.Register(new Registration<AuthRoleStore>());
    factory.Register(new Registration<AuthUserManager>());
    factory.Register(new Registration<AuthRoleManager>());

    // Custom user service used to inject custom registration workflow
    factory.UserService = new Registration<IUserService>(resolver => AuthObjects.AuthUserService);

    var scopeStore = new InMemoryScopeStore(Scopes.Get());
    factory.ScopeStore = new Registration<IScopeStore>(scopeStore);
    var clientStore = new InMemoryClientStore(Clients.Get());
    factory.ClientStore = new Registration<IClientStore>(clientStore);

    var cors = new InMemoryCorsPolicyService(Clients.Get());
    factory.CorsPolicyService = new Registration<ICorsPolicyService>(cors);

    ...

    var options = new IdentityServerOptions
    {
        SiteName = "Authentication",
        SigningCertificate = LoadCertificate(),
        Factory = factory,
        AuthenticationOptions = authOptions
    };

    ...
});

I do note that the IdentityServer3 log entries say "CORS request made for path: /connect/authorize" rather than "CORS request made for path: /auth/connect/authorize". But looking through the IdentityServer3 source code suggests this probably isn't the issue.

Perhaps the InMemoryCorsPolicyService isn't being picked up?

Any ideas of why things aren't working for the AJAX called ApiController?

Thinktecture.IdevtityServer3 v1.6.2 has been installed using NuGet.

Update

I'm having a conversation with the IdentityServer3 developer, but am still having an issue reaching a resolution. In case it helps:

https://github.com/IdentityServer/IdentityServer3/issues/1697

Gavin
  • 5,629
  • 7
  • 44
  • 86

4 Answers4

0

Did you try adding https url also?- client.AllowedCorsOrigins.Add("https://test.myapp.com");

Santosh Karanam
  • 1,077
  • 11
  • 23
0

The documentation of IdentityServer says you should configure it on the client:

AllowedCorsOrigins = ... // Defaults to the discovery, user info, token, and revocation endpoints.

https://docs.duendesoftware.com/identityserver/v6/reference/options/#cors

Mafii
  • 7,227
  • 1
  • 35
  • 55
0

CORS is a nightmare! It's a browser thing which is why you're witnessing different behaviour in IE than in Chrome.

There are (at least) two ways that CORS is configured on the server. When a client makes a request with the Origin header you have to tell the server whether or not to accept it -- if accepted then the server adds the Access-Control-Allow-Origin header to the response for the browser.

In MVC / webAPI you have to add CORS services, set a CORS policy, and then .UseCors something like this:

builder.Services.AddCors((options =>
            {
                if (settings.AllowedCorsOrigins.Length > 0)
                {
                    options.AddDefaultPolicy(builder => 
                    { 
                        builder.SetIsOriginAllowedToAllowWildcardSubdomains();
                        builder.AllowAnyHeader().AllowAnyMethod().WithOrigins(settings.AllowedCorsOrigins); 
                    });
                }
                if (isDevelopment)
                {
                    options.AddPolicy("localhost", builder => 
                    {
                        builder.SetIsOriginAllowedToAllowWildcardSubdomains();
                        builder.AllowAnyHeader().AllowAnyMethod().SetIsOriginAllowed((string origin) => { return origin.Contains("localhost"); }); });
                    }
            });

and

            app.UseCors();
            if (app.Environment.IsDevelopment())
            {
                app.UseCors("localhost");
            }

Typically, you want the list of allowed hosts as an array of strings in your appsettings.json. And watch out for the boobytrap with SetIsOriginAllowedToAllowWildcardSubdomains.

As well as this, IdentityServer has its own additional CORS settings which are applied in addition to the standard MVC/webAPI settings. These are in the ClientCorsOrigin table and this doesn't support wildcard subdomains. You can sidestep this whole boobytrap by implementing your own ICorsPolicyService to use the same settings from your appsettings.json something like this

    public class CorsPolicyService : ICorsPolicyService
    {
        private readonly CorsOptions _options;

        public CorsPolicyService(IOptions<CorsOptions> options)
        {
            _options = options.Value;
        }

        private bool CheckHost(string host)
        {
            foreach (string p in _options.AllowedCorsOrigins)
            {
                if (Regex.IsMatch(host, Regex.Escape(p).Replace("\\*", "[a-zA-Z0-9]+"))) // Hyphen?
                {
                    return true;
                }
            }
            return false;
        }

        public Task<bool> IsOriginAllowedAsync(string origin)
        {
            return Task.FromResult(CheckHost(origin));
        }
    }

Richard Barraclough
  • 2,625
  • 3
  • 36
  • 54
0

To Allow Cors policy add this to you Startup.cs file.

code

services.AddCors(options =>
{
options.AddDefaultPolicy(
                  builder =>
                  {
                      builder.WithOrigins("https://localhost:44343") //replace with your localhost 
                        .AllowAnyHeader()
                        .AllowAnyMethod();
                  });
                  });
M Junaid
  • 46
  • 3