Spring OAuth 2 authorization server authenticates clients using user details service - java

I have a Spring boot application with spring-security-oauth2-autoconfigure. The resource server and authorization server run in the same application.
The application has an user details service globally configure throught WebSecurityConfiguration. It is used to resources' owner authentication.
I have configured the authorization server to authenticate the clients using a in memory store, like that:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("devglan-client")
.secret("$2a$04$e/c1/RfsWuThaWFCrcCuJeoyvwCV0URN/6Pn9ZFlrtIWaU/vj/BfG")
.authorizedGrantTypes("implicit","refresh_token", "password", "authorization_code")
.scopes("read write trust");
}
The problem:
When I try to get an authorization code using the GET authorize endpoint, then the Spring Security tries to authenticate the application client using the user details service.
I include in the request a HTTP Authorization Header with the Basic option which contains the cliend_id:client_secret credentials.
GET /oauth/authorize?
response_type=code&client_id=bpclient&scope=read HTTP/1.1
Host: localhost:8080
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZGV2Z2xhbi1jbGllbnQ6MTIzNDU2
Cache-Control: no-cache
Postman-Token: 77dd0129-bb86-d039-d252-8e7d483092f2
I debugged the code and indetify that DaoAuthenticationProvider tried to retrive the app client credentials using the user details service.
The question
Why the Spring OAuth use the user details service to authenticate the application clients in oauth authorization flow rather than the in-memory configuration?
My code
Pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
Application Main
#SpringBootApplication()
#EnableAutoConfiguration
public class Application extends RepositoryRestConfigurerAdapter{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The Authorization Server configuration:
#Configuration
#EnableAuthorizationServer
public class ServidorAutorizacaoOAuthConfiguracao extends AuthorizationServerConfigurerAdapter {
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("devglan-client")
.secret("$2a$04$e/c1/RfsWuThaWFCrcCuJeoyvwCV0URN/6Pn9ZFlrtIWaU/vj/BfG")
.authorizedGrantTypes("implicit","refresh_token", "password", "authorization_code")
.scopes("read write trust");
}
}
The WebSecurity Configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SegurancaConfiguracao extends WebSecurityConfigurerAdapter{
#Autowired
private UsuarioServico usuarioServico;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.usuarioServico).passwordEncoder(encoder());
}
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
The resource server configuration:
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ServidorRecursoOAuthConfiguracao extends
ResourceServerConfigurerAdapter {
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.antMatchers("/oauth2/authorization/google", "/login/oauth2/code/google", "/login").permitAll()
.antMatchers("/oauth/authorize").permitAll()
.anyRequest().permitAll()
.and()
.formLogin()
.and()
.oauth2Login()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}

Related

springdoc-openapi-ui returning 406 error-not-acceptable

I am using springdoc-openapi-ui for my web application but when I hit http://localhost:8080/swagger-ui/index.html in the browser I am getting 406 error code.
Below is the dependency in pom.xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.14</version>
</dependency>
And Spring Security config is
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll();
http.headers().frameOptions().sameOrigin();
return http.build();
}
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().antMatchers("/swagger-ui/**", "/v3/api-docs/**");
}
Spring boot version I am using 2.7.7
Below is network detail via inspect element in chrome for the swagger-ui.config ..

Keycloak redirects too many times after login in Spring gateway cloud

I am building one spring cloud gateway and in that I am implementing Keycloak Security everything works fine but once login done on keycloak it is not redirecting back and showing error too many redirects I am not sure what I am missing?
Below are the dependencies which I am using:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
And my application Api gateway start application code is as below:
#SpringBootApplication
#ComponentScan(basePackages = {"com", "com.b","com.auth","com.security"})
public class APIGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(APIGatewayApplication.class, args);
}
#Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new PathBasedKeycloakConfigResolver();
}
}
Security Config http code is as below:
#Configuration
public class SecurityConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain (ServerHttpSecurity http) {
http
.authorizeExchange()
.anyExchange()
.authenticated()
.and()
.oauth2Login(); // to redirect to oauth2 login page.
return http.build();
}
}
and in my application.yml file I am adding below configuration:
spring:
security:
oauth2:
client:
provider:
my-keycloak-provider:
issuer-uri: http://localhost:8280/auth/realms/Default
registration:
keycloak-spring-gateway-client:
provider: my-keycloak-provider
client-id: Default
client-secret: 8ZRUH62Pfhfde6uqasD8dfgdhvqWt03K6
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/app'
main:
web-application-type: reactive
application:
name: app
cloud:
gateway:
default-filters:
- TokenRelay
So when I start application it is running fine and it is getting redirected to the Keycloak login page and after login it shows error "localhost redirected you too many times." How to resolve it so once it is login it should redirected once only to main application? Please help.

Spring Boot Resource Server Invalid Token

