Migrating Springdoc from Springfox [duplicate] - spring-boot

This question already has an answer here:
How can I bypass authentication for Swagger-UI?
(1 answer)
Closed last month.
I am working on a legacy project (still uses Spring Boot 1.5).
Our team tasked with upgrading the project and one of the task is update Springfox to Springdoc.
I have change all annotations and I think it works because I can get the YAML or JSON file using /v3/api-docs URL.
One think that I cannot access is the Swagger UI. In the application.yml, I have added this:
springdoc:
api-docs:
resolve-schema-properties: true
swagger-ui:
use-root-path: true
validatorUrl: none
path: /swagger-ui
operationsSorter: alpha
tagsSorter: alpha
docExpansion: none
But everytime I access /swagger-ui/index.html or /swagger-ui.html, it seems it got error due to security or filter chain. There is security dependency in the project. I am newbie here so any suggestion what I can check. How to debug this?

You need to allow unauthenticated access to the Swagger resources in your Spring Security config. The following worked for me:
#Configuration
#EnableWebSecurity
class WebSecurityConfiguration {
private static final String[] SWAGGER_PATHS = {"/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/webjars/swagger-ui/**"};
private final UserRegistrationFilter authenticationTokenFilter;
#Bean
SecurityFilterChain filterChain(final HttpSecurity http) throws Exception {
return http
// Configure access rules
.authorizeHttpRequests(authorize -> authorize
.antMatchers(SWAGGER_PATHS).permitAll()
.anyRequest().authenticated())
// All your other config
.build();
}
}

Related

How to change `Authentication` implementation in spring-boot resource-server with opaque-token introspection?

