Skip to content
This repository has been archived by the owner on Sep 18, 2021. It is now read-only.

InMemoryCorsPolicyService - rejected because invalid CORS path #1697

Closed
gavinharriss opened this issue Aug 10, 2015 · 12 comments
Closed

InMemoryCorsPolicyService - rejected because invalid CORS path #1697

gavinharriss opened this issue Aug 10, 2015 · 12 comments
Labels

Comments

@gavinharriss
Copy link
Contributor

I'm trying to enable CORS for clients, but if users wait more than 3 mins before triggering an AJAX call they are getting a "CORS request made for path: /connect/authorize from origin: http://test.myapp.com but rejected because invalid CORS path" error logged.

I'm using a InMemoryCorsPolicyService, but perhaps I'm calling it incorrectly?

I've provided a lot of detail on Stack Overflow before thinking to ask here. I copy the detail here if it would help.

http://stackoverflow.com/questions/31912105/identityserver3-rejected-because-invalid-cors-path

Thank you in advance for any insights you might be able to provide.

@brockallen
Copy link
Member

Oh this might be related to the chrome issue -- search the archives. Chrome makes a normal request look like a CORS request (for some reason). We fixed this I think in 2.0.0 (I don't recall which version).

@gavinharriss
Copy link
Contributor Author

Unfortunately it's an issue across browsers. On IE I get the following error instead:

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

@brockallen
Copy link
Member

Ok, this is different. Why are you making an ajax call to the authorize endpoint?

@gavinharriss
Copy link
Contributor Author

We aren't explicitly calling the authorize endpoint but the ApiController's are decorated with the [Authorize] attribute so the framework must be initiating the call for us after the 3 minute window for some reason next time the user hits the API through AJAX.

@brockallen
Copy link
Member

Oh, sounds like you're making an API call to your own app, the OIDC middleware is kicking in and triggering a 302 to IdSvr's login process (which starts at the authorize endpoint). The short answer is that your API controllers should be issuing a 401 on failed authorization.

@gavinharriss
Copy link
Contributor Author

The user is actually authorized at the point when the issue occurs. They can still click around pages under the management of Controller's decorated with the [Authorize] attribute, just not ApiController's for some reason after 3 minutes of inactivity. So we shouldn't need to issue a 401? Or am I misunderstanding the mechanics of the 401 in this case?

We're seeing these messages logged by IdentityServer3:

2015-08-10 16:42 Warning CORS request made for path: /connect/authorize from origin: http://test.myapp.com but rejected because invalid CORS path

@brockallen
Copy link
Member

Right -- cookies are used to authenticate to your MVC code, but access tokens are used to authenticate to your Web APIs -- at least that's how it's typically done. I don't know how your stuff is setup, or how you're trying to protect your app, but I am 99% sure that the CORS error is a red herring for whatever the real issue is.

@gavinharriss
Copy link
Contributor Author

It does appear I'm just using cookies:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});

I'll investigate tokens now. I just assumed the cookies would be shared across both types of controllers.

Thank you for your time looking into this. I my be a little while responding now as entering new territory.

@gavinharriss
Copy link
Contributor Author

Do you mind scanning your eyes over the my client configuration in case I'm making a noob mistake somewhere (still learning this stuff)? I've added UseIdentityServerBearerTokenAuthentication alongside UseCookieAuthentication, so hopefully this is what's required to use tokens as well as cookies? ...

public static void Configure(IAppBuilder app, string clientId, string authority, string redirectUri, string postLogoutRedirectUri)
{
    Uri baseUri = new Uri(authority);
    JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

    app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
    {
        Authority = new Uri(baseUri, "/auth").ToString(),
        RequiredScopes = new[] { "openid", "ecan" }
    });

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = "Cookies"
    });

    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
        ClientId = clientId,
        Authority = new Uri(baseUri, "/auth").ToString(),
        RedirectUri = redirectUri,
        PostLogoutRedirectUri = postLogoutRedirectUri,
        ResponseType = "id_token token", // "token" is required for ProtocolMessage.AccessToken
        Scope = "openid ecan",

        SignInAsAuthenticationType = "Cookies",

        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            SecurityTokenValidated = async n =>
            {
                var token = n.ProtocolMessage.AccessToken;
                if (!string.IsNullOrEmpty(token))
                {
                    n.AuthenticationTicket.Identity.AddClaim(
                        new Claim("access_token", token));
                }

                // Used by sign out functionality
                n.AuthenticationTicket.Identity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));

                var userInfoClient = new UserInfoClient(
                    new Uri(baseUri, "/auth/connect/userinfo"),
                    n.ProtocolMessage.AccessToken);

                var userInfo = await userInfoClient.GetAsync();

                if (userInfo.Claims != null)
                    userInfo.Claims.ToList().ForEach(ui => n.AuthenticationTicket.Identity.AddClaim(new Claim(ui.Item1, ui.Item2)));
            },
            RedirectToIdentityProvider = async n =>
            {
                if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
                {
                    var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
                    n.ProtocolMessage.IdTokenHint = idTokenHint;
                }
            }
        }
    });
}

@brockallen
Copy link
Member

Cookies suffer from XSRF security issues with Web API controllers, so using a token in the authorization header prevents that attack. That's why OIDC is such an important protocol.

@brockallen
Copy link
Member

As for the code, your API will still accept the cookie with the config you used. Look into why Web API provides the SuppressDefaultHostAuthentication API call - understanding that will help you understand the right way to do it.

@tjrobinson
Copy link

For others coming across this:

SuppressDefaultHostAuthentication

While the MVC templates use a cookie based authentication mechanism, the new SPA templates prefer to use a token based authentication model explicitly passed via the Authorization HTTP header (which is better since it avoids CSRF attacks). This means that the default authentication from the host must be ignored since the authentication will be performed against something else other than a cookie. Web API 2 added a feature to ignore the host level authentication called SuppressDefaultHostAuthentication. This is an extension method on the HttpConfiguration that adds a message handler. The purpose of this message handler is to simply (and explicitly) assign an anonymous principal to the RequestContext’s Principal property. This way if cookie middleware does process an incoming cookie, by the time the call arrives at Web API the caller will be treated as anonymous.

Source: https://brockallen.com/2013/10/27/host-authentication-and-web-api-with-owin-and-active-vs-passive-authentication-middleware/

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants