Skip to content
This repository has been archived by the owner on Jul 31, 2024. It is now read-only.

Discuss CORS #167

Closed
leastprivilege opened this issue Jul 21, 2016 · 19 comments
Closed

Discuss CORS #167

leastprivilege opened this issue Jul 21, 2016 · 19 comments
Assignees
Milestone

Comments

@leastprivilege
Copy link
Member

No description provided.

@mjosh954
Copy link

Hey all, I had noticed that Client has a collection of AllowedCorsOrigins. Is this property being used to validate against the origin string being passed in the ICorsPolicyService? When I created my own ICorsPolicyProvider, I didn't see any way to check against that, because I don't have any reference to the client calling except for the orgin string passed in the IsOriginAllowedAsync. Is this interface just a blanket check against all origins no matter how we filter it, or should we check the origin against all AllowedCorsOrigin collections for all clients? Thanks

@leastprivilege
Copy link
Member Author

This feature is not done yet.

@mjosh954
Copy link

@leastprivilege Oh I see. I want to contribute to this functionality. Is there already an idea on how we would want to achieve this? Maybe expose a GetAllClientsAsync method in the IClientStore and do something similar to what the InMemoryCorsPolicyService is doing, or would that not be as efficient?

@dementeddevil
Copy link

I added a method to IClientStore2 (derived from IClientStore) that allows fetching CORS origins for enabled clients (InMemoryCorsPolicyService currently looks at all clients whether enabled or not)
Seems that we are missing the ability to optionally restrict a given CORS origin to specific methods (which would be nice) - is this something you anticipate adding in the future?

@brockallen
Copy link
Member

Yea, maybe on the iclientservice it makes sense to add the cors semantics. we'll think about it.

@devseal
Copy link

devseal commented Aug 24, 2016

When are you planning to release this functionality??

@brockallen
Copy link
Member

tumblr_m9524la1s11rnpex0

@brockallen
Copy link
Member

@dementeddevil, what did you mean by this:

Seems that we are missing the ability to optionally restrict a given CORS origin to specific methods

@rsnj
Copy link

rsnj commented Aug 25, 2016

What is the reason that IdentityServer needs to have its own CORS implementation rather than leveraging the ASP.NET CORS middleware?
https://github.com/aspnet/CORS/

@brockallen
Copy link
Member

We are using that implementation. It's just a matter of allowing CORS for specific endpoints and determining where the policy is sourced from. It's common to associate CORS origins with client configurations.

@devseal
Copy link

devseal commented Aug 26, 2016

I was able to make this work with a kind of a hack.
First, I enabled the CORS middleware after enabling the identity server and before enabling MVC.
And then at the begining of the Configure method, I verify that all the headers I need are present and then set the values only if they are already present, and if not then I add the missing headers.
Here is the code for the Configure:

`;

    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
        app.Use(async (context, next) =>
        {
            if(context.Request.Method == "OPTIONS")
                context.Response.StatusCode = (int)HttpStatusCode.OK;

            var headers = context.Response.Headers;
            if (headers.ContainsKey("Access-Control-Allow-Origin"))
            {
                headers["Access-Control-Allow-Origin"] = string.Join(",", context.Request.Headers["Referer"].Select(x => x.Substring(0, x.Length - 1)));
            }
            else
            {
                context.Response.Headers.Append("Access-Control-Allow-Origin", string.Join(",", context.Request.Headers["Referer"].Select(x => x.Substring(0, x.Length - 1))));
            }
            if (headers.ContainsKey("Access-Control-Allow-Headers"))
            {
                headers["Access-Control-Allow-Headers"] = "Origin, Content-Type, Accept, Client, Authorization, X-Auth-Token, X-Requested-With";
            }
            else
            {
                context.Response.Headers.Append("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Client, Authorization, X-Auth-Token, X-Requested-With");
            }
            if (headers.ContainsKey("Access-Control-Allow-Methods"))
            {
                headers["Access-Control-Allow-Credentials"] = "GET, POST, PATCH, PUT, DELETE, OPTIONS";
            }
            else
            {
                context.Response.Headers.Append("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS");
            }
            if (headers.ContainsKey("Access-Control-Allow-Credentials"))
            {
                headers["Access-Control-Allow-Credentials"] = "true";
            }
            else
            {
                context.Response.Headers.Append("Access-Control-Allow-Credentials", "true");
            }

            context.Response.Headers.Append("Access-Control-Expose-Headers", "X-Auth-Token");
            context.Response.Headers.Append("Vary", "Origin");

            await next();
        });

        Func<string, LogLevel, bool> filter = (scope, level) =>
            scope.StartsWith("IdentityServer") ||
            scope.StartsWith("IdentityModel") ||
            level == LogLevel.Error ||
            level == LogLevel.Critical;

        loggerFactory.AddConsole(filter);
        loggerFactory.AddDebug(filter);

        if (environment.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Temp",
            AutomaticAuthenticate = false,
            AutomaticChallenge = false
        });

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        string apiUrl = setting.GetSetting("ApiUrl");

        app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
        {
            Authority = apiUrl,
            ScopeName = "scope",
            ScopeSecret = "secret",
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
            RequireHttpsMetadata = false,                                
        });

        app.UseIdentityServer();

        app.UseCors(x => x.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials());

        //app.UseWelcomePage();
        app.UseStaticFiles();
        app.UseMvcWithDefaultRoute();
    }

`
Hope this help others with the same problem in the meantime.

