Grails spring security: auth failure redirect [duplicate] - grails

I'm using grails along with spring security and angularjs. When a user session has expired and the user clicks an ajax action on the page, rather than respond with a 401, the application attempts to redirect to the login page which no response from the original ajax action.
I'm still using a traditional login page and some my application still has some traditional page links, so when a session has expired and a user clicks a page link, I would like to redirect to the login page.
If a user clicks on an ajax request, I would like to get a 401 response rather than the redirected html response so that I can do a redirect in my javascript.
I have the following config setting.
grails.plugin.springsecurity.providerNames = ['hriLoginClientAuthenticationProvider']
grails.plugin.springsecurity.useSecurityEventListener = true
grails.plugin.springsecurity.failureHandler.defaultFailureUrl = '/login?error=1'
grails.plugin.springsecurity.auth.loginFormUrl = '/login'
grails.plugin.springsecurity.logout.postOnly = false
What do I need to do to get ajax request to not redirect to the login page?

I've run into a similar issue and have implemented a filter in the filter chain to detect AJAX requests and respond with a customized HTTP status (you can change it to 401 if you like).
Basically there are three parts to this. The first, is the filter. It's a servlet filter and examines the request as well as the state of the authentication in the session. Second, defining the filter as a bean within the application context in Resources.groovy. Finally, inserting it into the Spring Security filter chain, which I've done in Bootstrap.groovy.
I'll walk you through this now.
First the servlet filter (under src/java)
package com.xyz.security;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.security.web.util.ThrowableCauseExtractor;
import org.springframework.web.filter.GenericFilterBean;
public class AjaxTimeoutRedirectFilter extends GenericFilterBean {
// private static final Logger logger =
// LoggerFactory.getLogger(AjaxTimeoutRedirectFilter.class);
private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
private int customSessionExpiredErrorCode = 901;
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
// logger.debug("Chain processed normally");
} catch (IOException ex) {
throw ex;
} catch (Exception ex) {
Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
RuntimeException ase = (AuthenticationException) throwableAnalyzer
.getFirstThrowableOfType(AuthenticationException.class,
causeChain);
if (ase == null) {
ase = (AccessDeniedException) throwableAnalyzer
.getFirstThrowableOfType(AccessDeniedException.class,
causeChain);
}
if (ase != null) {
if (ase instanceof AuthenticationException) {
throw ase;
} else if (ase instanceof AccessDeniedException) {
if (authenticationTrustResolver
.isAnonymous(SecurityContextHolder.getContext()
.getAuthentication())) {
// logger.info("User session expired or not logged in yet");
String ajaxHeader = ((HttpServletRequest) request)
.getHeader("X-Requested-With");
if ("XMLHttpRequest".equals(ajaxHeader)) {
// logger.info("Ajax call detected, send {} error code",
// this.customSessionExpiredErrorCode);
HttpServletResponse resp = (HttpServletResponse) response;
resp.sendError(this.customSessionExpiredErrorCode);
} else {
// logger.info("Redirect to login page");
throw ase;
}
} else {
throw ase;
}
}
}
}
}
private static final class DefaultThrowableAnalyzer extends
ThrowableAnalyzer {
/**
* #see org.springframework.security.web.util.ThrowableAnalyzer#initExtractorMap()
*/
protected void initExtractorMap() {
super.initExtractorMap();
registerExtractor(ServletException.class,
new ThrowableCauseExtractor() {
public Throwable extractCause(Throwable throwable) {
ThrowableAnalyzer.verifyThrowableHierarchy(
throwable, ServletException.class);
return ((ServletException) throwable)
.getRootCause();
}
});
}
}
public void setCustomSessionExpiredErrorCode(
int customSessionExpiredErrorCode) {
this.customSessionExpiredErrorCode = customSessionExpiredErrorCode;
}
}
Second, defining the filter as a bean in the application context in Resources.groovy
beans = {
ajaxTimeoutRedirectFilter(com.xyz.security.AjaxTimeoutRedirectFilter)
}
And finally, getting the filter into the Spring Security filter chain (I used BootStrap.groovy for this)
import grails.plugin.springsecurity.SecurityFilterPosition
import grails.plugin.springsecurity.SpringSecurityUtils
class BootStrap {
def init = { servletContext ->
SpringSecurityUtils.clientRegisterFilter('ajaxTimeoutRedirectFilter', SecurityFilterPosition.EXCEPTION_TRANSLATION_FILTER.order + 10)
}
def destroy = {
}
}

