Spring Boot handle JWT manually - spring-boot

I'm currently working with OpenID and the JWT for authentication and try to work it out manually - just for understandy .. dont worry ;-)
So i created a Filter which extends AbstractAuthenticationProcessingFilter but it is not called after getting the JWT token. This is my security config:
#Configuration
#EnableWebSecurity
class WebSecurityConfig : WebSecurityConfigurerAdapter() {
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http
.addFilterAfter(OpenIdConnectFilter("/secure"), OpenIDAuthenticationFilter::class.java)
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
}
having a breakpoint in the attemptAuthentication of the OpenIdConnectFilter but it is never called, even no exception nothing .. just 403 on client side.
Edit:
So do you have any idea how to "intercept" the incoming request with my OpenIDConnectFilter to do the verification there?

Related

Spring Security: don't redirect to login page in case unauthorised

I have Spring Security with oAuth2 authorisation.
I use it for REST API.
My configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.authorizeRequests()
.antMatchers("/health").permitAll()
.antMatchers("/**").authenticated()
.and()
.oauth2Login()
.and()
.httpBasic();
}
}
I need to make all requests return me 401 when I didn't authorise.
But now when I'm not authorised I got redirect to /login page.
I need to use it like usual REST API: if I did authorise then get content, otherwise get 401 Unauthorised.
How I can make it?
Thanks in addition for help.
Basically you need to configure an AuthenticationEntryPoint which is invoked when Spring Security detects a non-authenticated request. Spring also gives you a handy implementation which enables you to return whatever HttpStatus you need:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
//rest of your config...
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
}

Spring Boot security not bypassing login page URL

I am using OAuth2 for security token but for few URLs like /login and /welcome I don't want it's access via OAuth2 token. I am using below code but for both /login and /welcome it is asking token and returning HTTP status 401.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers(HttpMethod.GET, "/login").permitAll()
.antMatchers(HttpMethod.GET, "/welcome").permitAll()
.anyRequest().authenticated();
}
}

Spring Security with OAuth2 losing session

We have a Spring Boot-based Gateway using Spring Security, OAuth2 login, and Zuul routing. It is also using Spring Session to store sessions in Redis. This Gateway stores an OAuth2 token in the session and forwards the OAuth2 Bearer token to backend services.
We have an issue where users are being signed out quite often. It appears this happens roughly hourly. We are not even quite sure what is causing this with all the different tools in place.
Our session cookie in the browser expires in a longer period of time. So I suspect it is either Spring invalidating the session, or the OAuth2 token expiring.
From a quick inspection of the code, it appears that OAuth2TokenRelayFilter supports refreshing the token. Is this correct?
How can track down the cause of this and fix it?
For reference, we are using these versions:
Spring Boot 2.1.12
Spring Cloud Greenwich.SR4
Here are some relevant snippets.
Our web security config for the web pages.
#Configuration
#EnableWebSecurity
#EnableOAuth2Sso
#Order(SecurityProperties.BASIC_AUTH_ORDER - 2)
#Profile("!security-disabled")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers("/login", "/login/**", "/favicon.ico").permitAll()
.antMatchers("/signout").authenticated()
.anyRequest().hasAnyRole("ADMIN", "MEMBER")
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.httpBasic()
.disable()
.formLogin()
.disable()
.logout()
.logoutUrl("/signout")
.deleteCookies("SESSION")
.and()
// #formatter:on
}
Security configuration for API paths.
#Configuration
#Order(SecurityProperties.BASIC_AUTH_ORDER - 2 - 10)
#Profile("!security-disabled")
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter
{
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.requestMatchers()
.antMatchers("/api/**")
.and()
.authorizeRequests()
.antMatchers("/**").hasAnyRole("ADMIN", "MEMBER")
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.headers()
.frameOptions().sameOrigin()
.and()
.httpBasic()
.disable()
.formLogin()
.disable()
.logout()
.disable()
.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint());
// #formatter:on
}
}
Update
We have done some debugging of the Spring internals. First, we found that we were missing an OAuth2RestTemplate. Per the OAuth2 Boot documentation we found how to add it with:
#Bean
public OAuth2RestTemplate oauth2RestTemplate(
OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails details)
{
return new OAuth2RestTemplate(details, oauth2ClientContext);
}
This is now throwing an exception when OAuth2TokenRelayFilter calls restTemplate.getAccessToken().getValue();.
A redirect is required to get the users approval
This exception is thrown from AuthorizationCodeAccessTokenProvider.
OAuth2TokenRelayFilter
OAuth2TokenRelayFilter is a pre type filter which set the contexts with ACCESS_TOKEN and TOKEN_TYPE which will be used for the further authentication. It validates the tokens using getAccessToken() method and responds with "Cannot obtain valid access token" with 401 status.
You may check the validity of tokens and refresh token is correctly configured with grant_type as refresh_token as The Refresh Token grant type is used by clients to exchange a refresh token for an access token when the access token has expired which allows clients to continue to have a valid access token without further interaction with the user.
In case if you want to disable OAuth2TokenRelayFilter, you may use the following
zuul.OAuth2TokenRelayFilter.pre.disable=true

