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
This feature is not done yet.
@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?
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?
Yea, maybe on the iclientservice it makes sense to add the cors semantics. we'll think about it.
When are you planning to release this functionality??

@dementeddevil, what did you mean by this:
Seems that we are missing the ability to optionally restrict a given CORS origin to specific methods
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/
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.
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.
@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?
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!
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.
@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
Done.
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
I had same issue. Anyone have solution? Thanks
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.
Most helpful comment