Did you consider "locking a screen" when the user is idle on a client-side? Of course you should handle end of a session on server-side but in fact it seems even cleaner and more secure solution than waiting for an action from client side (especially if user has left and left on a screen some sensitive data).
Check out this ng-idle directive.

Related

My SparkJava resource server gets 403 errors when trying to validate access tokens

I want to set up a very basic REST API using Spark-java, which just checks an access token obtained from my own authorisation server. It creates a GET request to the authorisation server's /oauth/authorize endpoint followed by ?token=$ACCESS_TOKEN.
Whenever I try this, I get diverted to the /error endpoint and a 403 error.
Here's my API class:
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.utils.StringUtils;
import java.io.IOException;
import static spark.Spark.*;
public class SampleAPI {
private static final Logger logger = LoggerFactory.getLogger("SampleAPI");
public static void main(String[] args) {
// Run on port 9782
port(9782);
// Just returns "Hello there" to the client's console
before((preRequest, preResponse) -> {
System.out.println("Getting token from request");
final String authHeader = preRequest.headers("Authorization");
//todo validate token, don't just accept it because it's not null/empty
if(StringUtils.isEmpty(authHeader) || !isAuthenticated(authHeader)){
halt(401, "Access not authorised");
} else {
System.out.println("Token = " + authHeader);
}
});
get("/", (res, req) -> "Hello there");
}
private static boolean isAuthenticated(final String authHeader) {
String url = "http://localhost:9780/oauth/authorize";
//"Bearer " is before the actual token in authHeader, so we need to extract the token itself as a substring
String token = authHeader.substring(7);
HttpGet getAuthRequest = new HttpGet(url + "?token=" + token);
getAuthRequest.setHeader("Content-Type", ContentType.APPLICATION_FORM_URLENCODED.getMimeType());
CloseableHttpClient httpClient = HttpClients.createMinimal();
try {
CloseableHttpResponse response = httpClient.execute(getAuthRequest);
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("Status code " + statusCode + " returned for access token " + authHeader);
return statusCode == 200;
} catch (IOException ioException) {
System.out.println("Exception when trying to validate access token " + ioException);
}
return false;
}
}
The System.out.println statements are just for debugging.
Here's my authorisation server's WebSecurityConfigurerAdapter class:
package main.config;
import main.service.ClientAppDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity(debug = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
//returns AuthenticationManager from the superclass for authenticating users
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder getPasswordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
//Allow for DB access without any credentials
web.ignoring().antMatchers("/h2-console/**");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//configures user details, and uses the custom UserDetailsService to check user credentials
auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
//disable CORS and CSRF protection for Postman testing
http.cors().disable().anonymous().disable();
http.headers().frameOptions().disable();
http.csrf().disable();
}
}
And here's my authorisation server's application.properties:
server.port=9780
#in-memory database, will get populated using data.sql
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=admin
spring.datasource.password=syst3m
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.properties.hibernate.format_sql=true
#adds to existing DB instead of tearing it down and re-populating it every time the app is started
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false
What have I done wrong? Do I need to specify my API as a resource server using Spring Security? Do I need to add it to the authorisation server's application.properties?
If you want to use Spring as a security framework then the most common option is to configure it as a resource server. Here is a getting started tutorial. The API will then never get redirected.
With Spark another option is to just provide a basic filter that uses a JWT validation library, such as jose4j. This tends to provide better control over error responses and gives you better visibility over what is going on. See this Kotlin example, which will be easy enough to translate to Java.

Multiple rooms authorisation with Spring WebSocket and security