@brockallen
Copy link
Member

brockallen commented Aug 26, 2016

@Jjanne i'm curious why you had to do so much of that implementation? was there something about our ICorsPolicyService that wasn't working for you?

@devseal
Copy link

devseal commented Aug 26, 2016

I needed CORS for both the IdentityServer and for the API.
When I add the CORS middleware before adding the IdentityServer, only the calls to IdentityServer get the CORS, but the calls to the API (mvc) do not get the CORS settings. Also I was only able to add the policy directly with the builder. When I tried using a policy by name (added in the ConfigureServices) nothing worked. Then I decided to remove CORS middleware completely and put the headers myself, and that still did not work. I was getting error on the preflight (OPTIONS) when using chrome. Then I decided to try and use CORS middleware only for the mvc, and then make sure that all headers were fine before sending the response. And that worked!

@brockallen
Copy link
Member

Ok, that makes sense. And the problems you're having (and the reason we have this open issue) is because the design of the Microsoft CORS middleware doesn't really make it easy for our situation where we're hosting 2 different "things" in the same pipeline. Plus with the fact that the policy for IdentityServer will most likely need to be dynamic from a database.

We'll keep investigating here and try to come up with a solid solution that works. Thanks.

@rsnj
Copy link

rsnj commented Aug 26, 2016

@Jjanne I was having the same issue, but I have it working now without any workarounds. Although I still get warnings in my logs from IdentityServer about CORS rejections on any of my API endpoints. I think that the ASP.NET CORS provider takes over after and adds the headers on the responses. Its just confusing that I have two separate CORS configurations and that IdentityServer is failing on the /api calls even though the correct Origin is configured.

My Startup.cs file just has:
In ConfigureServices() =>

services.AddCors();

In public void Configure() =>

app.UseCors(policy =>
{
  policy.WithOrigins("http://localhost:4200");
  policy.AllowAnyHeader();
  policy.AllowAnyMethod();
  policy.AllowCredentials();
});

In Clients.cs:

AllowedCorsOrigins = new List<string>
{
  "http://localhost:4200"
}

These are the warnings from my logs:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 OPTIONS http://foo.com/api/user  
warn: IdentityServer4.Hosting.Cors.PolicyProvider[0]
      CORS request made for path: /api/user from origin: http://localhost:4200 but rejected because invalid CORS path
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 2.9445ms 204 

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://foo.com/api/user  
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware[3]
      HttpContext.User merged via AutomaticAuthentication from authenticationScheme: Cookies.
warn: IdentityServer4.Hosting.Cors.PolicyProvider[0]
      CORS request made for path: /api/user from origin: http://localhost:4200 but rejected because invalid CORS path
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]

Request Headers:

GET /api/user HTTP/1.1
Host: foo.com
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
authorization: Bearer <token>
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Accept: */*
Referer: http://localhost:4200/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

Response Headers:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:4200
X-Powered-By: ASP.NET
Date: Fri, 26 Aug 2016 14:53:40 GMT
Cache-Control: proxy-revalidate
Transfer-Encoding: chunked
Connection: Keep-Alive
Content-Encoding: gzip
Age: 0

@brockallen
Copy link
Member

Done.

@aroy1313
Copy link

aroy1313 commented Feb 9, 2017

I want to use ASP NET membership tables as base and then add claims, scopes and token tables as well, in order to store Refresh tokens in the database

@lamemmv
Copy link

lamemmv commented Jul 31, 2017

I had same issue. Anyone have solution? Thanks

@lock
Copy link

lock bot commented Jan 14, 2020

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 14, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants