Can anyone suggest how to validate (coming from realiable source,not expired) JWT token coming in header in spring webflux - spring

Can anyone suggest how to validate (coming from realiable source,not expired) JWT token coming in header in spring boot webflux.

Implement ServerSecurityContextRepository and override Mono<SecurityContext> load(ServerWebExchange exchange) method.
Then extract the token from ServerWebExchange and perform validation.
And to use custom security context do:
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
#RequiredArgsConstructor
public class BaseSecurityConfig implements WebFluxConfigurer {
protected final ServerSecurityContextRepository serverSecurityContextRepository;
protected ServerHttpSecurity basicSecurity(ServerHttpSecurity http) {
return http.securityContextRepository(serverSecurityContextRepository)
...
}
}

Related

Spring Cloud Gateway - Intercept under hood request/response to Keycloak IDP

We are implementing a Spring Cloud Gateway application (with Webflux) that is mediating the OAuth2 authentication with Keycloak.
SCG checks if the Spring Session is active: if not, redirects to Keycloak login page and handles the response from the IDP. This process is executed out-of-the-box by the framework itself.
Our needs is to intercept the IDP Keycloak response in order to retrieve a field from the response payload.
Do you have any advices that will help us to accomplish this behavior?
Thanks!
You can implement ServerAuthenticationSuccessHandler:
#Component
public class AuthenticationSuccessHandler implements ServerAuthenticationSuccessHandler {
private ServerRedirectStrategy redirectStrategy;
public AuthenticationSuccessHandler(AuthenticationService authenticationService) {
redirectStrategy = new DefaultServerRedirectStrategy();
}
#Override
public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
if(authentication instanceof OAuth2AuthenticationToken) {
//Your logic here to retrieve oauth2 user info
}
ServerWebExchange exchange = webFilterExchange.getExchange();
URI location = URI.create(httpRequest.getURI().getHost());
return redirectStrategy.sendRedirect(exchange, location);
}
}
And update your security configuration to include success handler:
#Configuration
public class SecurityConfiguration {
private AuthenticationSuccessHandler authSuccessHandler;
public SecurityConfiguration(AuthenticationSuccessHandler authSuccessHandler) {
this.authSuccessHandler = authSuccessHandler;
}
#Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange(exchange -> exchange
//other security configs
.anyExchange().authenticated()
.and()
.oauth2Login(oauth2 -> oauth2
.authenticationSuccessHandler(authSuccessHandler)
);
return http.build();
}
}

Evaluate Web Services Interceptor Before Spring Security Filter Chain

I have a SOAP-based web services application which is leveraging Spring Web Services (and Spring WS Security) as well as Spring Security. I am using a custom AbstractWsSecurityInterceptor to authenticate the incoming requests (using an injected AuthenticationManager) and to add the successful authentications to the SecurityContext. I then have a custom AcessDecisionManager which is using a custom WebSecurityExpressionHandler to validate a certain property from the principal added to the context by the interceptor.
Below is an idea of what my configuration files look like:
SecurityConfig.java:
#Getter
#Setter
#Configuration
#RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final AccessDecisionManager customAccessDecisionManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
cors()
.and().csrf().disable()
.authorizeRequests()
.accessDecisionManager(customAccessDecisionManager)
.antMatchers(GET, "/actuator/**").permitAll()
.anyRequest().access("customAccessMethod()")
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
WebServiceConfig.java:
#EnableWs
#Configuration
#RequiredArgsConstructor
public class WebServiceConfig extends WsConfigurerAdapter {
private final AuthenticationManager authenticationManager;
#Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}
...
...
#Bean
AbstractWsSecurityInterceptor customAuthenticationInterceptor() {
return new CustomAuthenticationInterceptor(authenticationManager);
}
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(customAuthenticationInterceptor());
}
}
The issue with this setup is that the Spring Security filter chain is evaluated first and fails the authentication because the AccessDecisionManager is evaluated before the request has a chance to enter the custom AbstractWsSecurityInterceptor and place the authentication in the SecurityContext.
Is there any way to evaluate the interceptor and handling of the request on the Web Services and WS Security side of things before it then hits the Spring Security filter chain? Is this a possibility?
Thank you in advance for the help!

How to enable CSRF for all request in Spring Security

As spring disables CSRF token for certain methods like GET, how we can enable CSRF token validation for all requests including GET using spring security.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().requireCsrfProtectionMatcher(AnyRequestMatcher.INSTANCE);
}
}
Note this configuration means csrf token is required for any request ,that's unnecessary and hard to achieve

Run a Spring Boot oAuth2 application as resource server AND serving web content