I'm making multi rooms chat with user authorization: users can have access only to some assigned rooms.
For every room I creating a topic with unique room id
How can I check permissions during the opening socket for reading?
On the server-side, for new inbound connection, I want to get room id from topic URL and check user access permissions for the room. But I didn't find how I can do it. I don't see the place, there it's possible.
AbstractSecurityWebSocketMessageBrokerConfigurer -- no way for dynamic check
#Configuration
class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry message) {
message.nullDestMatcher().permitAll()
.simpDestMatchers("/app/**").authenticated()
.anyMessage().hasRole("USER")
}
}
WebSocketMessageBrokerConfigurer -- can't get current url
#Configuration
class WebSocketSecurityConfig extends WebSocketMessageBrokerConfigurer {
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.SUBSCRIBE.equals(accessor.getCommand())) {
...
}
return message
}
});
}
}
I know, how to check access during writing messages, but can't find, how to do it during opening a web socket for reading. What is the standard mechanism for this case?
Dependencies:
compile 'org.grails.plugins:grails-spring-websocket:2.5.0.RC1'
compile "org.springframework.security:spring-security-messaging"
compile "org.springframework.security:spring-security-config"
compile "org.springframework.security:spring-security-core:5.1.8.RELEASE"
compile "org.springframework:spring-messaging:5.1.6.RELEASE"
UPDATE
I can pass room id from the client as a header, but on the server in configureClientInboundChannel I can't be sure, that room id in header same with id in topic URL. I can use some hashes, generated on the server-side, but it looks too complex
var socket = new SockJS("${createLink(uri: '/stomp')}");
var client = webstomp.over(socket);
client.connect({room-id:"0"}, function() {
client.subscribe("/topic/room/1", function(message) {
console.log("/topic/room/1");
}, {roomId:"1"});
client.subscribe("/topic/room/2", function(message) {
console.log("/topic/room/2");
}, {roomId:"2"});
});
During debugging, I have checked headers of command with type StompCommand.CONNECT.
For StompCommand.SUBSCRIBE command current topic URL presented in simpDestination header
Final solution is:
#Configuration
class WebSocketSecurityConfig extends WebSocketMessageBrokerConfigurer {
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.SUBSCRIBE.equals(accessor.getCommand())) {
def currentAuthentication = accessor.getHeader('simpUser') // from spring security
String destinationUrl = (String )accessor.getHeader('simpDestination')
// do check, and throw AuthenticationException
}
return message
}
});
}
}

OAuthTAI Authentication failed with Status = 401, WWW-Authenticate: Bearer realm="imfAuthentication", scope="UserAuthRealm" in MFP Version 7.1

