spring boot oauth2 configuration: resource server remains unprotected - spring

I have implemented authorization server and resource server using spring boot. authorization server works fine and I am able to get tokens. But my resource server remains unprotected. My objective is that resource server ahould only be accessed by someone having valid access token.
My entire code is:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
TokenStore tokenStore;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore)
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("client")
.scopes("read", "write")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.authorizedGrantTypes("password", "refresh_token")
.secret("secret")
.accessTokenValiditySeconds(180)
.refreshTokenValiditySeconds(600);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security); //To change body of generated methods, choose Tools | Templates.
}
}
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.tokenServices(tokenServices())
.resourceId("MY_RESOURCE");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.requestMatchers().antMatchers("/**")
.and()
.authorizeRequests()
.antMatchers("/").access("hasRole('USER')")
.antMatchers("/secure/").access("hasRole('ADMIN')")
.and()
.exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore);
return defaultTokenServices;
}
}
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("bill").password("abc123").roles("ADMIN").and()
.withUser("bob").password("abc123").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll();
}
}
#Configuration
#EnableGlobalMethodSecurity
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
#SpringBootApplication
#RestController
public class Application extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
#GetMapping(value = "/")
public ResponseEntity<?> hello(){
return ResponseEntity.ok("Hello World");
}
#GetMapping(value = "/secure/")
public ResponseEntity<?> secure(){
return ResponseEntity.ok("Secure Resorce");
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>boot-oauth2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>boot-oauth2</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
What am I missing?
Thanks for help.
UPDATE:
I figured out that my resource server is unprotected because of presence of OAuth2SecurityConfig class. If I remove this class and add following class (where I have moved the inMemmory users), then resource server is protected as required
#Configuration
public class WebSecurityGlobalConfig extends GlobalAuthenticationConfigurerAdapter {
#Autowired
UserService userService;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("bill").password("abc123").roles("ADMIN").and()
.withUser("bob").password("abc123").roles("USER");
}
}
So, I am sensing improper HttpSecurity configuration in OAuth2SecurityConfig class is conflicting with resource server config.
So, How can I configure HttpSecurity of OAuth2SecurityConfig so, that it does allow access token protection for resource server paths and normal web security for non-resource server paths

finally after a lot of googling, I found the solution.
It was due to order of filters. order of OAuth2 resource filter has been changed in spring-boot-1.5.1. as change log says
The default order of the OAuth2 resource filter has changed from 3 to
SecurityProperties.ACCESS_OVERRIDE_ORDER - 1. This places it after the
actuator endpoints but before the basic authentication filter chain.
The default can be restored by setting
security.oauth2.resource.filter-order = 3
So, I changed the order of my OAuth2 resource server filter to 3 by setting it in application.properties security.oauth2.resource.filter-order = 3 and my problem was solved.

Annotate your OAuth2SecurityConfig with #EnableGlobalMethodSecurity(prePostEnabled = true)

I had the same issue.
I had another class extending WebSecurityConfigurerAdapter that i guess was in conflict with AuthorizationServerConfigurerAdapter.
I just removed the WebSecurityConfigurerAdapter class and it worked.

Related

Spring Boot 2.5.3 OAuth2 - Auth-Server and Webservice separate, Login error

