How to get SavedRequest in Spring Security 3.1.1? - spring-security

My example is
SavedRequest savedRequest = (SavedRequest) httpRequest.getSession()
.getAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY);
but this doesn't work in SpringSecurity 3.1.1

SavedRequest savedRequest = (SavedRequest)httpRequest.getSession().getAttribute(AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY);

Related

How to configure Spring OAuth2 in JHipster for stateless authentication against GitLab?

I have set up an OAuth2-based installation (spring-security-oauth2-core 5.2.4) of JHipster (3.9.0) against a gitlab repository. Mainly following https://www.jhipster.tech/security/#oauth2 but using gitlab instead of keycloak.
This is an excerpt from the application.yml:
spring: ...
security:
oauth2:
client:
provider:
oidc:
issuer-uri: https://gitlab.mysite.com
registration:
oidc:
client-id: 1234123412322312312312312312321312321312
client-secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Indeed the oauth2-authentication works well, until the session internally expires. When the client then returns the jsession-Id cookie, the underlying undertow-infrastructure throws a java.lang.IllegalStateException: UT000010: Session is invalid.
It seems that org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizationRequestRepository.saveAuthorizationRequest stores the authorizationRequest in the user session not caring about session management configuration: (see last line below)
#Override
public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request,
HttpServletResponse response) {
Assert.notNull(request, "request cannot be null");
Assert.notNull(response, "response cannot be null");
if (authorizationRequest == null) {
this.removeAuthorizationRequest(request, response);
return;
}
String state = authorizationRequest.getState();
Assert.hasText(state, "authorizationRequest.state cannot be empty");
Map<String, OAuth2AuthorizationRequest> authorizationRequests = this.getAuthorizationRequests(request);
authorizationRequests.put(state, authorizationRequest);
request.getSession().setAttribute(this.sessionAttributeName, authorizationRequests);
}
This approach seems to contradict the stateless JWT-based authentication approach of JHipster (see e.g. https://github.com/jhipster/generator-jhipster/issues/8627).
Anyway the spring2 OAuth2 management seems to counterfeit classical load balancing approaches.
Is there a way to configure OAuth2 management in spring to work stateless with GitLab?

Spring Security with oidc: refresh the tokens

Spring Boot 2 with Spring Security 5 can be configured to use an openID connect ID provider for authentication.
I managed to setup up my project just by configuring Spring Security - that works fine with all kinds of perfectly preconfigured security mechanisms like mitigation of session fixation.
But it seems that Spring Security does not refresh the tokens (which are stored in the session) by itself when they are expired.
Is there a setting for that or do I have to care for the refresh myself?
Update: Spring Boot 2.1 has been released, so it is time to revisit this problem. I still have no clue if the accessToken can now be automatically refreshed or if I have to write code for doing so...
According to the documentation,
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#webclient
When using a WebClient configured correctly, as given in the documentation it will automatically be refreshed.
Spring Security will automatically refresh expired tokens (if a refresh token is present)
This is also supported by the features matrix that refresh tokens are supported.
https://github.com/spring-projects/spring-security/wiki/OAuth-2.0-Features-Matrix
There was an older blog on Spring Security 5 that gives you access to beans that you could do this manually,
Authentication authentication =
SecurityContextHolder
.getContext()
.getAuthentication();
OAuth2AuthenticationToken oauthToken =
(OAuth2AuthenticationToken) authentication;
There will be an OAuth2AuthorizedClientService automatically configured as a bean in the Spring application context, so you’ll only need to inject it into wherever you’ll use it.
OAuth2AuthorizedClient client =
clientService.loadAuthorizedClient(
oauthToken.getAuthorizedClientRegistrationId(),
oauthToken.getName());
String refreshToken = client.getRefreshToken();
And, failing to find it right now, but I assume as part of the OAuth2AuthorizedClientExchangeFilterFunction has the calls to do a refresh.
According to https://github.com/spring-projects/spring-security/issues/6742 it seems that the token is intentionally not refreshed:
An ID Token typically comes with an expiration date. The RP MAY
rely on it to expire the RP session.
Spring does not. There are two enhancements mentioned at the end which should solve some of the refresh issues - both are still open.
As a workaround, I implemented a GenericFilterBean which checks the token and clears the authentication in the current security context. Thus a new token is needed.
#Configuration
public class RefreshTokenFilterConfig {
#Bean
GenericFilterBean refreshTokenFilter(OAuth2AuthorizedClientService clientService) {
return new GenericFilterBean() {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication instanceof OAuth2AuthenticationToken) {
OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
OAuth2AuthorizedClient client =
clientService.loadAuthorizedClient(
token.getAuthorizedClientRegistrationId(),
token.getName());
OAuth2AccessToken accessToken = client.getAccessToken();
if (accessToken.getExpiresAt().isBefore(Instant.now())) {
SecurityContextHolder.getContext().setAuthentication(null);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
};
}
}
Additionally I had to add the filter to the security config:
#Bean
public WebSecurityConfigurerAdapter webSecurityConfigurer(GenericFilterBean refreshTokenFilter) {
return new WebSecurityConfigurerAdapter() {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(refreshTokenFilter, AnonymousAuthenticationFilter.class)
Implemented with spring-boot-starter-parent and dependencies in version 2.2.7.RELEASE:
spring-boot-starter-web
spring-boot-starter-security
spring-boot-starter-oauth2-client
I appreciate opinions about this workaround since I'm still not sure if such an overhead is really needed in Spring Boot.
even a bounty of 100 rep points did not yield an answer. So I guess there is currently no mechanism implemented to automatically refresh the access token with Spring Security.
A valid alternative seems to use the spring boot keycloak adapter which is capable of refreshing the token.

Grails 3 with Spring boot autoconfigure

I'm trying to integrate a Grails application with the Netflix Eureka stuff for using Spring Cloud Ribbon for making REST calls to services. In a normal Spring Boot application it is nothing more than adding the required dependencies and the spring boot autoconfigure will make sure that my RestTemplate is configured for the Ribbon use.
But in our Grails (3.0.7) application the Spring Boot autoconfiguration will not kick in. Does anyone have an idea to get the Grails with Spring Boot autoconfigure working?
Found the problem. Spring boot's #AutoConfigure was working after all.
Problem when trying to use a Spring RestTemplate for rest with Ribbon:
class MyController {
RestTemplate restTemplate
def index() {
def result = restTemplate.getEntity("http://my-service/whatever", Void.class) // call gives nullPointerException due restTemplate is not injected
render "Response: $result"
}
}
Because Spring Boot registers the Ribbon enabled RestTemplate bean not under bean name restTemplate, the Grails convention based injection mechanism (field name must match bean name) doesn't work. To work around this problem it is needed to #Autowired to the restTemplate field and let Spring do the injection.
So this is the solution:
class MyController {
#AutoWired
RestTemplate restTemplate
def index() {
def result = restTemplate.getEntity("http://my-service/whatever", Void.class) // restTemplate is now injected using Spring instead of Grails
render "Response: $result"
}
}

Ignore WebSocket connection in Spring Security SavedRequest

I have a Grails application with spring-security-core plugin and Atmosphere framework.
If I log out from a page that has opened a WebSocket connection, then Spring Security keeps the URL of the WebSocket connection as SavedRequest.
DEBUG savedrequest.HttpSessionRequestCache - DefaultSavedRequest added to Session: DefaultSavedRequest[http://localhost:8080/formx/formX/update]
DEBUG savedrequest.HttpSessionRequestCache - DefaultSavedRequest added to Session: DefaultSavedRequest[http://localhost:8080/formx/formX/notifications/?X-Atmosphere-Transport=close&X-Atmosphere-tracking-id=b5d8fde4-d950-41fd-9b49-02e06799a36f&conversationId=988080042]
The first entry in the log has the correct value for SavedRequest, but somehow it is overwritten by the Atmosphere WebSocket connection.
How do I tell Spring Security to not use the Atmosphere WebSocket connection as SavedRequest?
I guess I can use some Atmosphere Protocol Specific Header to distinguish connections.
In Java config you can set the RequestMatcher - then it's easy.
In WebSecurityConfigurerAdapter:
protected void configure(HttpSecurity http) {
HttpSessionRequestCache cache = new HttpSessionRequestCache(); //this one is used by default
cache.setRequestMatcher(AnyRequestMatcher.INSTANCE); //change the request matcher, so it do not match your Atmosphere requests
http.requestCache().requestCache(cache);
}

Spring Security Access role

I have a j2ee web application running on spring web flow using spring security. How do I change during runtime my Role saved in the session?
If it's possible, it would be something like this:
SecurityContext context = SecurityContextHolder.getContext();
Object principal = context.getAuthentication().getPrincipal();
Object credentials = context.getAuthentication().getCredentials();
GrantedAuthority[] authorities = new GrantedAuthority[1];
authorities[0] = new GrantedAuthorityImpl("MY_NEW_ROLE");
Authentication auth = new UsernamePasswordAuthenticationToken(
principal, credentials, authorities);
SecurityContextHolder.getContext().setAuthentication(auth);

Resources