Problem:
I am using MFP 7.1 and oauth.tai_1.0.0.jar in my android application for app authenticity and have defined the realm on MFP's end. Each time I try to register to the application I see in the log
OAuthTAI Authentication failed with Status = 401, WWW-Authenticate: Bearer realm="imfAuthentication", scope="UserAuthRealm"
This is not preventing the application flow. I am only getting this error in the log and this error is seen before the realm class's init method is initialized and after that everything works fine.
I am wondering why I am getting this error.
Analysis:
I have checked the challenge handler in android, it is fine. I also did a fresh installation of the app in order to be sure of a new access token being sent from MFP.
I had also checked in MFP' Oauth jar and checked the 401 error case, it checks for invalid_token and invalid_authorization. But in my case, none of these two are there as I am not getting this in error description. I have the custom authenticator class defined which is mapped to UserAuthReal, code below:
CustomUserAuthenticator.java
package com.ibm.mfp;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.worklight.core.auth.impl.AuthenticationContext;
import com.worklight.server.auth.api.AuthenticationResult;
import com.worklight.server.auth.api.AuthenticationStatus;
import com.worklight.server.auth.api.MissingConfigurationOptionException;
import com.worklight.server.auth.api.UserIdentity;
import com.worklight.server.auth.api.WorkLightAuthenticator;
public class CustomUserAuthenticator implements WorkLightAuthenticator {
private static final long serialVersionUID = -548850541866024092L;
private static final Logger logger = Logger.getLogger(CustomUserAuthenticator.class.getName());
private String pin;
private String userName;
private String uniqueID;
private String userNumber;
private String userAuthFlag;
private String registrationNumber;
protected Map<String, Object> authenticationData;
public void init(Map<String, String> options) throws MissingConfigurationOptionException {
logger.info("CustomUserAuthenticator initialized");
}
public AuthenticationResult processRequest(HttpServletRequest request, HttpServletResponse response,
boolean isAccessToProtectedResource) throws IOException, ServletException {
String clientID = AuthenticationContext.getCurrentClientId();
logger.info("CustomUserAuthenticator :: processRequest : clientID : " + clientID);
String requestURI = request.getRequestURI();
logger.info("CustomUserAuthenticator :: processRequest : request.getRequestURI() :" + requestURI);
String requestQueryString = request.getQueryString();
requestQueryString = null;
logger.info("CustomUserAuthenticator :: processRequest : request.getQueryString() :" + requestQueryString);
// Request the epin from the user
if (request.getRequestURI().contains("/ADIBMBA/auth/v2/auth")) {
this.pin = request.getParameter("pin");
this.userName= request.getParameter("userName");
this.uniqueID = request.getParameter("uniqueID");
this.userNumber = request.getParameter("userNumber");
this.userAuthFlag = request.getParameter("userAuthFlag");
this.registrationNumber = request.getParameter("registrationNumber");
if (null != this.customerNumber) {
logger.info(
"CustomUserAuthenticator :: processRequest : request.getRequestURI() : getParameter customerNumber : "
+ this.customerNumber);
}
if (null != pin && pin.length() > 0) {
return AuthenticationResult.createFrom(AuthenticationStatus.SUCCESS);
} else {
response.setContentType("application/json; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.getWriter().print("{\"authStatus\":\"required\", \"errorMessage\":\"Please enter epin\"}");
return AuthenticationResult.createFrom(AuthenticationStatus.CLIENT_INTERACTION_REQUIRED);
}
}
if (!isAccessToProtectedResource) {
return AuthenticationResult.createFrom(AuthenticationStatus.REQUEST_NOT_RECOGNIZED);
}
response.setContentType("application/json; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.getWriter().print("{\"authStatus\":\"required\"}");
return AuthenticationResult.createFrom(AuthenticationStatus.CLIENT_INTERACTION_REQUIRED);
}
public boolean changeResponseOnSuccess(HttpServletRequest request, HttpServletResponse response)
throws IOException {
String requestURI2 = request.getRequestURI();
logger.info("CustomUserAuthenticator :: changeResponseOnSuccess : request ");
logger.info("CustomUserAuthenticator :: changeResponseOnSuccess : response ");
// first worked partially with if
// (request.getRequestURI().contains("/ADIBMBA/auth/v2/auth")){
if (request.getRequestURI().contains("/ADIBMBA/mainapps/services/apis/App/iOSnative")
|| (request.getRequestURI().contains("/ADIBMBA/auth/v2/auth"))) {
response.setContentType("application/json; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.getWriter().print("{\"authStatus\":\"complete\"}");
return true;
}
return false;
}
public AuthenticationResult processAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
String errorMessage) throws IOException, ServletException {
logger.info("CustomUserAuthenticator :: processAuthenticationFailure");
response.setContentType("application/json; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.getWriter().print("{\"authStatus\":\"failed\", \"errorMessage\":" + errorMessage + ","
+ (String) authenticationData.get("error") + "}");
return AuthenticationResult.createFrom(AuthenticationStatus.CLIENT_INTERACTION_REQUIRED);
}
public AuthenticationResult processRequestAlreadyAuthenticated(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
logger.info("CustomUserAuthenticator :: processRequestAlreadyAuthenticated");
return
AuthenticationResult.createFrom(AuthenticationStatus.
REQUEST_NOT_RECOGNIZED);
}
public Map<String, Object> getAuthenticationData() {
authenticationData = new HashMap<String, Object>();
authenticationData.put("userName", userName);
authenticationData.put("uniqueID", uniqueID);
authenticationData.put("pin", pin);
authenticationData.put("userNumber", userNumber);
authenticationData.put("userAuthFlag", userAuthFlag);
authenticationData.put("registrationNumber", registrationNumber);
return authenticationData;
}
public HttpServletRequest getRequestToProceed(HttpServletRequest request, HttpServletResponse response,
UserIdentity userIdentity) throws IOException {
return null;
}
#Override
public WorkLightAuthenticator clone() throws CloneNotSupportedException {
CustomUserAuthenticator otherAuthenticator = (CustomUserAuthenticator) super.clone();
return otherAuthenticator;
}
}
Summary:
If the application flow is normal then why i am getting this OAuthTAI 401 error in log. Suppose If it is a problem related to token & id token then i should not be able to access protected resource data. Application should not allow me to proceed further.
From the description and comments, it appears you have mixed up Liberty's OAuth TAI and MFP's OAuth security model.
MFP's OAuth security model is used to protect MFP resources ( adapters and runtime endpoints), while Liberty's OAuth TAI is to protect resources deployed on Liberty server ( eg: web applications).
The link you followed details steps using which MFP server can act as the OAuth server for resources deployed in Liberty server.
From the description and the custom Authenticator that extends WorklightAuthenticator you are depending on the security validation to be done by MFP's security framework. If the requirement is for your MFP adapters to be protected by OAuth security and the device begins by obtaining an OAuth token from MFP server, then you should use MFP's OAuth security and not resort to Liberty OAuth TAI. MFP's OAuth security framework works out of the box, without any need to configure TAI.
Refer to the following links for a better understanding and working samples:
a) MFP OAuth security model
b) Java adapters
c) Custom Authenticator