Keycloak with Spring boot - How to apply resource scopes

I'm using Keycloak 7.0.1 with Spring Boot 1.5.16.RELEASE securing endpoint by specifying resources, role based polices and permissions - and that's works as expected.
The tricky things is to secure only POST and allow GET requests to one some particular URIs. What have I done in application.yml:
policy-enforcer-config:
enforcement-mode: ENFORCING
paths[0]:
name: all
path: /*
paths[1]:
name: test post
path: /my/url
methods[0]:
method: GET
scopes[0]: view
methods[1]:
method: POST
scopes[0]: edit
In keyclaok I've created edit and view scopes, /my/url resource, policy with role and negative decision (if user has that role - deny access), permission contains resource, scope and policy. Evaluation works as expected, but my spring application always receive 403 error.
Could you provide me with an example of resource scope usage or advice what else shout be done to make that working?
Could be multiple problems, but first of all, check your SecurityConfig.
This is what we have in place:
#Configuration
#EnableWebSecurity
#KeycloakConfiguration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableConfigurationProperties(KeycloakSpringBootProperties.class)
#RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
bla-bla..
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers(HttpMethod.POST, "/auth/**")
.antMatchers(HttpMethod.OPTIONS,"/**")
// allow anonymous resource requests
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/actuator/**"
)
;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// we don't need CSRF because our token is invulnerable
.csrf().disable()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(
getRestAuthenticationEntryPoint(),
new AntPathRequestMatcher("/**")
)
.authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// system state endpoint
.antMatchers("/ping").permitAll()
.antMatchers(HttpMethod.GET, "/whatever/you/need/to/open/to/public/one", "/whatever/you/need/to/open/to/public/two").permitAll()
// User authentication actions
.antMatchers("/auth/**").permitAll()
.antMatchers("/**/*.css").permitAll()
.anyRequest().authenticated()
;
http
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
;
// disable page caching
http
.headers()
.frameOptions().sameOrigin()
.cacheControl();
}
If you want to restrict REST API endpoints with role, add #PreAuthorize("hasRole('your.role.from.keycloak')") for your controller method

Spring Boot Security - Multiple configurations

I'm working (and struggling a little bit) on an example using spring-boot with spring security.
My system is using a web app and also provide an REST-API, so i would like to have form based security (web) and basic auth (resp api).
As the spring documentation recommend (https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#multiple-httpsecurity), I need to create a multi http web security configuration.
The main code works, but if I use Postman for the test of my RestApi following use-case does not work.
All GET-requests to /restapi/ working without authentication (statuscode 200)
All POST-requests to /restapi/ without the BASIC Auth Header are working (statuscode 401)
All POST-requests to /restapi/ with a correct BASIC Auth Header are work (statuscode 200)
BUT all requests with a wrong BASIC Auth header (f.e. user1/1234567) are returning the HTML-Loginpage defined in the first WebSecurityConfigurerAdapter (FormWebSecurityConfigurerAdapter)
Does anyone has an idea - what is wrong with my configuration?
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
private static RestAuthenticationAccessDeniedHandler restAccessDeniedHandler;
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.withUser("admin").password("{noop}12345678").roles("ADMIN").and()
.withUser("user").password("{noop}12345678").roles("USER");
}
#Configuration
#Order(1)
public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/restapi/**")
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/restapi/**").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(UNAUTHORIZED))
.and()
.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler) ;
}
}
/*
Ensures that any request to our application requires the user to be authenticated (execpt home page)
Requests matched against "/css/**", "/img/**", "/js/**", "/index.html", "/" are fully accessible
Allows users to authenticate with HTTP Form Based authentication
Configure logout with redirect to homepage
*/
#Configuration
public static class FormWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/img/**", "/js/**", "/index.html", "/").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/index.html")
.permitAll();
}
}
}
I know it is a question from some time ago but I still want to share the answer for people who are struggling with this issue.
After a lot of searching I found out that the /error endpoint in spring boot 2.x is now secured by default. What I mean to say is in the past the /error was a endpoint what had no security at all (or didn't exist). The solution to this issue is quite straight forward.
antMatchers('/error').permitAll()
within your web security adapter configuration(s).
What happens if you don't do this, the security will check the endpoint against your configuration and if it cannot find this endpoint (/error) it will redirect to the standard login form, hence the 302.

Resources