Spring Security OAuth2 Jwt Access Denied - spring-boot

I'm trying to do this tutorial https://www.tutorialspoint.com/spring_boot/spring_boot_oauth2_with_jwt.htm about Spring Security and OAuth2, Spring boot 1.5. i can get the access token. but when i try to get the endpoint i get access denied error in Postman. I m working on Mac os.
configure Methode
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
}
In tokenEnhancer Method i commented the public key, because i had an error about Mac verification
#Bean
public JwtAccessTokenConverter tokenEnhancer() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(privateKey);
//converter.setVerifierKey(publicKey);
return converter;
}

Users which have appropriate GrantedAuthority should have access to endpoints and it's possible to achieve by next settings.
For instance
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/products/**").hasRole("USER")
.anyRequest().authenticated()
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
}
On the other hand need to know 'role' of user.
It's possible to check in db or other places which persist this info.
Or need to find method which define Principle of current user. It's depend on type of authentication. Implementation of method doFilterInternal OncePerRequestFilter abstract class responsible of defining Priciple.

Related

hasRole() and denyAll() method don't restrict access to resources

I'm developing Spring Boot and Spring Security web application with authorization and resource servers enabled. I have defined a set of users with roles assigned to them and trying to implement roles based access to REST endpoints. I was able to implement token based access to endpoints, but can't restrict access to end users, that would be based on their roles.
I have done two endpoints: /rest/products/list and /rest/products/add and trying to restrict access to /rest/products/add endpoint with the user that is of ADMIN role.
My WebSecurityConfigurerAdapter is as follows:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("user1")
.password(passwordEncoder.encode("user1Pass"))
.roles("USER")
.and()
.withUser("user2")
.password(passwordEncoder.encode("user2Pass"))
.roles("USER")
.and()
.withUser("admin")
.password(passwordEncoder.encode("adminPass"))
.roles("ADMIN");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/rest/products/add").hasAnyRole("ADMIN")
.antMatchers("/rest/products/list").denyAll();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Therefore, resource /rest/products/add should be accessible to admin / adminPass user only as far as that user has ADMIN role. But if to try to it with user1 / user1Pass, it is still accessible:
Get access token for user1 postman screen
Accessing ADMIN only related endpoint with user1 Postman screen
Also I added (in the testing purpose) in the configuration method the following rule .antMatchers("/products/list").denyAll(); Here is indicated that /products/list shouldn't be accessible to any user. But it still keeps on responding (provided access correct token).
In the similar question here How to fix role in Spring Security? the order of matchers should be from the more specific to the less. But in my case there are two matchers and no matchers that can overlap them.
I'm using Spring Boot with spring-boot-starter-security plugin version 2.5.2.
What additional configuration should be done to make .hasRole("ADMIN") and .denyAll() work as expected?
Finally was able to find the solution with the following:
Here there is an example of ResourceServerConfigurerAdapter class. From this and from your comment, dur, I realized that I confused ResourceServerConfigurerAdapter and WebSecurityConfigurerAdapter, trying to define access restriction matchers in WebSecurityConfigurerAdapter. I changed resource server configuration in the following way:
Method that was in WebSecurityConfigurerAdapter
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/products/add").hasAnyRole("ADMIN")
.antMatchers("/rest/products/list").denyAll();
}
was moved to
#Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/products/add").hasAnyRole("ADMIN")
.antMatchers("/rest/products/list").denyAll();
}
}
Now restrictions defined by matchers above are working as expected.

ActiveDirectoryLdapAuthenticationProvider and authentication using userDetailsService

I have two different users in my application. Ldap users and api users. Ldap users have privilege to access an endpoint and api users a different endpoint. I have implemented the api user authentication using UserDetailsService and having the details in my application.yaml file.
The issue I am facing now is, The endpoint that only Ldap users should access is now being accessed my api users as well. How can I prevent this. Please find my code snippet below
public class ServiceSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("ldapProvider")
private AuthenticationProvider authenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
// security for apiuser
http
.authorizeRequests()
.antMatchers(“/abcd/**).hasRole(“admin”)
.and()
.httpBasic().and().userDetailsService(userDetailsService());
// security for ldap users
http
.csrf().disable()
.authorizeRequests()
.antMatchers(“/ghhgh” + "/**").fullyAuthenticated()
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().and()
.authenticationProvider(authenticationProvider)
.exceptionHandling();
}
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername(“api”)
.password(passwordEncoder().encode(“test”))
.roles(“admin”)
return new InMemoryUserDetailsManager(user);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
In spring security it is indeed possible to register multiple authentication mechanisms.
BUT you cannot register a specific authentication provider to a specific route.
The spring securty docs say:
ProviderManager is the most commonly used implementation of AuthenticationManager. ProviderManager delegates to a List of AuthenticationProviders. Each AuthenticationProvider has an opportunity to indicate that authentication should be successful, fail, or indicate it cannot make a decision and allow a downstream AuthenticationProvider to decide.
So in every request, the registered AuthenticationProviders are checked one after the other until one is successful, or all fail.
To solve your problem, you need to define multiple custom authorities, that you assign your users.
Then you secure your endpoints using these authorities.
E.g. you give every ldap user the authority LDAP_USER and every api user the authority API_USER. Then you configure your security accordingly:
Register all AuthenticationProviders:
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ldapProvider);
auth.userDetailsService(userDetailsService());
}
And configure the endpoints:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
(...)
.authorizeRequests()
// security for apiuser
.antMatchers(“/abcd/**).hasRole(“API_USER”)
// security for ldap users
.antMatchers(“/ghhgh” + "/**").hasRole("LDAP_USER")
(...)
}

LDAP authentication with AD LDP from Spring Boot application

I am trying to implement LDAP authentication in a Sprint Boot application. In the test environment I have installed an Active Directory LDP service with which to authenticate. I have created a user within the AD instance, enabled the account and set a password. I am then trying to authenticate using this account from the Spring login form.
When I try to log in using AD I get an error message:
Your login attempt was not successful, try again.
Reason: Bad credentials
As I am new to both AD and Spring it is quite possible I have mis-configured either (or both!).
Do you have any suggestions as to how I can further diagnose this problem or is there anything obvious I may have missed?
My Spring Boot code (I have tried a number of different variations on this code, this is one example):
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider =
new ActiveDirectoryLdapAuthenticationProvider("foo.bar", "ldap://servername:389");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
It turns out that there was nothing wrong with my Java implementation. The issue appears to be with the AD LDP configuration. I tried connecting to another, known good instance of AD LDP and authentication worked first time.
I am going to mark this as the answer as I am no longer interested in a solution to this question and wish to close it down...

Authorization Server

I am using Spring Rest + OAUTH2 + React in my project. For creating authorization server I got some code from an example. But the problem is I am not able to understand the code. Can someone explain me this code:
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyPair keyPair = new KeyStoreKeyFactory(
new ClassPathResource("keystore.jks"), "suleman123".toCharArray())
.getKeyPair("resourcekey");
converter.setKeyPair(keyPair);
return converter;
}
/**
* This method configure client details service by using inMemory implementation. JDBC Implementation can also used
*/
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("acme") // client id
.secret("acmesecret") // required for trusted clients
.authorizedGrantTypes("authorization_code", "refresh_token",
"password") // Grant types that are authorized for the client to use
.scopes("openid") // scope to which the client is limited
.autoApprove(true);
}
/**
* This method configure the grant types. By default all grant types are supported except password
*/
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(authenticationManager).accessTokenConverter(
jwtAccessTokenConverter());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess(
"isAuthenticated()");
}
}
Spring boot works with auto-configuration. What you see here is someone extending a spring auto-configuration class in order to customize it to his needs.
TL;DR:
They set a JWT based oauth2 authorization server.
Detailed Answer:
In this case, by combining #EnableAuthorizationServer and extending AuthorizationServerConfigurerAdapter, you can enable, manipulate and modify your authorization server.
In this example, instead of using normal string tokens, they want to use JWT. For that reason the first bean initialized is JwtAccessTokenConverter. More on JWT.
configure(ClientDetailsServiceConfigurer clients) - They configure one in-memory client to use in the application.
configure(AuthorizationServerEndpointsConfigurer endpoints) - They configure the default authenticationManager as the one initialized by spring and injected in the top of your configuration class and set the accessTokenConverter to use jwtAccessTokenConverter mentioned in #1. Doing that will allow them to generate JWT tokens when queering for a new token.
configure(AuthorizationServerSecurityConfigurer oauthServer) - They set all endpoints to allow access to everything when there is a token authenticated user (oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");).

Spring security java config basic authentication filter

I am trying to add basic authentication for my APIs where the users will be authenticated based on their credential stored in MongoDB. I want to use java config instead of XML based config. So far what I have learnt is I have to create #Configuration by extending WebSecurityConfigurerAdapter and override configure method. In that I can add a custom filter by addFilterBefore().
But how can I get a Authentication header information in the filter, how to proceed if the user is authenticated. I have been googling a lot but didn't find any good example that will help a novice like me whose been into spring just for 1 week.
Does any one have a good tutorial or sample that can help me get started with this? Thanks in advance.
As example, you can use next solution.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().authenticationEntryPoint(getBasicAuthenticationEntryPoint());
}
#Bean
public BasicAuthenticationEntryPoint getBasicAuthenticationEntryPoint(){
BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
basicAuthenticationEntryPoint.setRealmName("Basic Authentication");
return basicAuthenticationEntryPoint;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
It works for me. But you need to implement UserDetailsService interface.
Spring automatically checks is user authenticated and tries to proceed authentication if not.

Resources