Spring security OAuth2 - invalidate session after authentication

We are securing out REST services using spring security OAuth2. Applications can call into either the /oauth/authorize, /oauth/token or /rest-api endpoints. The token and rest-api endpoints are stateless and do not need a session.
Can we invalidate the session after the user is authenticated? If so, what is the best approach. We want the user to sign-in always whenever a call to /oauth/authorize is made. Currently, calls to /oauth/authorize are skipping authentication whenever a session exists.
Understanding that the question is a bit old, I hope that the following could be helpful for those who search for the correct answer for the question
OP asked not about tokens invalidation, but how to invalidate httpSession on Spring OAuth2 server right after user authentication successfully passed and a valid access_token or authorization_code (for subsequent getting of access_token) returned to a client.
There is no out-of-the-box solution for this use-case still. But working workaround from the most active contributor of spring-security-oauth, Dave Syer, could be found here on GitHub
Just copy of the code from there:
#Service
#Aspect
public class SessionInvalidationOauth2GrantAspect {
private static final String FORWARD_OAUTH_CONFIRM_ACCESS = "forward:/oauth/confirm_access";
private static final Logger logger = Logger.getLogger(SessionInvalidationOauth2GrantAspect.class);
#AfterReturning(value = "within(org.springframework.security.oauth2.provider.endpoint..*) && #annotation(org.springframework.web.bind.annotation.RequestMapping)", returning = "result")
public void authorizationAdvice(JoinPoint joinpoint, ModelAndView result) throws Throwable {
// If we're not going to the confirm_access page, it means approval has been skipped due to existing access
// token or something else and they'll be being sent back to app. Time to end session.
if (!FORWARD_OAUTH_CONFIRM_ACCESS.equals(result.getViewName())) {
invalidateSession();
}
}
#AfterReturning(value = "within(org.springframework.security.oauth2.provider.endpoint..*) && #annotation(org.springframework.web.bind.annotation.RequestMapping)", returning = "result")
public void authorizationAdvice(JoinPoint joinpoint, View result) throws Throwable {
// Anything returning a view and not a ModelView is going to be redirecting outside of the app (I think).
// This happens after the authorize approve / deny page with the POST to /oauth/authorize. This is the time
// to kill the session since they'll be being sent back to the requesting app.
invalidateSession();
}
#AfterThrowing(value = "within(org.springframework.security.oauth2.provider.endpoint..*) && #annotation(org.springframework.web.bind.annotation.RequestMapping)", throwing = "error")
public void authorizationErrorAdvice(JoinPoint joinpoint) throws Throwable {
invalidateSession();
}
private void invalidateSession() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
HttpSession session = request.getSession(false);
if (session != null) {
logger.warn(String.format("As part of OAuth application grant processing, invalidating session for request %s", request.getRequestURI()));
session.invalidate();
SecurityContextHolder.clearContext();
}
}
}
add pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
Another solution could be to set session time out to some very small value. The simplest way to achieve that is put the following to application.yml config:
server:
session:
timeout: 1
But it's not ideal solution as the minimum value could be provider is 1 (zero is reserved for infinite sessions) and it is in minutes not in seconds
From what I understand, you are trying to programmatically logout after you have undertaken certain set of actions. Probably you should look into the SecurityContextLogoutHandler and see how it works. There is a method for logout there. I think calling it as an advice will solve your problem.
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
Assert.notNull(request, "HttpServletRequest required");
if (invalidateHttpSession) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
}
SecurityContextHolder.clearContext();
}
First: in your configuration declare bean with token store for oauth
#Bean
#Primary
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
For controller approach we made the following class
#Controller
public class TokenController {
#RequestMapping(value = "/oauth/token/revoke", method = RequestMethod.POST)
public #ResponseBody void create(#RequestParam("token") String value) {
this.revokeToken(value);
}
#Autowired
TokenStore tokenStore;
public boolean revokeToken(String tokenValue) {
OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
if (accessToken == null) {
return false;
}
if (accessToken.getRefreshToken() != null) {
tokenStore.removeRefreshToken(accessToken.getRefreshToken());
}
tokenStore.removeAccessToken(accessToken);
return true;
}
}
If you don't wan't to use this approach you can grab current user's token autowiring Principal:
OAuth2Authentication authorization = (OAuth2Authentication) principal;
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authorization.getDetails();
String token = details.getTokenValue();
Or even autowiring OAuth2Authentication:
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
String token = details.getTokenValue();
I can offer such an option (according to #de_xtr recomendation):
import static org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes;
#Slf4j
#Component
#Aspect
public class InvalidateSessionAspect {
private final LogoutHandler logoutHandler;
public InvalidateSessionAspect() {
logoutHandler = new SecurityContextLogoutHandler();
}
#Pointcut("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")
public void postAccessTokenPointcut() {
}
#AfterReturning(value = "postAccessTokenPointcut()", returning = "entity")
public void invalidateSession(JoinPoint jp, Object entity) {
log.debug("[d] Trying to invalidate the session...");
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) currentRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
logoutHandler.logout(request, null, null);
log.debug("[d] Session has been invalidated");
}
}
And the option without any aspects:
#Slf4j
class LogoutHandlerInterceptor implements HandlerInterceptor {
#Override
public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object h, ModelAndView view) {
HttpSession session = req.getSession(false);
if (session != null) {
log.debug("[d] Trying to invalidate the session...");
session.invalidate();
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(null);
SecurityContextHolder.clearContext();
log.debug("[d] Session has been invalidated");
}
}
}
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
//...
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.addInterceptor(new LogoutHandlerInterceptor())
// ...
;
}
}