I'm using Spring Boot 1.5.13 and with that Spring Security 4.2.6 and Spring Security oAuth2 2.0.15.
I want to find a best practice setup for our Spring Boot applications that serve a mixed set of content: A REST API, and some web pages that provide a convenience "landing page" for developers with some links on it, plus Swagger based API documentation, which is also web content.
I have a configuration that allows me to run the app with proper authorization code flow, hence I can access all web content via Browser and get authenticated by the configured IdP (in my case PingFederate), plus I can make API calls from within the Browser, i.e. directly or with a REST Client, e.g. with RESTClient.
This is my security configuration:
#Slf4j
#Configuration
#EnableWebSecurity
#EnableOAuth2Sso // this annotation must stay here!
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login**", "/webjars/**", "/css/**").permitAll()
.antMatchers("/cfhealth").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/protected", "/api/**").authenticated();
}
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
}
and the oAuth2 configuration:
#Configuration
#Slf4j
public class OAuth2Config extends ResourceServerConfigurerAdapter {
#Value("${pingfederate.pk-uri}")
String pingFederatePublicKeyUri;
#Autowired
PingFederateKeyUtils pingFederateKeyUtils;
#Override
public void configure(ResourceServerSecurityConfigurer config) {
config.tokenServices(tokenServices());
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
String certificate = pingFederateKeyUtils.getKeyFromServer(pingFederatePublicKeyUri);
String publicKey = pingFederateKeyUtils.extractPublicKey(certificate);
converter.setVerifier(pingFederateKeyUtils.createSignatureVerifier(publicKey));
return converter;
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
}
But when I want to call a REST API programmatically/outside the Browser with a bearer token in the header, e.g. with curl, the authorization code flow kicks in and redirects to the local login endpoint. What I want is that API calls accept the bearer token for authentication, without creating a session, and that all web content/mvc calls in the Browser establish a session.
curl -i -H "Accept: application/json" -H "Authorization: Bearer $TOKEN" -X GET http://localhost:8080/authdemo/api/hello
Adding the #EnableResourceServer annotation to the above SecurityConfig class (and adding security.oauth2.resource.filter-order=3 in the application properties file, I can make the curl command work, but then the authorization code flow is broken, I get the following output in the Browser for all URLs in my application:
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
Now is there a way to get this szenario working nicely? If yes, how would that look like? Or is it only supported in later versions of Spring Boot+Security+oAuth2?
The question at Spring Boot with Security OAuth2 - how to use resource server with web login form? is quite similar
I found the solution: It takes multiple HttpSecurity configurations. I found out by reading the great article written by Matt Raible at https://developer.okta.com/blog/2018/02/13/secure-spring-microservices-with-oauth where he introduced me to the notion of requestMatchers(.). This is how I finally implemented it:
#Configuration
#EnableResourceServer
#EnableWebSecurity(debug = true)
#EnableOAuth2Sso
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(new RequestHeaderRequestMatcher("Authorization"))
.authorizeRequests().anyRequest().fullyAuthenticated();
}
}
With that I can access the service with a Browser, leading to a authorization code flow. But accessing the API (or actually any part of the service) leads to a validation of the provided Bearer token.
And to illustrate the way how some endpoints can be exluded/made public in such a case, here's how I configure the actuator endpoints and one very simple 'ping' endpoint I've added myself:
#Configuration
#Order(1)
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new OrRequestMatcher(EndpointRequest.to("health", "info"),
new AntPathRequestMatcher("/cfhealth"))).authorizeRequests().anyRequest().permitAll();
}
}
And my implementation of the /cfhealth endpoint:
#Controller
#Slf4j
public class MainController {
#GetMapping(value = "/cfhealth")
#ResponseBody
public String cfhealth() {
return "ok";
}
}
I'm happy to learn from others if that's the best practice way of Spring Security configuration or if there are better ways to do it. I've spent quite some time on the topic in the last few weeks on it, and it takes quite some effort to grasp the basic Spring Security concepts.

Spring Boot 1.3.3 #EnableResourceServer and #EnableOAuth2Sso at the same time

I want my server be a ResourceServer, which can accept a Bearer Access token
However, If such token doesn't exist, I want to use the OAuth2Server to authenticate my user.
I try to do like:
#Configuration
#EnableOAuth2Sso
#EnableResourceServer
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
However, in this case, only the #EnableResourceServer annotation works. It returns
Full authentication is required to access this resource
And do not redirect me to the login page
I mentioned that the #Order is important, if I add the #Order(0) annotation,
I will be redirect to the login page, however, I cannot access my resource with the access_token in Http header:
Authorization : Bearer 142042b2-342f-4f19-8f53-bea0bae061fc
How can I achieve my goal? I want it use Access token and SSO at the same time.
Thanks~
Using both configuration on same request would be ambiguous. There could be some solution for that, but more clear to define separate request groups:
OAuth2Sso: for users coming from a browser, we want to redirect them to the authentication provider for the token
ResourceServer: usually for api requests, coming with a token they got from somewhere (most probably from same authentication provider)
For achieving this, separate the configurations with request matcher:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Bean("resourceServerRequestMatcher")
public RequestMatcher resources() {
return new AntPathRequestMatcher("/resources/**");
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http
.requestMatcher(resources()).authorizeRequests()
.anyRequest().authenticated();
}
}
And exclude these from the sso filter chain:
#Configuration
#EnableOAuth2Sso
public class SsoSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("resourceServerRequestMatcher")
private RequestMatcher resources;
#Override
protected void configure(final HttpSecurity http) throws Exception {
RequestMatcher nonResoures = new NegatedRequestMatcher(resources);
http
.requestMatcher(nonResoures).authorizeRequests()
.anyRequest().authenticated();
}
}
And put all your resources under /resources/**
Of course in this case both will use the same oauth2 configuration (accessTokenUri, jwt.key-value, etc.)
UPDATE1:
Actually you can achieve your original goal by using this request matcher for the above configuration:
new RequestHeaderRequestMatcher("Authorization")
UPDATE2:
(Explanation of #sid-morad's comment)
Spring Security creates a filter chain for each configuration. The request matcher for each filter chain is evaluated in the order of the configurations.
WebSecurityConfigurerAdapter has default order 100, and ResourceServerConfiguration is ordered 3 by default. Which means ResourceServerConfiguration's request matcher evaluated first. This order can be overridden for these configurations like:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration configuration;
#PostConstruct
public void setSecurityConfigurerOrder() {
configuration.setOrder(3);
}
...
}
#Configuration
#EnableOAuth2Sso
#Order(100)
public class SsoSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
So yes, request matcher is not needed for SsoSecurityConfiguration in the above sample. But good to know the reasons behind :)

Resources