I'm trying to configure OAuth2 for a Spring project. I used jdbc authentification and my authorization server and resource server are two separate API. My issue is now with the microservices. I'm trying to use this shared authorization server to authenticate the microservices. I can get access_token from the token endpoint.
I can check the access_token from the check_token endpoint.
My resource server configuration:
#SpringBootApplication
#EnableCircuitBreaker
#EnableDiscoveryClient
#EnableResourceServer
public class ProductApiServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApiServiceApplication.class, args);
}
}
And application.yml:
security:
oauth2:
client:
client-id: saba-product-api-service
client-secret: secret123
resource:
id: saba-product-api-service
token-info-uri: http://localhost:9999/uaa/oauth/check_token
And REST controller:
#GetMapping("/user/me")
public Principal user(Principal principal) {
return principal;
}
When I call the /user/me endpoint I get invalid_token.
My Resource Server log:
And my Authorization Server log:
What is wrong with my code?
Update
The problem is because of this code:
I had the same issue. In my case, I was using spring cloud oauth2, Hoxton.SR4 release and it was working. So, I change to Hoxton.SR6 and the issue was throwed. My Authoriation Server also was a Eureka's client, and the issue was origined cause this dependency. There was one dependĂȘncia inside Eureka Client, named jackson-dataformat-xml, and because it the return of check_token endpoint was converted in xml instead json. When RemoteTokenServices called check_token, and the resulta was a xml, it culdn't decerialized in map<String,Object> the right way. If you had more than one aud, scope or authorities, it picked the last one. And the active propertie was trated as string. In my case I solved the issue excluding in Authorization Server the dependency mentioned from Eureka Client, like this:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
Finally, I replaced
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
with
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.5.0.RELEASE</version>
</dependency>
// gh-838
if (map.containsKey("active") && !"true".equals(String.valueOf(map.get("active")))) {
logger.debug("check_token returned active attribute: " + map.get("active"));
throw new InvalidTokenException(accessToken);
}

Why does my OAuth2 won't working with Spring Boot?

I'm trying to setup Facebook login with OAuth2 for Spring Boot.
First I have my spring security configuration. I want every page from www.localhost:8080/Intranet/** to become blocked for people that haven't been authorized by Facebook.
#Configuration
#EnableOAuth2Client
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.antMatcher("/Intranet/**")
.authorizeRequests()
.antMatchers("/", "/Intranet")
.permitAll()
.anyRequest()
.authenticated()
.and()
.logout().logoutSuccessUrl("/").permitAll();
}
}
I create my application.yml here:
spring:
application:
name: spektrakonhemsida
security:
oauth2:
client:
registration:
facebook:
clientId: myID
clientSecret: mySecret
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://graph.facebook.com/me
# Server configuration
server:
port: 8080
error:
whitelabel:
enabled: false
Then I have my dependencies for Spring Security and OAuth2:
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<!-- Prevent /error to crash -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
This is what's happening:
When I access www.localhost:8080/Intranet <- Works perfekt!
When I access www.localhost:8080/Intranet/Bokning <- I will be navigated to /error where my text shows up "You have no rights here! Please login".
But I want users to become automatically navigated to Facebook's login page when they enters /Intranet/**
Why does this not happening?
Found a solution now. This need to be done to make it work with Facebook.
Security:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/Intranet/Bokning").authenticated() // Block this
.antMatchers("/**", "/Intranet**").permitAll() // Allow this for all
.anyRequest().authenticated()
.and().logout().logoutSuccessUrl("/").permitAll()
.and()
.oauth2Login();
}
}
And appllication.yml
spring:
security:
oauth2:
client:
registration:
facebook:
clientId: myID
clientSecret: mySecret
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://graph.facebook.com/me
server:
port: 8080
And pom.xml file:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>

Error in spring security config class

In my spring app, I am using Spring 4.0.4 and Spring Security 3.2.3. I copied this code directly from a tutorial on the sprin website but I am having problem compiling because the method registerAuthentication couldn't be overriden from the WebSecurityConfigurerAdapter class and the HttpSecurity class doesn't have the methode authorizeUrls. Is there a jar I'm missing? Or is it the version?
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("letsnosh").password("noshing").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls()
.antMatchers("/order/**").hasRole("USER")
.antMatchers("/checkout").hasRole("USER")
.anyRequest().anonymous()
.and()
//This will generate a login form if none is supplied.
.formLogin();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Looks like this is now .authorizeRequests() as of 3.2.0.RC1 . See https://jira.spring.io/browse/SEC-2202
Now it is .authorizeRequests()
I have the same code and it works fine for me. Have you checked that you have both spring-security-web and spring-security-config in your classpath? Try to add them to your dependency section (if you are using maven).
<!-- Spring security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
To get that section of that tutorial to run with gradle, it uses
compile 'org.springframework.security:spring-security-web:3.2.0.M2'
compile 'org.springframework.security:spring-security-core:3.2.0.M2'
compile 'org.springframework.security:spring-security-config:3.2.0.M2'
I'm not sure what later versions would do.
Also, be aware that the .anyRequest().anonymous() line will lock out all pages except order and checkout once your logged in since you're no longer anonymous. Just remove that line for it to work the way the tutorial expects it to.
Try this
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("letsnosh").password("noshing").roles("USER");
}

Categories