Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance swagger-ui access #1384

Closed
waschmittel opened this issue Dec 14, 2021 · 16 comments
Closed

Enhance swagger-ui access #1384

waschmittel opened this issue Dec 14, 2021 · 16 comments
Labels
enhancement New feature or request

Comments

@waschmittel
Copy link

Describe the bug

  • Swagger UI cannot load the API description after start. It only works after explicitly requesting ${apiDocs.path}/swagger-config

To Reproduce
Steps to reproduce the behavior:

  1. Configure springdoc as follows
  apiDocs:
    path: /api-docs/v3
    groups:
      enabled: true
  group-configs:
    - group: basic
      paths-to-match: /v2/**,
    ...
  1. Start the Spring Boot application

  2. Open swagger-ui/index.html?configUrl=/api-docs/v3/swagger-config

Swagger UI cannot find the API definition. The source of index.html contains:

...
<script>
    window.onload = function() {
      // Begin Swagger UI call region
      const ui = SwaggerUIBundle({
        url: "",
        dom_id: '#swagger-ui',
        deepLinking: true,
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIStandalonePreset
        ],
        plugins: [
          SwaggerUIBundle.plugins.DownloadUrl
        ],
        layout: "StandaloneLayout"
      });
      // End Swagger UI call region

      window.ui = ui;
    };
  </script>
  1. Open /api-docs/v3/swagger-config

  2. Open swagger-ui/index.html?configUrl=/api-docs/v3/swagger-config again

Swagger UI can now find the API definition. The source of index.html contains:

    <script>
    window.onload = function() {
      // Begin Swagger UI call region
      const ui = SwaggerUIBundle({
        url: "",
        dom_id: '#swagger-ui',
        deepLinking: true,
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIStandalonePreset
        ],
        plugins: [
          SwaggerUIBundle.plugins.DownloadUrl
        ],
        layout: "StandaloneLayout" ,

  "oauth2RedirectUrl" : "http://localhost:8080/swagger-ui/oauth2-redirect.html",
  "urls" : [ {
    "url" : "<redacted>",
    "name" : "<redacted>"
  } ]

      });
      // End Swagger UI call region

      window.ui = ui;
    };
  </script>
  • What version of spring-boot you are using? --> 2.6.1
  • What modules and versions of springdoc-openapi are you using? --> 1.6.0
  • What is the actual and the expected result using OpenAPI Description (yml or json)? --> Swagger UI should always work
  • Provide with a sample code (HelloController) or Test that reproduces the problem --> not related to a particular controller

Expected behavior

Swagger UI should always work

@bnasslahsen
Copy link
Contributor

@waschmittel,

You are using the wrong url to request the swagger-ui.
Since v1.6.0, query params are disabled by default. You can read the CHANGELOG for more details.
If you want the legacy mode, you can still enable it using springdoc.swagger-ui.queryConfigEnabled=true

Use the default url as described in the documentation: http://serverName:applicationPort/swagger-ui.html
or instead of swagger-ui.html the value of springdoc.swagger-ui.path

@abrudin
Copy link

abrudin commented Dec 15, 2021

@bnasslahsen

Use the default url as described in the documentation: http://serverName:applicationPort/swagger-ui.html or instead of swagger-ui.html the value of springdoc.swagger-ui.path

It is still a strange default behaviour - as if loading of the default configuration has to be "triggered" by /swagger-ui.html. To recap:

  1. When navigating to /swagger-ui/index.html (first thing) no config is found
  2. When navigating to /swagger-ui.html redirection to /swagger-ui/index.html occurs but now config is found (you may have to Ctrl-F5)
  3. All subsequent requests to /swagger-ui/index.html finds config and works fine.

This behaviour seems independent of the queryConfigEnabled configuration.

A pretty common behaviour is to bookmark the page appearing in the browser after being sent the link - this will make /swagger-ui/index.html bookmarked, which will fail after new deploys until someone "triggers" config loading by navigating to /swagger-ui.html.

Is this really the intended behaviour?

(BTW, thanks for your commitment and fast responses)

@bnasslahsen
Copy link
Contributor

bnasslahsen commented Dec 15, 2021

@abrudin,

You shouldn't navigate to /swagger-ui/index.html. It was never the case: Before v1.6.0, you had query params in the URL as well.

Now since v1.6.0 query params are disabled by default as we are based on swagger-ui v1.4.3. You can read the CHANGELOG and this security link.

You should only use: /swagger-ui.html or replace it by your own config of: springdoc.swagger-ui.path
Now, if you want the legacy mode, you can still enable it using (Not recommended) springdoc.swagger-ui.query-config-enabled=true

Simply bookmark /swagger-ui.html URL or equivalent and it just work as expected (see samples in the demos links).

Hope it answers your question.

@abrudin
Copy link

abrudin commented Dec 15, 2021

@bnasslahsen

Yes, I understand that I should use /swagger-ui.html if I haven't configured otherwise, and doing that works fine. The issue I have concerns the usability aspect for my API client users, and it is exactly the same for the demos linked in your response:

  1. I give my users a clickable link to /swagger-ui.html
  2. They end up on /swagger-ui/index.html because of the default redirect
  3. They click the star/bookmark/favorite button in their browser and the /swagger-ui/index.html-version is saved.
  4. I deploy a new version of my service (multiple times every week)
  5. When my users use their bookmark, they end up with an empty API documentation, if I do not mitigate by somehow "loading" the config after each deploy using the /swagger-ui.html-link.

The /swagger-ui.html-address is so volatile and it never actually shows up in the browser's navigation bar, so it is hard for non-tech-savvy users to understand why they should navigate to that address instead of the one actually showing in the navigation bar.

This is not a big issue, and I will not push further, but I still think that it violates the principle of least surprise.

Thanks again for taking the time listening to your users.

@bnasslahsen
Copy link
Contributor

bnasslahsen commented Dec 15, 2021

@abrudin,

We have chosen not to change the default swagger-ui settings for security reasons,
We prefer you get surprised for this change (anticipating more serious issues), than getting surprised for a security breach.
If your context is accepting the security risks, simply set the property to enable the legacy mode with queryConfigEnabled, and your bookmark will work as earlier.

The last thing, is if you believe there is a better url handling, feel free to propose a PR.
I am giving it a try to see if we can improve it...

@abrudin
Copy link

abrudin commented Dec 15, 2021

@bnasslahsen

Yes, I agree 100% with the decision to not set queryConfigEnabled enabled by default - I'm sorry if anything I said made it appear otherwise. Following a link provided as a query parameter in the URL without validation never seems like a good idea from a security perspective, even if it happens client-side.

I will take a look at the source - a spontaneous thought is if one somehow could initialize whatever lazy property that holds the configUrl at application startup instead of when the user navigates to /swagger-ui.html.

@abrudin
Copy link

abrudin commented Dec 15, 2021

I thought that a workaround could be to manually configure the configUrl to the generated one:
config-url: /v3/api-docs/swagger-config

Problem is, that when this property is configured, the contents on that URL will not be properly generated. I got it to work by rearranging the if-clauses in AbstractSwaggerWelcome#buildConfigUrl like so:

	protected void buildConfigUrl(UriComponentsBuilder uriComponentsBuilder) {
		apiDocsUrl = buildApiDocUrl();
		swaggerConfigUrl = buildSwaggerConfigUrl();
		if (StringUtils.isEmpty(swaggerUiConfig.getConfigUrl())) {
			swaggerUiConfigParameters.setConfigUrl(swaggerConfigUrl);
		}
		if (CollectionUtils.isEmpty(swaggerUiConfigParameters.getUrls())) {
			String swaggerUiUrl = swaggerUiConfig.getUrl();
			if (StringUtils.isEmpty(swaggerUiUrl))
				swaggerUiConfigParameters.setUrl(apiDocsUrl);
			else
				swaggerUiConfigParameters.setUrl(swaggerUiUrl);
		}
		else
			swaggerUiConfigParameters.addUrl(apiDocsUrl);
		calculateOauth2RedirectUrl(uriComponentsBuilder);
	}

but maybe this could be done in the SwaggerIndexPageTransformer instead so that the configuration could be avoided.

Feel free to reach out if you feel this is a path worth making a PR for or if I could help in any other way,

@bnasslahsen
Copy link
Contributor

bnasslahsen commented Dec 16, 2021

@abrudin,

This will be an alternative, even it will require an extra http call!
I have already pushed a first fix for it.

Don't hesitate to give a try to the last SNAPSHOT and let me know if it's better.

@bnasslahsen bnasslahsen changed the title Swagger UI works only after manually requesting ${apiDocs.path}/swagger-config after start Enhance swagger-ui access post v1.6.0 Dec 16, 2021
@bnasslahsen bnasslahsen changed the title Enhance swagger-ui access post v1.6.0 Enhance swagger-ui access Dec 16, 2021
@abrudin
Copy link

abrudin commented Dec 16, 2021

@bnasslahsen I tried the 1.6.2-SNAPSHOT. Unfortunately it seems to break the grouping functionality, regardless of if I use /swagger-ui.html or /swagger-ui/index.html. (i.e. rendering a mix of all versions of all endpoints in the same view).

I'll take a further look, just wanted to let you know.

@bnasslahsen
Copy link
Contributor

@abrudin,

Demo with groups, is deployed without any issue and tests are passing.

Are you able to provide a minimal reproducible sample ?

@abrudin
Copy link

abrudin commented Dec 16, 2021

@bnasslahsen

Yes, I will try to provide an example - unfortunately I will be away for a week, so it will appear after Christmas if not solved before that.

What I can say immediately, is the following:

  • it is the GroupedOpenApi beans that for some reason does not get picked up - in the configuration, the urls parameter is empty, and the url parameter points to /v3/api-docs which picks up all the endpoints.
  • If I configure a group via the group-configs parameter in application.yaml (and remove the corresponding bean), that group will show, but not the other GroupedOpenApi beans.

I have tried to quickly compare with the demo application which uses a mix between application.yaml-group-configs and GroupedOpenApi-beans, but something apparently differs, as the demo-case picks up both the application.yaml-configured groups and the GroupedOpenApi-beans. When debugging and trying to understand what is going on, I struggle a little with the statefulness of the code and do not immediately see where e.g. the urls-field is set.

The difference appears when switching from 1.6.1 or earlier, to 1.6.2-SNAPSHOT.

@bnasslahsen
Copy link
Contributor

@abrudin,

I think it's finally with the webflux demo only in local demo!
Will investigate on the root cause...

bnasslahsen added a commit that referenced this issue Dec 17, 2021
@bnasslahsen
Copy link
Contributor

@abrudin,

Feel free to test with latest version and provide your feedback.

@abrudin
Copy link

abrudin commented Dec 17, 2021

@bnasslahsen
Yes, at a quick glance, that latest commit seems to fix the issue.

Thanks for your help!

@bnasslahsen
Copy link
Contributor

@abrudin,

Perfect! And thank you for your help on this enhancement.
We will release it very soon.

@bnasslahsen bnasslahsen added the enhancement New feature or request label Jan 9, 2022
@not3baddad
Copy link

@abrudin @bnasslahsen
Just wanted to thank you both for this enhancement. I'm pretty new to Java and was facing the same scenario and had no idea what was causing it or how it might have been resolved.

@springdoc springdoc locked as resolved and limited conversation to collaborators Feb 9, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants