swagger-api / swagger-ui

Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.
https://swagger.io
Apache License 2.0
26.67k stars 8.97k forks source link

CORS issue in oauth2 authorizationCode flow #6081

Open aldredb opened 4 years ago

aldredb commented 4 years ago

Q&A (please complete the following information)

Content & configuration

Example Swagger/OpenAPI definition:

  securitySchemes:
    oauth:
      type: oauth2
      flows: 
        authorizationCode:
          authorizationUrl: https://XX/authorization 
          tokenUrl: https://XX/token
          scopes: 
            registrar: can register

Swagger-UI configuration options:

ui.initOAuth({
    clientId: "XXX",
    clientSecret: "XXX",
    appName: "apitemplate",
    scopeSeparator: " ",
    useBasicAuthenticationWithAccessCodeGrant: 'true',
  })

Describe the bug you're encountering

I used the authorization_code grant flow to receive my grant code, however, during token retrieval i received error: Auth ErrorTypeError: Origin http://localhost:8080 is not allowed by Access-Control-Allow-Origin.

Screenshots

image

image

laurynasr commented 4 years ago

The authorization server needs to be set up to allow CORS for this to work. However, even with an authorization server set up for CORS this sometimes fails because it unnecessarily adds "X-Requested-With" header to Token endpoint call, and that "upgrades" the request to require preflight. And as Token request is generally a simple request, some authorization servers don't handle preflight at all. The "X-Requested-With" header was added with this change: https://github.com/swagger-api/swagger-ui/commit/937c8f6208f3adf713b10a349a82a1b129bd0ffd

aldredb commented 4 years ago

Unfortunately i don't have control over the authorization server..It's a managed service which I leveraged. Are there any workarounds that can be done?

hkosova commented 4 years ago

@aldredb you can use requestInterceptor to proxy requests through a CORS proxy. See the example here: https://github.com/swagger-api/swagger-ui/issues/1888#issuecomment-173179594

BeChaRem commented 4 years ago

@hkosova What about laurynasr comment about removing the X-Requested-With header? This break CORS for us.

Looking at OIDC library on github neither appauth-js or oidc-client-js need preflight for this. It's all done with simple request.

SpoonMeiser commented 4 years ago

The original problem that the X-Requested-With header was added for (the browser does the wrong thing if you enter incorrect details) seems less of a problem than not being able to authenticate at all.

I'd argue that the header should be removed, or at least made configurable so that people who have issues with CORS can disable it.

SpoonMeiser commented 4 years ago

In particular, PR #4934 concerns an oauth2 server that returns a header of WWW-Authenticate: Basic ... - "basic" here seems incorrect; is a bug with the server rather than swagger-ui? If so, I think this change should be reversed, and maybe a test added that the request from authorizeRequest complies with a simple request that won't trigger preflight.

SpoonMeiser commented 4 years ago

@aldredb @BeCharem can you check whether the changes in this branch on my fork fix the problem for you?

https://github.com/SpoonMeiser/swagger-ui/tree/fix-cors-issue

Turns out that it doesn't fix the problem I'm having, but these changes add a test that the request is a "simple" request, so might be worth making a PR if it fixes someone's problem.

BeChaRem commented 4 years ago

It fix the issue for us. No preflight and a successful call to the token endpoint.

jstallm commented 4 years ago

It fix the issue for us. No preflight and a successful call to the token endpoint.

What exactly resolved the issue for you?

BeChaRem commented 4 years ago

It fix the issue for us. No preflight and a successful call to the token endpoint.

What exactly resolved the issue for you?

Removing the "X-Requested-With": "XMLHttpRequest" Headers for the Token request as seen in SpoonMeiser commit.

jstallm commented 4 years ago

It fix the issue for us. No preflight and a successful call to the token endpoint.

What exactly resolved the issue for you?

Removing the "X-Requested-With": "XMLHttpRequest" Headers for the Token request as seen in SpoonMeiser commit.

Ok. Thank you. How exactly can I reference @SpoonMeiser fork as a nuget package?

BeChaRem commented 4 years ago

I don't know. I pulled his branch and tested locally to confirms the fix. I didn't go farther than that.

jstallm commented 4 years ago

I don't know. I pulled his branch and tested locally to confirms the fix. I didn't go farther than that.

How exactly did you test locally? How did you reference a local version of swaggerUI without a nuget reference?

The reason I dont believe this is a solution is because I am encountering the exact same issue and have used a Chrome extension to remove the X-Requested-With header. Even after removing the header using the extension, it still has the same error message. Therefore, its hard for me to validate this is in fact the solution.

BeChaRem commented 4 years ago

SwaggerUI have a standalone page in ./dist when you build it.

Steps I did to verify @SpoonMeiser branch:

  1. Clone his repo
  2. Checkout fix-cors-issue
  3. (Optionnal)Modify packages.json to serve on https instead of http
  4. Modify index.html for my own swagger.json file and openid config
      ui.initOAuth({
          clientId: "my-client-id",
          usePkceWithAuthorizationCodeGrant: true
        })
  5. npm install
  6. npm run build
  7. npm run start
  8. Verify that it works.

Only 1 GET request was done to the token endpoint. No pre-flight. Request Headers

jstallm commented 4 years ago

1 workaround I have found for me is to use Chrome Extension and force the response header Access-Control-Allow-Origin to be *

Even though I have a proxy that returns this same exact header, using a chrome extension to force modify the header strangely allows me to workaround the issue. For me, changing/modifying the X-Requested-With header did not resolve the issue.

BeChaRem commented 4 years ago

1 workaround I have found for me is to use Chrome Extension and force the response header Access-Control-Allow-Origin to be *

Even though I have a proxy that returns this same exact header, using a chrome extension to force modify the header strangely allows me to workaround the issue. For me, changing/modifying the X-Requested-With header did not resolve the issue.

Our IdP reflect the origin, instead of replying with *. I don't know if this makes a difference.

access-control-allow-origin: https://localhost:44343

jstallm commented 4 years ago

1 workaround I have found for me is to use Chrome Extension and force the response header Access-Control-Allow-Origin to be * Even though I have a proxy that returns this same exact header, using a chrome extension to force modify the header strangely allows me to workaround the issue. For me, changing/modifying the X-Requested-With header did not resolve the issue.

Our IdP reflect the origin, instead of replying with *. I don't know if this makes a difference.

access-control-allow-origin: https://localhost:44343

I dont believe it makes a difference since I can workaround the issue by using https://localhost:44386 or *

The million dollar question for me is : Why exactly would using a chrome plugin to set the value of access-control-allow-origin to the exact same value as the server response change the outcome of how the browser treats the response?

There is no difference whatsoever in the value returned from the server or from the plugin. They are the same value! Diff shown below image

So in summary: I experienced the exact same issue that the author of this issue experienced. My temporary workaround ( certainly not permanent since API developer/customers are not going to want to download a plugin just for using an API) is to use a chrome plugin to override the value of the response header "access-control-allow-origin" to either "*" or "https://localhost:44343" , which is the exact same value returned from the server response. Therefore, I dont believe a proxy is necessarily the solution.

pfeigl commented 3 years ago

We are facing the same problem with an IdentityServer which does not support CORS for /conntect/token requests.

Most swagger implementations (we are using NSwag and Swashbuckle) do however support injecting custom javascript code into the swagger startup page.

With this we were able to workaround the issue by using this code

    window.fetch = function (fetch) {
        return function () {
            var req = arguments[1];
            if(req.headers["X-Requested-With"]) {
                delete req.headers["X-Requested-With"];
            }
            return fetch.apply(window, arguments);
        };
    }(window.fetch);

Hth someone else.

Odraio commented 3 years ago

We are facing the same problem with an IdentityServer which does not support CORS for /conntect/token requests.

Most swagger implementations (we are using NSwag and Swashbuckle) do however support injecting custom javascript code into the swagger startup page.

With this we were able to workaround the issue by using this code

  window.fetch = function (fetch) {
        return function () {
            var req = arguments[1];
            if(req.headers["X-Requested-With"]) {
                delete req.headers["X-Requested-With"];
            }
            return fetch.apply(window, arguments);
        };
    }(window.fetch);

Hth someone else.

This javascript solution does work, but what is the security risk of implementing this workaround?

mitar commented 2 years ago

Backreference, gitlab.com's oauth implementation has the same issue: https://gitlab.com/gitlab-org/gitlab/-/issues/300077#note_975798994

uladzimir-tryputska commented 2 years ago

Gitlab is not going to adjust CORS behaviour. Is there a chance to adjust it in the swagger-ui? :)

lion7 commented 1 year ago

Any chance to get https://github.com/swagger-api/swagger-ui/pull/4934 reverted? This header breaks the integration with Dex IDP which I'm currently using 😒. While the above piece of JavaScript works just fine, it's a very ugly workaround.

jwr3408 commented 1 year ago

Try this .NET package: AspNetCore.Proxy.

Add this code to your program.cs file after app.MapControllers().RequireAuthorization() and before app.Run()

  app.UseProxies(proxies =>
    {
        proxies.Map("oauth", proxy => proxy.UseHttp(ConfigSettings.TokenUrl,
            builder => builder.WithShouldAddForwardedHeaders(false)));
    });

here is my flows parameter:

 Flows = new OpenApiOAuthFlows()
                    {
                        ClientCredentials = new OpenApiOAuthFlow
                        {
                            TokenUrl = new Uri("/oauth", UriKind.Relative),
                            Scopes = new Dictionary<string, string>()
                        }
                    }
tmm360 commented 2 months ago

Any news on this? Still a problem

hichemelhic commented 3 weeks ago

@lion7 , If you have control over you DEX Idp server, you can fix this by just adding this config under the web section. allowedHeaders: ['x-requested-with']

lion7 commented 2 weeks ago

Thanks for the hint! This seems to be added in https://github.com/dexidp/dex/pull/3114 as a direct fix for this issue 😄