Open ID End point forward is not working

I am new to Open Id development.I have downloaded openid4java sample application from the internet and trying to implement the same in mine.Till now i have written the code to hit to open id end point after discover.its working till discovering.But after that when trying to hit the end point URI I am getting 404 error because its appending my project URL path also.
Ex:
/Openid/http:/www.myopenid.com/server.(here Openid is my project name).
This is my servlet :
package com.openid.registration;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.message.AuthRequest;
public class OpenIdRegistrationServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
private String returnToUrl;
RequestDispatcher rd = null;
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
HttpSession session=request.getSession(false);
String OpenID=request.getParameter("openid");
System.out.println("Open ID entered by the user"+OpenID);
// Delegate to Open ID code
DiscoveryInformation discoveryInformation = RegistrationService.performDiscoveryOnUserSuppliedIdentifier(OpenID);
// Store the disovery results in session.
System.out.println("OPEnd Point"+discoveryInformation.getOPEndpoint());
session.setAttribute("discoveryInformation", discoveryInformation);
// Create the AuthRequest
returnToUrl=RegistrationService.getReturnToUrl();
AuthRequest authRequest = RegistrationService.createOpenIdAuthRequest(discoveryInformation, returnToUrl);
rd = request.getRequestDispatcher(authRequest.getDestinationUrl(true));
System.out.println("Destination URL:"+authRequest.getDestinationUrl(true));
rd.forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request,response);
// TODO Auto-generated method stub
}
/**
* #see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request,response);
// TODO Auto-generated method stub
}
}
I have deployed my application in tomcat 5.Is there any way to remove my project name from the URL or do i need to redirect from apache webserver ? Any help is appreciated
Its my mistake.I have changed forward(request, response) to sendRedirect(authRequest.getDestinationUrl(true)).Its started working fine.

Resources