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

Unable to infer base url. This is common when using dynamic servlet registration or when the API is behind an API Gateway #2191

Closed
evser opened this Issue Jan 16, 2018 · 28 comments

Comments

Projects
None yet
@evser

evser commented Jan 16, 2018

After updating Swagger to 2.8.0, I started to experience this issue. I never faced this before.
#1865
Does anyone either?

@cpoissonnier

This comment has been minimized.

cpoissonnier commented Jan 17, 2018

Make sure that all query are ok in developer console, I have the same issue, some of my queries return 403.

In my project, Swagger is behind basic auth (to prevent swagger to be public), but /swagger-resources/** routes are not called with Authorization header since I updated to Springfox 2.8.0

The workaround is to make this route public :
.antMatchers("/swagger-resources/**").permitAll()

@goodlifeinc

This comment has been minimized.

goodlifeinc commented Jan 17, 2018

Well I am using Swagger 2.7.0 and this fix doesn't help.

Here is my security setup. Actuator endpoints are behind basicAuthentication.

http
	.csrf().disable()
		.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
	.and()
		.authorizeRequests()
			.antMatchers(actuatorPath + "/**").authenticated()
	.and()
		.httpBasic()
	.and()
		.authorizeRequests()
			.anyRequest().permitAll();

Stangest thing is same config in another project with same dependencies works fine.

EDIT

With a strange combination between @configuration and @EnableSwagger2 on SwaggerDocumentationConfig class, seems to have solved my issue.

@kasecato

This comment has been minimized.

Contributor

kasecato commented Jan 18, 2018

@cpoissonnier An Authorization header will be added on the same-origin by #2189.

@evser

This comment has been minimized.

evser commented Jan 20, 2018

We should NOT disable security checks for swagger resources - as it also have sensitive data that should NOT be shown to any unknown user

@evser

This comment has been minimized.

evser commented Jan 20, 2018

BTW, here are my settings (I use cookies-based auth in my browser):

       protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
				.anyRequest().authenticated()
				.and().exceptionHandling().accessDeniedHandler(new AccessDeniedHandlerImpl())
				.and().logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler())
				.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
				.and()
				.addFilterBefore(ssoFilter(ApplicationConfiguration.API_BASE_PATH + "/login"), BasicAuthenticationFilter.class)
				.requiresChannel().anyRequest().requireSecure();
	}

	@Bean
	public FilterRegistrationBean oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
		FilterRegistrationBean frb = new FilterRegistrationBean();
		frb.setFilter(filter);
		frb.setOrder(SecurityProperties.DEFAULT_FILTER_ORDER);
		return frb;
	}

	@Bean
	@ConfigurationProperties("oauth2.client")
	public OAuth2ProtectedResourceDetails authDetails() {
		return new AuthorizationCodeResourceDetails();
	}

	@Bean
	public SecurityConfiguration swaggerSecurityConfiguration() {
		return new SecurityConfiguration("client-id", "client-secret", "realm",
				"", "{{X-XSRF-COOKIE}}", ApiKeyVehicle.HEADER, "X-XSRF-TOKEN", ",");
	}

	private Filter ssoFilter(String path) {
		OAuth2ClientAuthenticationProcessingFilter oAuth2ClientAuthenticationFilter = new OAuth2ClientAuthenticationProcessingFilter(path);
		OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(authDetails(), oauth2ClientContext);
		DefaultRedirectStrategy defaultRedirectStrategy = new DefaultRedirectStrategy();
		oAuth2ClientAuthenticationFilter.setRestTemplate(oAuth2RestTemplate);
		oAuth2ClientAuthenticationFilter.setTokenServices(resourceServerTokenServices);
		oAuth2ClientAuthenticationFilter.setAuthenticationSuccessHandler(
				(request, response, authentication) -> {
					String redirectUrl = request.getParameter(REDIRECT_URL_PARAM);
					if (redirectUrl == null) {
						redirectUrl = DEFAULT_REDIRECT_URL;
					} else {
						if (!redirectUrlValidator.validateRedirectUrl(redirectUrl)) {
							request.setAttribute(MESSAGE_ATTRIBUTE_NAME,
									messageSource.getMessage("ivalid.redirect.url", new String[] { redirectUrl }, LocaleContextHolder.getLocale()));
							response.sendError(HttpStatus.FORBIDDEN.value());
						}
					}
					defaultRedirectStrategy.sendRedirect(request, response, redirectUrl);
				});
		return oAuth2ClientAuthenticationFilter;
	}

And Docket:

	@Bean
	public Docket api() throws IOException, URISyntaxException {
		final List<ResponseMessage> globalResponses = Arrays.asList(
				new ResponseMessageBuilder()
						.code(200)
						.message("OK")
						.build(),
				new ResponseMessageBuilder()
						.code(400)
						.message("Bad Request")
						.build(),
				new ResponseMessageBuilder()
						.code(500)
						.message("Internal Error")
						.build());
		final ApiInfo apiInfo = new ApiInfo("REST API", new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(CHANGELOG_FILENAME)))
				.lines()
				.collect(Collectors.joining(System.lineSeparator())),
				"1.0.0-RC1", "", new Contact("team", "", "bla@bla.com"), "", "", Collections.emptyList());
		return EnvironmentUtils
				.applyIfNotStaging(
						new Docket(DocumentationType.SWAGGER_2),
						docket -> docket.securitySchemes(Arrays.asList(new ApiKey("Token Access", HttpHeaders.AUTHORIZATION, In.HEADER.name()))))
				.useDefaultResponseMessages(false)
				.globalResponseMessage(RequestMethod.GET, globalResponses)
				.globalResponseMessage(RequestMethod.POST, globalResponses)
				.globalResponseMessage(RequestMethod.DELETE, globalResponses)
				.globalResponseMessage(RequestMethod.PATCH, globalResponses)
				.select()
				.apis(RequestHandlerSelectors.basePackage("com.controller"))
				.build()
				.apiInfo(apiInfo)
				.directModelSubstitute(Temporal.class, String.class);
	}
@Hronom

This comment has been minimized.

Hronom commented Jan 26, 2018

Same here what the status of pull request?
My application is protected by basic auth using api gateway.

@dilipkrish

This comment has been minimized.

Member

dilipkrish commented Jan 30, 2018

@evser thanks for sharing your configuration. You are absolutely correct about not unauthenticated users access to specification resources

@enderjo

This comment has been minimized.

enderjo commented Feb 12, 2018

Maybe Help. I found that the Springboot Application Class need to add @EnableSwagger2 annotation.

@dilipkrish dilipkrish added the next label Feb 27, 2018

@seetharamani

This comment has been minimized.

seetharamani commented Mar 9, 2018

Does this issue have been figured out?? Once i upgraded to 2.8.0 from 2.6.1, the swagger ui started showing the error "Unable to infer base url".

When I look at debug logs, i do see this line.

{"category":"org.springframework.boot.web.filter.OrderedRequestContextFilter","severity":"DEBUG","text":"Bound request context to thread: Request(GET //localhost:10780/my-service/api/null/null/swagger-resources)@5689340d"},"v":"1"}

Why the base url for the swagger-resources are messed up - no idea! I tried debugging, and when i step in I do see the context path of my application is right, which is /my-service.

Any help is appreciated.

@dilipkrish

This comment has been minimized.

Member

dilipkrish commented Mar 10, 2018

Not sure. Would be great if you could share your repository

@AlexRosier

This comment has been minimized.

AlexRosier commented Mar 16, 2018

I have the same issue using spring security with basic authentication. What avoided the issues is adding

protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers(
                                "/v2/api-docs",
                                 "/swagger-resources", 
                                "/swagger-resources/configuration/ui", 
                                "/swagger-resources/configuration/security")
                .permitAll()

But this removes all security of those endpoints. An other solution is setting the Authorization header with the basic authentication.
But what I think should happen is the basic authentication pop-up is shown before the "Unable to infer base url" pop-up

@dilipkrish

This comment has been minimized.

Member

dilipkrish commented Mar 17, 2018

@AlexRosier I think you need to protect the swagger resources as well starting from swagger-ui

@dilipkrish dilipkrish closed this in 8e37f4e Apr 1, 2018

@wafflebot wafflebot bot removed the next label Apr 1, 2018

@marcoblos

This comment has been minimized.

marcoblos commented Apr 9, 2018

Hi!

I follow the recomendations in this post and solved the problem.

With the code bellow I can make calls to my rest API ignoring the WWW-Authenticate headers and use this headers only in swagger context (e.g.: localhost:8080/swagger-ui.html) to ask to user your username and password (I used Basic Authentication in this example).

This code protects:

  • /swagger-resources/** with BasicAuthenticationEntryPoint
  • /swagger-ui.html** with BasicAuthenticationEntryPoint
  • "/webjars/** with BasicAuthenticationEntryPoint
  • /** with RestAuthenticationEntryPoint (this is a custom entry point)

In pom.xml

...
<repositories>
    <repository>
        <id>swagger</id>
        <name>swagger</name>
        <url>http://oss.jfrog.org/artifactory/oss-snapshot-local</url>
    </repository>
</repositories>
...
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.8.1-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.8.1-SNAPSHOT</version>
</dependency>
...

Class that extends WebSecurityConfigAdapter:

@Configuration
public class WebSecurityConfigEntryPointApplication extends WebSecurityConfigurerAdapter {

    private static final List<String> AUTH_LIST = Arrays.asList(
            "/swagger-resources/**",
            "/swagger-ui.html**",
            "/webjars/**",
            "favicon.ico");

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .antMatcher("/**").authorizeRequests().anyRequest().authenticated()
                .and()
                .exceptionHandling()
                .defaultAuthenticationEntryPointFor(swaggerAuthenticationEntryPoint(), new CustomRequestMatcher(AUTH_LIST))
                .and()
                .httpBasic()
                .authenticationEntryPoint(restAuthenticationEntryPoint)
                .and()
                .csrf().disable();
    }

    @Bean
    public BasicAuthenticationEntryPoint swaggerAuthenticationEntryPoint() {
        BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
        entryPoint.setRealmName("Swagger Realm");
        return entryPoint;
    }

    private class CustomRequestMatcher implements RequestMatcher {

        private List<AntPathRequestMatcher> matchers;

        private CustomRequestMatcher(List<String> matchers) {
            this.matchers = matchers.stream().map(AntPathRequestMatcher::new).collect(Collectors.toList());
        }

        @Override
        public boolean matches(HttpServletRequest request) {
            return matchers.stream().anyMatch(a -> a.matches(request));
        }

    }

}

RestAuthenticationEntryPoint:

@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
		response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
	}
}
@zueski

This comment has been minimized.

zueski commented Apr 19, 2018

I am facing the same issue. I have my app protected with session cookie with SAML authentication. I can get in to /swagger-ui.html, which does a request to /swagger-resources/configuration/ui. This is failing due to the cookie not being attached to the call. If it navigate to /swagger-resources/configuration/ui directly, it works. Is there a way to configure swagger-ui.html to pass the authentication on in the ajax call?

@dilipkrish

This comment has been minimized.

Member

dilipkrish commented Apr 20, 2018

@zueski your issue should be fixed in the upcoming release of 2.9.0

@dilipkrish dilipkrish added this to the 2.9.0 milestone Apr 21, 2018

@zueski

This comment has been minimized.

zueski commented Apr 25, 2018

Thanks @dilipkrish -- In the meantime, I have added this to my application.yml to work around this:

security:
  ignored: /swagger-resources/**
@dilipkrish

This comment has been minimized.

Member

dilipkrish commented Apr 25, 2018

@zueski FYI 2.9.0 is released

@Hronom

This comment has been minimized.

Hronom commented Apr 25, 2018

@dilipkrish Not see it in maven yet... need wait till maven central publish?

@dilipkrish

This comment has been minimized.

Member

dilipkrish commented Apr 25, 2018

@Hronom Its available in jcenter for now.

<repositories>
    <repository>
      <id>jcenter</id>
      <url>https://jcenter.bintray.com/</url>
    </repository>
</repositories>

There is an issue getting it released in maven central that Im working through.

@CarstenHS

This comment has been minimized.

CarstenHS commented May 18, 2018

I've fought this issue a lot (due to bad logging capabilities I believe) and will share my findings here.

My setup:

  • omitting some irrelevant stuff due to secrecy.

Spring boot 2.0.0.RELEASE
implementation 'io.springfox:springfox-swagger2:2.9.0'
implementation 'io.springfox:springfox-swagger-ui:2.9.0'

dependencyManagement {
imports {
mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Finchley.M9'
}
}

I have two filterchains:
1: catching "swagger-ui.html" and creating a session for GUI interfacing. (comes before 2.)
2: REST filter for all requests REST interfacing.

Swagger filter config:
@override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/error");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
        .antMatchers(AUTH_WHITELIST).permitAll()
}

// AUTH_WHITELIST as mentioned above in this thread - without "/error"

REST filter
Add the AUTH_WHITELIST (now including "/error") to both WebSecurity.ignore() and to HttpSecurity.antMatchers.permitAll()

NOTE:
Make sure if you apply any authentication filters that they lie before
AnonymousAuthenticationFilter
SessionManagementFilter

Hope this helps ya out! :)

@dilipkrish

This comment has been minimized.

Member

dilipkrish commented May 31, 2018

@CarstenHS thank you for sharing. This is probably one of the most sought after solutions for problems related to not rendering swagger-ui.

@lcareto

This comment has been minimized.

lcareto commented Jun 12, 2018

Nothing related to Spring Security configuration, but I had the same issue and I fixed it, by changing this :

@RestControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<ResponseWrapper> {
}

to

@RestControllerAdvice("my.super.package")
public class MyResponseBodyAdvice implements ResponseBodyAdvice<ResponseWrapper> {
}

Without adding restriction on base package to scan, in annotation @RestControllerAdvice, I guess it scans also all the package dependencies, and tries to cast all the ResponseBody to the defined type (in my case ResponseWrapper).

I'm running Spring Boot 2.0.2.RELEASE and Swagger 2.8.0; Springfox update to 2.9.0 didn't fix my problem.

@dilipkrish

This comment has been minimized.

Member

dilipkrish commented Jun 12, 2018

Thanks for letting us know @lcareto!

@HamedKaramoko

This comment has been minimized.

HamedKaramoko commented Jun 19, 2018

If after all attempt, your issue is not solved try to remove the property

server.servlet.path

in your spring boot application.properties.

Hope that will helps you guys. ;)

@dilipkrish

This comment has been minimized.

Member

dilipkrish commented Jun 19, 2018

Thanks for the tip @HamedKaramoko

@dschulten

This comment has been minimized.

Contributor

dschulten commented Oct 27, 2018

I had to apply @EnableSwagger2WebFlux or @EnableSwagger2WebMvc on my spring-boot Application.java, depending on it being in an mvc or webflux project. The EnableSwagger2 annotation is gone in the current code base 3.0.0-SNAPSHOT.

@dilipkrish

This comment has been minimized.

Member

dilipkrish commented Nov 7, 2018

@dschulten just FYI, it is but it is still not written in stone (still a SNAPSHOT), If the usability implies it remain for upgrade paths etc. Im open to bringing it back.

@luksrn

This comment has been minimized.

luksrn commented Dec 6, 2018

Hey guys, in my case it was a ResponseBodyAdvice that changed the response body of /v2/api-docs endpoint. I fixed the advice and the ui was rendered as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment