swagger-ui icon indicating copy to clipboard operation
swagger-ui copied to clipboard

CORS issue in oauth2 authorizationCode flow

Open aldredb opened this issue 4 years ago • 21 comments

Q&A (please complete the following information)

  • OS: [e.g. macOS] macOS
  • Browser: [e.g. chrome, safari] Safari
  • Version: [e.g. 22] 13.1
  • Method of installation: [e.g. npm, dist assets] npm
  • Swagger-UI version: [e.g. 3.10.0] 3.24.3
  • Swagger/OpenAPI version: [e.g. Swagger 2.0, OpenAPI 3.0] OpenAPI 3.0.2

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

aldredb avatar Jun 04 '20 13:06 aldredb

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

laurynasr avatar Jun 04 '20 14:06 laurynasr

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?

aldredb avatar Jun 04 '20 14:06 aldredb

@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

hkosova avatar Jun 16 '20 17:06 hkosova

@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.

BeChaRem avatar Jun 16 '20 18:06 BeChaRem

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 avatar Oct 16 '20 08:10 SpoonMeiser

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 avatar Oct 16 '20 08:10 SpoonMeiser

@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.

SpoonMeiser avatar Oct 16 '20 10:10 SpoonMeiser

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

BeChaRem avatar Oct 16 '20 16:10 BeChaRem

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

What exactly resolved the issue for you?

jstallm avatar Nov 17 '20 20:11 jstallm

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.

BeChaRem avatar Nov 17 '20 21:11 BeChaRem

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?

jstallm avatar Nov 19 '20 18:11 jstallm

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

BeChaRem avatar Nov 19 '20 18:11 BeChaRem

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.

jstallm avatar Nov 19 '20 19:11 jstallm

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
        })
  1. npm install
  2. npm run build
  3. npm run start
  4. Verify that it works.

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

BeChaRem avatar Nov 19 '20 19:11 BeChaRem

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.

jstallm avatar Nov 19 '20 19:11 jstallm

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

BeChaRem avatar Nov 19 '20 19:11 BeChaRem

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.

jstallm avatar Nov 19 '20 21:11 jstallm

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.

pfeigl avatar May 26 '21 15:05 pfeigl

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?

Odraio avatar Aug 31 '21 17:08 Odraio

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

mitar avatar Jun 08 '22 13:06 mitar

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

uladzimir-tryputska avatar Jul 18 '22 11:07 uladzimir-tryputska

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.

lion7 avatar Mar 20 '23 18:03 lion7

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>()
                        }
                    }

jwr3408 avatar Jul 17 '23 19:07 jwr3408