Following the example on https://developer.okta.com/blog/2019/03/12/oauth2-spring-security-guide using the projects Create an OAuth 2.0 Server and Build Your Client App I cannot get it running without error.
I don't use Thymeleaf, as my Webservice returns data, not a page.
OAuth 2.0 Server project
#SpringBootApplication
#EnableResourceServer
public class Demo2Application {
public static void main(String[] args) {
SpringApplication.run(Demo2Application.class, args);
}
}
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final PasswordEncoder passwordEncoder;
public AuthorizationServerConfig(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("abcd")
.secret(passwordEncoder.encode("fDw7Mpkk5czHNuSRtmhGmAGL42CaxQB9"))
.authorizedGrantTypes("authorization_code")
.scopes("user_info")
.autoApprove(true)
.redirectUris("http://localhost:8082/login/oauth2/code/");
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");;
}
}
#Configuration
#Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("john")
.password(passwordEncoder().encode("doe"))
.roles("USER");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
#RestController
public class UserController {
#GetMapping("/user/me")
public Principal user(Principal principal) {
return principal;
}
}
application.properties
server.port=8090
pom.xml
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.5.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
I omit the context path, which the project originally uses.
Webservice project
#RestController
public class MyRESTController {
#GetMapping("/securedPage")
public String securedPage(Principal principal) {
return "securedPage";
}
#GetMapping("/")
public String index(Principal principal) {
return "index";
}
}
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**").authorizeRequests()
.antMatchers("/", "/login**").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login();
}
}
application.properties
server.port=8082
server.servlet.session.cookie.name=UISESSION
spring.security.oauth2.client.registration.custom-client.client-id=abcd
spring.security.oauth2.client.registration.custom-client.client-secret=fDw7Mpkk5czHNuSRtmhGmAGL42CaxQB9
spring.security.oauth2.client.registration.custom-client.client-name=Auth Server
spring.security.oauth2.client.registration.custom-client.provider=custom-provider
spring.security.oauth2.client.registration.custom-client.scope=user_info
spring.security.oauth2.client.registration.custom-client.redirect-uri=http://localhost:8082/login/oauth2/code/
spring.security.oauth2.client.registration.custom-client.client-authentication-method=basic
spring.security.oauth2.client.registration.custom-client.authorization-grant-type=authorization_code
spring.security.oauth2.client.provider.custom-provider.authorization-uri=http://localhost:8090/oauth/authorize
spring.security.oauth2.client.provider.custom-provider.token-uri=http://localhost:8090/oauth/token
spring.security.oauth2.client.provider.custom-provider.user-info-uri=http://localhost:8090/user/me
spring.security.oauth2.client.provider.custom-provider.user-name-attribute=name
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
When running both porojects, with localhost:8082 in the web browser I get index as response.
With localhost:8082/securedPage I get redirected to the login page, entering username john and password doe I get the following error page:
Login with OAuth 2.0
[invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource: 404 : [{"timestamp":"2021-07-30T07:58:54.529+00:00","status":404,"error":"Not Found","path":"/user/me"}]
Auth Server
Don't know what the error is causing. Looks like it is related to the Webservice application property and the URL to the UserController in the OAuth 2.0 Server project
spring.security.oauth2.client.provider.custom-provider.user-info-uri=http://localhost:8090/user/me
One additional question is:
Can the login form be avoided? The credentials passed somehow.
If the client is not the web browser but some other application which has no UI. Or perhaps curl. I also have MockMvc tests, so far it was Basic Auth, now OAuth2, how will be those affected?

Spring Security inmemory authentication issue

I am using Spring Security inmemory authentication, but it is not working properly.
It is generating default password but it is not taking the user and password I generated in configuration. Below is my controller:
#CrossOrigin(origins = "*")
#EnableAutoConfiguration
#RestController
#RequestMapping("/api")
public class AppController {
#RequestMapping(value="/hello", method=RequestMethod.GET, produces="text/plain")
public String sayHello() {
return "Hello welcome to spring security!!!!";
}
}
And below is my security configuration class:
#Component
#EnableWebSecurity
/*#EnableGlobalMethodSecurity*/
#EnableAutoConfiguration(exclude = {
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class
})
public class SecurityProvider extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/*").hasRole("ADMIN")
.and()
.formLogin();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("ramu")
.password("password")
.roles("ADMIN")
.and()
.withUser("gopal")
.password("password")
.roles("USER");
}
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
// ALTHOUGH THIS SEEMS LIKE USELESS CODE,
// ITS REQUIRED TO PREVENT SPRING BOOT AUTO-CONFIGURATION
return super.authenticationManagerBean();
}
}
I also tried by excluding SecurityAutoconfiguration.class at main application by using below annonation:
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
public class App {
public static void main( String[] args ) {
SpringApplication.run(App.class, args);
}
}
But no luck, it is not taking the username and password I configured in my configuration class.
I fixed your setup, see the code below.
The WebSecurityConfig (instead of the former ServiceProvider component):
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/api/*").hasRole("ADMIN").and().formLogin();
}
PasswordEncoder passwordEncoder =
PasswordEncoderFactories.createDelegatingPasswordEncoder();
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("ramu")
.password("password")
.roles("ADMIN")
.and()
.passwordEncoder(passwordEncoder)
.withUser("gopal")
.password("password")
.roles("USER");
}
}
The RestController:
#CrossOrigin(origins = "*")
#EnableAutoConfiguration
#RestController
#RequestMapping("/api")
public class AppController {
#RequestMapping(value="/hello",method=RequestMethod.GET,produces="text/plain")
public String sayHello(){
return "Hello welcome to spring security!!!!";
}
}
What I had to change:
ServiceProvider #Component -> WebSecurityConfig #Configuration
I registered a PasswordEncoder
I reverted all of the #EnableAutoConfiguration annotations
I reverted the AuthenticationManager Bean
I used the spring-parent:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
Solution for spring-boot-starter-parent version 1.5.7.RELEASE:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/api/*").hasRole("ADMIN").and().formLogin();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.withUser("ramu")
.password("password")
.roles("ADMIN")
.and()
.withUser("gopal")
.password("password")
.roles("USER");
}}
Maybe consider to use a kind of passwordEncoder here as well.

Spring boot security oauth2 authorization_code flow login page not found

Hi i am trying to implement OAuth2 using spring security, so far i have managed to implement it using grant_type=password i am getting token, time, refresh token and resources are guarded as expected.
Now my next step is to use authorization_code+pkce implementation.
I have followed this tutorial, when i replace all the client credentials with my auth server details i get http://localhost:8080/oauth/login Not Found message. The request goes something like this http://localhost:8080/auth/oauth/authorize?client_id=SampleClientId&redirect_uri=http://localhost:8083/ui2/login&response_type=code&state=5ppnu6
My Configuration and server files are as follow, this is my first time implemeting oauth on server side so i might be doing very non-sense, please help me correct that.
AuthorizationServerConfig.java
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private AuthenticationManager manager;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("SampleClientId")
.secret(passwordEncoder.encode("secret"))
.authorizedGrantTypes("password", "authorization_code", "implicit", "refresh_token")
.scopes(UserDetailsServiceImpl.Role.USER.name(),
UserDetailsServiceImpl.Role.MODERATOR.name(),
UserDetailsServiceImpl.Role.ADMIN.name())
.redirectUris("http://localhost:8080/callback", "http://localhost:8083/ui2/login")
.accessTokenValiditySeconds(3600)
.refreshTokenValiditySeconds(4800);
}
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore)
.authenticationManager(manager);
}
}
BeansConfig.java
#Configuration
public class BeansConfig {
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public UserDetailsServiceImpl userDetailsService() {
return new UserDetailsServiceImpl();
}
}
ResourceServerConfig.java
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "resource_id";
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.authorizeRequests()
.antMatchers("/users/**").authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "userDetailsService")
private UserDetailsServiceImpl userDetailsService;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
public SecurityConfig() {
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
userDetailsService.init(passwordEncoder);
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll();
}
}
UsersControllers.java
#RestController
public class UsersControllers {
#RequestMapping(value = "/users", method = RequestMethod.GET)
ResponseEntity<Map<String, String>> get() {
final Map<String, String> map = new HashMap<>();
map.put("status", "ok");
return ResponseEntity.ok(map);
}
#GetMapping(value = "/user/me")
Principal me(Principal principal) {
final Map<String, String> map = new HashMap<>();
map.put("status", "ok");
return principal;
}
}
UserDetailsImpl.java
public class UserDetailsImpl implements UserDetails {
private final String username;
private final String password;
private final List<GrantedAuthority> roles;
public UserDetailsImpl(String username, String password, List<GrantedAuthority> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return this.username;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
UserDetailsServiceImpl.java
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
private static final Logger LOGGER = Logger.getLogger(UserDetailsServiceImpl.class.getSimpleName());
public enum Role {
USER,
MODERATOR,
ADMIN
}
private final List<UserDetailsImpl> users = new ArrayList<>();
public UserDetailsServiceImpl() {
}
public void init(BCryptPasswordEncoder passwordEncoder) {
users.add(new UserDetailsImpl("john", passwordEncoder.encode("doe"), buildUserAuthorities()));
users.add(new UserDetailsImpl("wow", passwordEncoder.encode("baby"), buildModeratorAuthorities()));
}
private List<GrantedAuthority> buildUserAuthorities() {
final List<GrantedAuthority> authorityList = new ArrayList<>();
authorityList.add(new SimpleGrantedAuthority(Role.USER.name()));
return authorityList;
}
private List<GrantedAuthority> buildModeratorAuthorities() {
final List<GrantedAuthority> authorityList = new ArrayList<>();
authorityList.add(new SimpleGrantedAuthority(Role.MODERATOR.name()));
return authorityList;
}
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
for (UserDetailsImpl details : users) {
if (details.getUsername().equals(s)) {
LOGGER.warning("Found user: " + s);
return details;
}
}
throw new UsernameNotFoundException("User " + s + " notfound");
}
public List<UserDetailsImpl> getUsers() {
return users;
}
}
Some of the contents from maven file
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

Getting 403 Forbidden when trying to get authorization code using the authorization code grant type

I have resource, authorization written using Spring boot and OAuth2. The resources are going to access by another web server application. So I thought of using the authorization code grant type but I also want to skip the approval screen.
I sent the following url to the auth server to get the authorization code from postman along with the basic authorization in the headers.
http://localhost:8081/OAuth2ExampleApplication/oauth/authorize?response_type=code&client_id=my-trusted-client&redirect_uri=http://localhost:8080/clientapp/auth/callback&scope=read%20write
But I am getting the 403 Forbidden Access Denied from the auth server.
Here is my complete code.
Resource Server:
ResourceServerConfiguration.java
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.requestMatchers().antMatchers("/user/**")
.and().authorizeRequests()
.antMatchers("/user/**").access("hasRole('ADMIN')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
Authorization Server :
AuthorizationServerConfiguration.java
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private AuthorizationCodeServices authorizationCodeServices;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.secret("secret")
.redirectUris("http://localhost:8080/clientapp/auth/callback")
.autoApprove(true)
.accessTokenValiditySeconds(120).//Access token is only valid for 2 minutes.
refreshTokenValiditySeconds(600);//Refresh token is only valid for 10 minutes.
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager).authorizationCodeServices(authorizationCodeServices);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
}
Since I set autoApprove, I think the auth server will skip the login screen and send the authorization code directly to the redirectUrl (auth client app).
WebSecurityConfiguration:
OAuth2SecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("bill").password("abc123").roles("ADMIN").and()
.withUser("bob").password("abc123").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/authorize", "/oauth/token").permitAll();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.pg.oauth2.example</groupId>
<artifactId>oauth2-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>oauth2-example</name>
<description>OAuth2 Example</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

Spring oauth2 and digest authentication will work together

I have added spring oauth2 into my restful service. Most of the services are consumed by my own portal so getting the token then calling the api is fine. However i have exposed some more web services which has to be called without this token concept. Those consumers have username and password.
The best example is Swagger implementation. Where opening the swagger page should be authenticated via digest instead of oauth token.
The below code changes i made but it is not working.
I believe, i no need to call oauth server from the resource server in this case. So just made the below code within the resource server. But seen the problem like after authentication page accepted my credentials it is again rerouting/redirecting to the same authentication page.
Please help.
#Configuration
#EnableResourceServer
public class ResourceServerImpl extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.anonymous().disable().requestMatchers().antMatchers("/**").and().authorizeRequests()
.antMatchers(HttpMethod.POST, "/url1/path1/path2").hasAnyAuthority( "FUNCTION_FN1")
.antMatchers(HttpMethod.GET, "/url2/path1/path2").hasAnyAuthority( "FUNCTION_FN2")
.antMatchers("/swagger-ui.html").hasRole("USER")
.anyRequest().authenticated()
.and().formLogin().and().httpBasic()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}
UPDATE
I dont think this is possible to make some request to be authenticated with DIGEST and some are with OAUTH. So currently i made swagger url also to be authenticated with oauth token, instead of digest support.
Yes, it's definitely possible to configure multiple authorization mechanisms in the same application. Distinct instance of WebSecurityConfigurerAdapter should be provided for each mechanism.
#EnableResourceServer and #EnableAuthorizationServer provide corresponding oauth adapters. ApiSecurityConfiguration provides adapter for basic auth.
Here is minimal working sample for basic and oauth2:
#SpringBootApplication
public class TestApp {
#RestController
public static class Endpoints {
// doesn't require any authentication
#RequestMapping("/test")
public String test() {
return "no auth";
}
// requires basic authentication
#RequestMapping("/api/test")
public String apiTest() {
return "basic auth";
}
// requires oauth authentication
#RequestMapping("/oauth/test")
public String oauthTest() {
return "oauth";
}
}
#Configuration
#EnableWebSecurity
public static class ApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/api/**")
.and()
.authorizeRequests().antMatchers("/api/**").hasRole("USER")
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER");
}
}
#Configuration
#EnableAuthorizationServer
public static class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("test")
.secret("test")
.authorizedGrantTypes("client_credentials")
.authorities("USER")
.scopes("read", "write", "test")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(120);
}
}
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/oauth/**")
.and()
.authorizeRequests().antMatchers("/oauth/**").authenticated();
}
}
public static void main(String[] args) {
SpringApplication.run(TestApp.class, args);
}
}
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>

Resources