I have a spring-boot resource-server configured with opaque-token introspection.
It works, but I'm now trying to opt-out BearerTokenAuthentication which is the type of Authentication build by OpaqueTokenAuthenticationProvider.
I implemented an OpaqueTokenIntrospector which turns introspection result into an OAuth2AuthenticatedPrincipal of my choice (implementation that is an Authentication too), but this is later turned into BearerTokenAuthentication byOpaqueTokenAuthenticationProvider and I could not find how to change this behavior.
Any clue?
edit
Opened a ticket for that: https://github.com/spring-projects/spring-security/issues/11661
The feature was merged on spring-security 5.8 branch.
All that is required now is to provide the resource-server opaque-token configurer with an OpaqueTokenAuthenticationConverter (or ReactiveOpaqueTokenAuthenticationConverter in webflux apps):
#Bean
SecurityFilterChain filterChain(
HttpSecurity http,
OpaqueTokenAuthenticationConverter authenticationConverter) throws Exception {
http.oauth2ResourceServer().opaqueToken()
.authenticationConverter(authenticationConverter);
return http.build();

403 Forbidden Using Springboot When Hitting Okta Userinfo endpoint

I'm trying to set up Okta as a sign on for a set of subpaths in my spring boot app.
I'm configuring the auth resource details with:
#Bean(name = "oktaOAuthClient")
public AuthorizationCodeResourceDetails oktaOAuthAdminClient(#Qualifier("oktaAdminConfiguration") OktaConfigurationProperties oktaAdminCongfig,
ICredentialsApi credentialsApi) {
String redirectUrl = UriComponentsBuilder.fromUriString("http://localhost:8091/")
.path(ConfigurationRequestPaths.ADMINISTRATION_LANDING)
.build(false)
.toUriString();
AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails();
client.setClientId(oktaAdminCongfig.getClientId());
client.setClientSecret(oktaAdminCongfig.getClientSecret());
client.setAccessTokenUri(oktaAdminCongfig.getAccessTokenUri());
client.setUserAuthorizationUri(oktaAdminCongfig.getUserAuthorizationUri());
client.setClientAuthenticationScheme(AuthenticationScheme.header);
client.setPreEstablishedRedirectUri(redirectUrl);
client.setScope(OKTA_SCOPES);
client.setUseCurrentUri(false);
client.setScope(OKTA_SCOPES);
return client;
}
These and other settings are found from the application.properties manually and are set as:
okta.admin.clientId={id}
okta.admin.clientSecret={secret}
okta.admin.accessTokenUri=https://dev-{value}.okta.com/oauth2/default/v1/token
okta.admin.userAuthorizationUri=https://dev-{value}.okta.com/oauth2/default/v1/authorize
okta.admin.issuer=https://dev-{value}.okta.com/oauth2/default
okta.admin.userInfoUrl=https://dev-{value}.okta.com/oauth2/default/v1/userinfo
Then I've made a filter with (Note, is the clientId set in the UserTokenInfoServices meant to be the client id from the okta client id/client secret?):
#Bean(name = "oktaFilter")
public Filter oktaFilter(#Qualifier("oktaOAuthClient") AuthorizationCodeResourceDetails oktaOAuthClient,
#Qualifier("oktaOAuthResource") ResourceServerProperties resource,
#Qualifier("oktaOAuthRestTemplate") OAuth2RestTemplate oktaOAuthRestTemplate) {
ExceptionMappingAuthenticationFailureHandler failureHandler = new ExceptionMappingAuthenticationFailureHandler();
failureHandler.setDefaultFailureUrl("/");
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(ConfigurationRequestPaths.ADMINISTRATION_LANDING);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(resource.getUserInfoUri(), oktaOAuthClient.getClientId());
tokenServices.setRestTemplate(oktaOAuthRestTemplate);
filter.setRestTemplate(oktaOAuthRestTemplate);
filter.setTokenServices(tokenServices);
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setUseReferer(true);
filter.setAuthenticationSuccessHandler(successHandler);
filter.setAuthenticationFailureHandler(failureHandler);
return filter;
}
Finally I've set up the WebSecurityConfigurerAdapter with the following:
http.antMatcher("/config/**")
.authorizeRequests()
.antMatchers("/config")
.permitAll()
.anyRequest().authenticated().and()
.exceptionHandling()
.authenticationEntryPoint(oktaLoginHandler)SimpleUrlAuthenticationSuccessHandler(ConfigurationRequestPaths.ADMINISTRATION_LANDING))
.and()
.logout().addLogoutHandler(oktaLogoutHandler).logoutSuccessUrl(externalAccessUrl).permitAll().and()
.addFilterBefore(oktaFilter, BasicAuthenticationFilter.class);
}
The redirect for the subpath works correctly and goes to a sign in page, but I get an error after signing in that warns:
org.springframework.security.authentication.BadCredentialsException: Could not obtain user details from token...Caused by: org.springframework.security.oauth2.common.exceptions.InvalidTokenException:
I believe this likely has to do with getting a 403 when hitting the okta userinfo endpoint:
Request is to process authentication
Retrieving token from https://dev-{value}.okta.com/oauth2/default/v1/token
Encoding and sending form: {grant_type=[authorization_code], code=[{code}], redirect_uri=[http://localhost:8091/config], client_id=[{id}], client_secret=[{secret}]}
HTTP GET https://dev-{value}.okta.com/oauth2/default/v1/userinfo
Accept=[application/json, application/*+json]
Response 403
I've also tried the okta starter but it seems to break when used with another oauth login to github for another set of subpaths in the application. The spring version I'm using doesn't include the .oauthLogin() and other settings for httpsecurity that I've seen some guides on.
edit: Adding my spring dependency list for more clarification:
org.springframework:spring-beans:5.1.20.RELEASE
org.springframework:spring-context:5.1.20.RELEASE
org.springframework:spring-jdbc:5.1.20.RELEASE
org.springframework:spring-tx:5.1.20.RELEASE
org.springframework:spring-web:5.1.20.RELEASE
org.springframework:spring-webmvc:5.1.20.RELEASE
org.springframework:spring-test:5.1.20.RELEASE
org.springframework.boot:spring-boot-actuator:2.1.18.RELEASE
org.springframework.boot:spring-boot-autoconfigure:2.1.18.RELEASE
org.springframework.boot:spring-boot-configuration-processor:2.1.18.RELEASE
org.springframework.boot:spring-boot-starter:2.1.18.RELEASE
org.springframework.boot:spring-boot-starter-actuator:2.1.18.RELEASE
org.springframework.boot:spring-boot-starter-security:2.1.18.RELEASE
org.springframework.boot:spring-boot-starter-thymeleaf:2.1.18.RELEASE
org.springframework.boot:spring-boot-starter-web:2.1.18.RELEASE
org.springframework.boot:spring-boot-starter-test:2.1.18.RELEASE
org.springframework.retry:spring-retry:1.3.1
org.springframework.security:spring-security-config:5.1.13.RELEASE
org.springframework.security:spring-security-core:5.1.13.RELEASE
org.springframework.security:spring-security-ldap:5.1.13.RELEASE
org.springframework.security:spring-security-web:5.1.13.RELEASE
org.springframework.security.oauth:spring-security-oauth2:2.3.8.RELEASE
org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.1.18.RELEASE
It sounds like you might be using the older Spring Security OAuth project:
spring-security-oauth?
This project has been deprecated. The newer Spring Security OAuth2 modules are great, and they are now first-class citizens, in Spring Security (they live in the official project now). Along with this Spring Boot 1.x is EoL, and is no longer getting patches and security updates.
Most of the guides you are seeing likely reference the newer libraries (e.g. things like .oauthLogin()).
Sorry for the typical StackOverflow answer of "don't do X", but here is what I would recommend:
Update your Spring Boot version
Migrate the newer OAuth libraries
Then add your new logic (this should be much easier after updating)
If you are already on Spring Boot 2 and the newer OAuth lib, let me know, and we can try to figure out why you don't have the newer HttpSecurity methods.

Spring Security + AAD: invalid_token_response

After signing in to the Azure Active Directory I get a 401 back from the POST to https://login.microsoftonline.com/common/oauth2/token.
I registered my application in my AD and gave it the permissions user.read and .... I changed the manifest and set oauth2AllowImplicitFlow to true.
Tenant id, client id and client secret are correctly filled in. I tried to change them to double check and changed it to the correct ones.
The web security config is the following, this is from the Microsoft/azure-spring-boot repository.
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
class AADOAuth2LoginSecurityConfig(private val oidcUserService: OAuth2UserService<OidcUserRequest, OidcUser>) : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService)
}
}
If I clone the sample and fill in the required configuration in application.properties everything works fine. If I start a brand new project, add the security, web, azure active directory, spring-security-oauth2-client and spring-security-oauth2-jose I get the 401.
The versions in use are the following
Spring Boot
2.1.0.RELEASE
Azure Spring Boot Version
2.0.7
application.properties
spring.security.oauth2.client.registration.azure.client-id=xxxxxx
spring.security.oauth2.client.registration.azure.client-secret=xxxxxx
azure.activedirectory.tenant-id=xxxxxx
azure.activedirectory.active-directory-groups=Users
After spitting through the debug logs the only difference I can see between the sample and the fresh project is that the HTTP Basic auth is different. The old version URL encodes the password (it contains non-url-safe characters) and then base64 encodes it as a whole base64(clientId:urlEncode(clientSecret)). The new version does not do this, can this be the actual problem? If so, how can I solve it since it's a change in the library then.

Spring Boot 2.0.0.M4 breaks http basic auth in application.yml

Spring Boot 1.5.6.RELEASE respected the basic-auth username and password as specified in my application.yml below.
I have upgraded to 2.0.0.M4 and now the application always starts with the default 'user' and randomly generated password. Basically the settings below are always completely ignored.
I saw some changes in the release note/doc specific to simplifying actuator security enabled/disabled. I didn't see anything specific to this.
Any ideas?
From my application.yml
security:
basic:
enabled: true
realm: some-service
user:
name: example_user
password: example_password
Update:
I've confirmed this functionality was just plainly taken out starting with Spring Boot 2.0.0.M4
In the appendices:
All the security.basic.* family of stuff is missing here from the M4 reference:
https://docs.spring.io/spring-boot/docs/2.0.0.M4/reference/html/common-application-properties.html
But appears here in the M3 reference:
https://docs.spring.io/spring-boot/docs/2.0.0.M3/reference/html/common-application-properties.html
I was able to temporarily downgrade to M3 to restore the previous functionality but would still appreciate some guidance on what replaced it. I just need a single hardcoded basic-auth user for this scenario. I'm aware I could use object configurations to do a much more complicated setup.
Edit 2018-01-31:
The ability to auto-configure a single user has been restored (via the spring.security.user configuration keys) starting with Spring Boot 2.0.0-RC1 (source).
Original answer:
The Spring Boot security.* properties have been deprecated starting with Spring Boot 2.0.0-M4. You can read about this in the Release Notes:
Security auto-configuration has been completely revisited: developers should read the companion blog post and refer to the Spring Boot 2.0 Security wiki page for more details about the change.
In order to restore the basic auth functionality you can register a custom WebSecurityConfigurerAdapter, like this:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
return new InMemoryUserDetailsManager(
User.withDefaultPasswordEncoder().username("user").password("password")
.authorities("ROLE_USER").build(),
User.withDefaultPasswordEncoder().username("admin").password("admin")
.authorities("ROLE_ACTUATOR", "ROLE_USER").build());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(EndpointRequest.to("health", "info")).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
.requestMatchers(StaticResourceRequest.toCommonLocations()).permitAll()
.antMatchers("/**").hasRole("USER")
.and()
.cors()
.and()
.httpBasic();
}
}
(This will also configure basic auth for the Actuator endpoints)
If you additionally need to read the username and password from a .properties file, you can simply inject the values using the #Value annotation.

UserInfoRestTemplateFactory setup fails with ResourceServerConfigurerAdapter and two spring-cloud-services-starters

I've slightly updated an Spring Cloud Services example to illustrate a problem that I'm having:
https://github.com/spring-cloud-services-samples/greeting/compare/master...timtebeek:master
After the above changes I'm using:
spring-cloud-services-starter-config-client:1.5.0.RELEASE
spring-cloud-services-starter-service-registry:1.5.0.RELEASE
spring-cloud-starter-oauth2:2.0.14.RELEASE
I've also added a minimal ResourceServerConfigurerAdapter
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
#Override
public void configure(final ResourceServerSecurityConfigurer resources) {
resources.resourceId("greeter");
}
}
And the bare minimum configuration setting:
security:
oauth2:
resource:
jwt:
key-uri: https://example.com/oauth/token_key
With these changes my application fails to deploy in PCF-DEV; I've not tried PCF proper, but expect the results to be similar. Here's the error message I get:
Method userInfoRestTemplateFactory in org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration required a single bean, but 2 were found:
- eurekaOAuth2ResourceDetails: defined by method 'eurekaOAuth2ResourceDetails' in class path resource [io/pivotal/spring/cloud/service/eureka/EurekaOAuth2AutoConfiguration.class]
- configClientOAuth2ResourceDetails: defined by method 'configClientOAuth2ResourceDetails' in io.pivotal.spring.cloud.service.config.ConfigClientOAuth2BootstrapConfiguration
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
So it's trying to use what should be completely separate OAuth2ProtectedResourceDetails from spring-cloud-services-starters to setup my application security; Where I would want it to use the external JWT key only.
Can anyone help me with how I can have my PCF deployed application using both config and discovery services also use an external JWT token for authentication setup?
I ran into the same thing. I saw this thread a few months ago. I also upgraded to spring boot 1.5.4 and cloud Dalston.SR4 and that got me over the hump. Thanks.
I was shown that http://start.spring.io was using spring boot 1.5.9. It runs on PivotalWS, so I knew there was a solution.
Try this change:
security:
oauth2:
client:
client-id: someclient
client-secret: someclientpass
resource:
jwt:
key-uri: https://example.com/oauth/token_key
The client-id and client-secret are dummy values in my case. I assume since you are also using JWT token, that your resource doesn't need to validate the token with your JWT token provider, only the signature (key-uri).
So by adding the client-id and client-secret, I'm guessing (totally guessing) that it creates the required OAuth2ProtectedResourceDetails with a better (closer) scope.
The fact that it was looking for "userInfoRestTemplateFactory" when we don't need to lookup user info is what pointed me in this direction.
My services are successfully deploying on PivotalWS (run.pivotal.io) with this change, using spring boot 1.5.9 and Dalston.SR4 with io.pivotal.spring.cloud:spring-cloud-services-dependencies:1.5.0.RELEASE
change spring-boot-starter-parent to be 1.5.2.RELEASE ,spring-cloud-dependencies to be Dalston.RC1 , spring-cloud-services-dependencies 1.5.0.RELEASE

Resources