Handling sessions and remembering logged in user with vertx - java

Currently when a user logs in to my web server using a web POST form, a custom authenticator and a custom user. I have the CustomUser put into the Session provided by the RoutingContext because, when using RoutingContext#setUser it only changes the user for that request and as soon as the user is redirected from the login processing page to their destination the CustomUser has been lost.
However, it also seems as though the Session in RoutingContext for the new page doesn't have any user stored in the entry where the auth placed the CustomUser, could this be sending a completely different Session?
Routing:
//ROUTE DEFINITIONS
// SESSION AND COOKIE
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)).setNagHttps(false)); //TODO SSL
router.route().handler(CookieHandler.create());
// STATIC
router.route("/").handler(new StaticHandler()); //BASE
router.route("/admin").handler(new StaticHandler()); //ADMIN PAGE
// FORM REQUESTS
router.route("/login").handler(new AuthAndRegHandler(new CustomAuth(), dbController)); //LOGIN REQUEST
router.route("/logout").handler(new AuthAndRegHandler(new CustomAuth(), dbController)); //LOGOUT REQUEST
// AJAX
router.route("/ajax/updateInvoice").handler(new AjaxHandler());
// ERRORS
router.route().failureHandler(new ErrorHandler());
router.route().handler(handle -> {
handle.fail(404);
});
//END DEFINITIONS
AuthAndRegHandler:
public class AuthAndRegHandler extends AuthHandlerImpl {
private DatabaseController db;
private CustomAuth authProvider;
public AuthAndRegHandler(CustomAuth authProvider, DatabaseController db) {
super(authProvider);
this.db = db;
this.authProvider = authProvider;
}
#Override
public void handle(RoutingContext event) {
Logger log = LoggerFactory.getLogger(this.getClass());
HttpServerResponse response = event.response();
HttpServerRequest request = event.request();
Session session = event.session();
String requestedPath = request.path();
authProvider.setJdbc(db.getJdbc()); //returns a JDBCClient
if(requestedPath.equalsIgnoreCase("/login")) {
if(request.method() != HttpMethod.POST)
event.fail(500);
else {
request.setExpectMultipart(true);
request.endHandler(handle -> {
MultiMap formAtts = request.formAttributes();
String email = formAtts.get("email");
String pw = formAtts.get("password");
log.info(email + ":" + pw + " login attempt");
authProvider.authenticate(new JsonObject()
.put("username", email)
.put("password", pw), res -> {
if(res.succeeded()) {
CustomUser userToSet = (CustomUser) res.result();
session.put("user", userToSet);
log.info("Login successful for " + email);
response.putHeader("Location", "/").setStatusCode(302).end();
} else {
event.fail(500);
log.error("Auth error for " + request.host());
}
});
});
}
}
}
}
CustomAuth returns true every time for testing purposes.
StaticHandler
CustomUser user = session.get("user");
event.setUser(user);
response.putHeader("Content-Type", "text/html");
if(user != null) {
log.info(user.principal().getString("email") + " user detected");
event.setUser(user);
} else
log.info("Null user request detected"); //Constantly outputs, even after a login form has been submitted
I'm not entirely sure what's going wrong here. Vertx has sub-optimal documentation for a rookie like myself on session and handling things without their out-of-the-box implementations. Any help on how to log someone in and maintain their session like a normal website would be appreciated.

For those who stumble upon the same problem, but usually skip the comments:
Vert.x SessionHandler depends on CookieHandler, and the order is important here.
From the Vert.x examples:
router.route().handler(CookieHandler.create());
router.route().handler(sessionHandler);

Related

How to use Spring OAuth2 Client in SPA and multi-node application?

I want to implement a feature that user connects his account with external applications (similar feature is in Facebook). User has to log in to external application and grant permission to access data by my application.
Once user connected an external app, data will be exchanged in background using access and refresh tokens.
Application architecture is:
SPA front-end (Angular)
REST API (Spring), multiple nodes
ScyllaDB
Envoy proxy (with JWT verification)
The first idea is to use Spring OAuth2 Client. However, some changes need to be made:
there is no Principal because JWT is verified by Envoy proxy and X-USER-ID header is added
REST API is stateless and we shouldn't store authorization code in session
even with sessions, there are multiple nodes and we need to share authorization code between nodes
custom URL, e.g. /app_name/connect instead of /oauth2/authorization/app_name
redirect URL may be invalid (but it's verified by Spring's filter)
How this could work:
user click "Connect with app" in SPA
SPA redirects user to /oauth2/authorization/app_name (or custom URL)
Spring redirects user to external app's authentication server
user authenticates and grants permissions
external app redirects user back to Spring (or straight to SPA?)
Spring redirects user back to SPA (or SPA sends access token to REST API?)
Despite Spring Security components can be replaced, many of them are coupled and you need to rewrite OAuth2 Client flow almost from scratch. Maybe I'm doing something wrong and it can be achieved easier.
What I already did:
http
.cors().and()
.csrf().disable()
.authorizeRequests().anyRequest().permitAll().and()
.oauth2Client(); // get rid of these two filters?
#Configuration
#RequiredArgsConstructor
public class OAuth2ClientConfig {
private final CassandraTemplate cassandraTemplate;
// overriding original client service - we need to store tokens in database
#Bean
public OAuth2AuthorizedClientService authorizedClientService(
CassandraTemplate cassandraTemplate,
ClientRegistrationRepository clientRegistrationRepository) {
return new ScyllaOAuth2AuthorizedClientService(cassandraTemplate, clientRegistrationRepository);
}
// configure client provider to use authorization code with refresh token
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
var authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.build();
var authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository,
authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
// the specs recommend to use WebClient for exchanging data instead of RestTemplate
#Bean
public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
// override request repository - and I'm stuck there
#Bean
public AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() {
return new ScyllaOAuth2AuthorizationRequestRepository(cassandraTemplate);
}
}
Because there are multiple nodes of REST API, we can't use sessions. We need to store request somewhere, e.g. ScyllaDB, Redis, Hazelcast, etc. I decided to store it as JSON in ScyllaDB but I ran into trouble.
#Slf4j
#RequiredArgsConstructor
public final class ScyllaOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
private final CassandraTemplate cassandraTemplate;
private final ObjectMapper objectMapper = new ObjectMapper();
#Override
public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
Assert.notNull(request, "request cannot be null");
var stateParameter = this.getStateParameter(request);
if (stateParameter == null) {
return null;
}
return this.getAuthorizationRequest(request, stateParameter);
}
#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;
}
var state = authorizationRequest.getState();
var userId = UUID.fromString(request.getHeader(Constants.USER_ID));
Assert.hasText(state, "authorizationRequest.state cannot be empty");
try {
// serialization of Auth2AuthorizationRequest to JSON works
cassandraTemplate.getCqlOperations().execute("insert into oauth2_requests (user_id,state,data) values (?,?,?)",
userId, state, objectMapper.writeValueAsString(authorizationRequest));
} catch (JsonProcessingException e) {
log.warn("Unable to save authorization request", e);
}
}
#Override
public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request) {
Assert.notNull(request, "request cannot be null");
var stateParameter = this.getStateParameter(request);
if (stateParameter == null) {
return null;
}
var userId = UUID.fromString(request.getHeader(Constants.USER_ID));
var originalRequest = this.getAuthorizationRequest(request, stateParameter);
cassandraTemplate.getCqlOperations().execute("delete from oauth2_requests where user_id=? and state=?",
userId, stateParameter);
return originalRequest;
}
private String getStateParameter(HttpServletRequest request) {
return request.getParameter(OAuth2ParameterNames.STATE);
}
private UUID getUserId(HttpServletRequest request) {
return UUID.fromString(request.getHeader(Constants.USER_ID));
}
private OAuth2AuthorizationRequest getAuthorizationRequest(HttpServletRequest request, String state) {
var userId = getUserId(request);
var jsonRequest = cassandraTemplate.getCqlOperations().queryForObject(
"select data from oauth2_requests where user_id=? and state=?", String.class, userId, state);
if (StringUtils.isNotBlank(jsonRequest)) {
try {
// trying to mess with OAuth2ClientJackson2Module
var objectMapper = new Jackson2ObjectMapperBuilder().autoDetectFields(true)
.autoDetectGettersSetters(true)
.modules(new OAuth2ClientJackson2Module())
.visibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY)
.build();
return objectMapper.readValue(jsonRequest, OAuth2AuthorizationRequest.class);
} catch (JsonProcessingException e) {
log.warn("Error decoding authentication request", e);
}
}
return null;
}
}
I get error when trying to deserialize JSON to OAuth2AuthorizationRequest:
Missing type id when trying to resolve subtype of [simple type, class org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest]: missing type id property '#class'
Without adding OAuth2ClientJackson2Module there is another error:
Cannot construct instance of `org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
By the way, OAuth2ClientJackson2Module seems never used in original filters.
Maybe it's better to serialize this object Java way and store it as BLOB or do not store request in database but somewhere other.
Another part is the controller action:
// it had to be /apps/app_name/connect but in Spring OAuth2 Client it's hardcoded to append provider name at the end
#GetMapping("/apps/connect/app_name")
public void connect(HttpServletRequest request, HttpServletResponse response) throws IOException {
userAppService.authorize(request, response, "app_name");
}
To get rid of filters which verify redirect URL and have many things hardcoded:
#Service
#RequiredArgsConstructor
public class UserAppService {
private final HttpSecurity httpSecurity;
private final AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private final AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
private final ClientRegistrationRepository clientRegistrationRepository;
private final OAuth2AuthorizedClientManager authorizedClientManager;
private final OAuth2AuthorizedClientRepository authorizedClientRepository;
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public void authorize(HttpServletRequest request, HttpServletResponse response, String appName) throws IOException {
var userId = UUID.fromString(request.getHeader(Constants.USER_ID));
var authorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId(appName)
.principal(UUIDPrincipal.fromUserId(userId))
.build();
if (isAuthorizationResponse(request)) {
var authorizationRequest = this.authorizationRequestRepository.loadAuthorizationRequest(request);
if (authorizationRequest != null) {
processAuthorizationRequest(request, response);
}
} else {
try {
OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest);
if (authorizedClient != null) {
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
System.out.println(accessToken);
}
} catch (ClientAuthorizationException e) {
// in this URL provider name is appended at the end and no way to change this behavior
var authorizationRequestResolver = new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository,
"/apps/connect");
var authorizationRequest = authorizationRequestResolver.resolve(request);
this.authorizationRequestRepository.saveAuthorizationRequest(authorizationRequest, request, response);
this.redirectStrategy.sendRedirect(request, response, authorizationRequest.getAuthorizationRequestUri());
}
}
}
private void processAuthorizationRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
var authorizationRequest = this.authorizationRequestRepository.removeAuthorizationRequest(request, response);
var registrationId = (String) authorizationRequest.getAttribute(OAuth2ParameterNames.REGISTRATION_ID);
var clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
var params = toMultiMap(request.getParameterMap());
var redirectUri = UrlUtils.buildFullRequestUrl(request);
var authorizationResponse = convert(params, redirectUri);
var authenticationRequest = new OAuth2AuthorizationCodeAuthenticationToken(
clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
OAuth2AuthorizationCodeAuthenticationToken authenticationResult;
try {
var authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
authenticationResult = (OAuth2AuthorizationCodeAuthenticationToken) authenticationManager
.authenticate(authenticationRequest);
} catch (OAuth2AuthorizationException ex) {
OAuth2Error error = ex.getError();
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(authorizationRequest.getRedirectUri())
.queryParam(OAuth2ParameterNames.ERROR, error.getErrorCode());
if (!StringUtils.hasText(error.getDescription())) {
uriBuilder.queryParam(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription());
}
if (!StringUtils.hasText(error.getUri())) {
uriBuilder.queryParam(OAuth2ParameterNames.ERROR_URI, error.getUri());
}
this.redirectStrategy.sendRedirect(request, response, uriBuilder.build().encode().toString());
return;
}
// just copy-paste of original filter - trying to understand what's happening there
Authentication currentAuthentication = SecurityContextHolder.getContext().getAuthentication();
String principalName = (currentAuthentication != null) ? currentAuthentication.getName() : "anonymousUser";
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
authenticationResult.getClientRegistration(), principalName, authenticationResult.getAccessToken(),
authenticationResult.getRefreshToken());
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, currentAuthentication, request,
response);
String redirectUrl = authorizationRequest.getRedirectUri();
this.redirectStrategy.sendRedirect(request, response, redirectUrl);
}
private static boolean isAuthorizationResponse(HttpServletRequest request) {
return isAuthorizationResponseSuccess(request) || isAuthorizationResponseError(request);
}
private static boolean isAuthorizationResponseSuccess(HttpServletRequest request) {
return StringUtils.hasText(request.getParameter(OAuth2ParameterNames.CODE))
&& StringUtils.hasText(request.getParameter(OAuth2ParameterNames.STATE));
}
private static boolean isAuthorizationResponseError(HttpServletRequest request) {
return StringUtils.hasText(request.getParameter(OAuth2ParameterNames.ERROR))
&& StringUtils.hasText(request.getParameter(OAuth2ParameterNames.STATE));
}
// copy paste - not tested this code yet
static MultiValueMap<String, String> toMultiMap(Map<String, String[]> map) {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(map.size());
map.forEach((key, values) -> {
if (values.length > 0) {
for (String value : values) {
params.add(key, value);
}
}
});
return params;
}
static OAuth2AuthorizationResponse convert(MultiValueMap<String, String> request, String redirectUri) {
String code = request.getFirst(OAuth2ParameterNames.CODE);
String errorCode = request.getFirst(OAuth2ParameterNames.ERROR);
String state = request.getFirst(OAuth2ParameterNames.STATE);
if (StringUtils.hasText(code)) {
return OAuth2AuthorizationResponse.success(code).redirectUri(redirectUri).state(state).build();
}
String errorDescription = request.getFirst(OAuth2ParameterNames.ERROR_DESCRIPTION);
String errorUri = request.getFirst(OAuth2ParameterNames.ERROR_URI);
return OAuth2AuthorizationResponse.error(errorCode)
.redirectUri(redirectUri)
.errorDescription(errorDescription)
.errorUri(errorUri)
.state(state)
.build();
}
}
Client service to stored authorized clients in database:
#RequiredArgsConstructor
public class ScyllaOAuth2AuthorizedClientService implements OAuth2AuthorizedClientService {
private final CassandraTemplate cassandraTemplate;
private final ClientRegistrationRepository clientRegistrationRepository;
#Override
#SuppressWarnings("unchecked")
public OAuth2AuthorizedClient loadAuthorizedClient(String clientRegistrationId, String principal) {
var id = BasicMapId.id("userId", principal).with("appCode", clientRegistrationId);
var userApp = cassandraTemplate.selectOneById(id, UserApp.class);
if (userApp != null) {
var clientRegistration = getClientRegistration(clientRegistrationId);
var accessToken = getAccessToken(userApp);
var refreshToken = getRefreshToken(userApp);
return new OAuth2AuthorizedClient(clientRegistration, principal, accessToken, refreshToken);
} else {
return null;
}
}
#Override
public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
Assert.notNull(authorizedClient, "authorizedClient cannot be null");
Assert.notNull(principal, "principal cannot be null");
var userApp = new UserApp();
userApp.setUserId((UUID) principal.getPrincipal());
userApp.setAppCode(authorizedClient.getClientRegistration().getClientId());
if (authorizedClient.getAccessToken() != null) {
userApp.setAccessToken(authorizedClient.getAccessToken().getTokenValue());
userApp.setAccessTokenType(OAuth2AccessToken.TokenType.BEARER.getValue());
userApp.setAccessTokenScopes(authorizedClient.getAccessToken().getScopes());
userApp.setAccessTokenIssuedAt(authorizedClient.getAccessToken().getIssuedAt());
userApp.setAccessTokenExpiresAt(authorizedClient.getAccessToken().getExpiresAt());
}
if (authorizedClient.getRefreshToken() != null) {
userApp.setRefreshToken(authorizedClient.getRefreshToken().getTokenValue());
userApp.setRefreshTokenIssuedAt(authorizedClient.getRefreshToken().getIssuedAt());
userApp.setRefreshTokenExpiresAt(authorizedClient.getRefreshToken().getExpiresAt());
}
cassandraTemplate.insert(userApp);
}
#Override
public void removeAuthorizedClient(String clientRegistrationId, String principal) {
var id = BasicMapId.id("userId", principal).with("appCode", clientRegistrationId);
cassandraTemplate.deleteById(id, UserApp.class);
}
private ClientRegistration getClientRegistration(String clientRegistrationId) {
var clientRegistration = this.clientRegistrationRepository.findByRegistrationId(clientRegistrationId);
if (clientRegistration == null) {
throw new DataRetrievalFailureException(
"The ClientRegistration with id '" + clientRegistrationId + "' exists in the data source, "
+ "however, it was not found in the ClientRegistrationRepository.");
}
return clientRegistration;
}
private OAuth2AccessToken getAccessToken(UserApp userApp) {
return new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER,
userApp.getAccessToken(),
userApp.getAccessTokenIssuedAt(),
userApp.getAccessTokenExpiresAt(),
userApp.getAccessTokenScopes());
}
private OAuth2RefreshToken getRefreshToken(UserApp userApp) {
return new OAuth2RefreshToken(userApp.getRefreshToken(), userApp.getRefreshTokenIssuedAt());
}
}
Too much code overwrite. I need to make it as simple as possible.
Currently I'm struggling with storing authorize request in database.
How to do it Spring way but to keep the app architecture given at the beginning of this question?
Any way to configure OAuth2 Client without hardcoded URL like /oauth2/authorization/provider_name?
Maybe it's better to do the whole OAuth2 flow client-side (within SPA) and the SPA should send access and request token to REST API (to store the tokens in order to be able to exchange data with external app)?
In OAuth2 wording, REST APIs are resource-servers, not clients.
What you can do is have
your proxy be transparent to OAuth2 (forward requests with their JWT access-token authorization header and responses status code)
configure each REST API as resource-server. Tutorials there: https://github.com/ch4mpy/spring-addons/tree/master/samples/tutorials.
add an OAuth2 client library to your Angular app to handle tokens and authorize requests. My favorite is angular-auth-oidc-client
probably use an intermediate authorization-server for identity federation (Google, Facebook, etc., but also internal DB, LDAP, or whatever is needed), roles management, MFA,... Keycloak is a famous "on premise" solution, but you can search for "OIDC authorization-server" in your favorite search engine and have plenty of alternate choices, including SaaS like Auth0 or Amazon Cognito.
This is fully compatible with distributed architectures and micro-services (session-less is the default configuration for resource-servers in the tutorials I linked).
Two cases for a micro-service delegating some of its processing to another resource-server:
the "child" request is made on behalf of the user who initiated the request => retrieve original access-token from Authentication instance in security-context and forward it (set it as Bearer authorization header for the sub-request)
the "child" request is not made on behalf of a user => client-credentials must be used (the micro-services acquires a new access-token in its own name to authorize the sub request). Refer to spring-boot-oauth2-client and your preferred REST client docs for details (WebClient, #FeignClient, RestTemplate).

How do I look up an HttpSession by it's Id in a Springboot based application?

I want to obtain a HttpSession object by URL Path variable id to get some attributes from it.
Context:
I'm trying to implement a web server that has a register and login sub-systems as a learning exercise.
I'm using JAVA, Springboot and various other spring dependencies like hibernate, jdbc, etc.
I got the behavior I wanted, but as I tested my logic with an Android client application I encountered that the register confirmation link I send, does not work if I access it from another device, because the device-sender has a different session and thus my logic fails.
The flow of my registration is as follows:
User POSTs at /register -> { name, email, password }
Server saves this information in their session and sends confirmation email with /register/confirm/{token}
As the user GETs at /register/confirm/{token} that was send to their email,
the server checks if this token is contained in their session and commits the information from the session to the database.
Of course if I register from the device and try to confirm through another device they'd have different sessions and hence the temp information would not be available to the other device, but this is the same user trying to register and I'm looking for a work around. The way I decided to change my code is to send the user /register/confirm/{sessionId}+{token} to their email, but I can't find my way around obtaining the other HttpSession.
(#ServletComponentScan)
I tried to create a HttpSessionListener and tried to maintain a HashMap of HttpSession's but for some reason the Framework would instantiate the Listener object, but never send createSession events to it thus it's HashMap is always empty, thus {sessionId} is never found.
To provide some extra code for context.
My Listener:
#WebListener
public class SessionLookUpTable implements HttpSessionListener {
static final HashMap<String, HttpSession> sessionHashMap = new HashMap<>();
public SessionLookUpTable() {
super();
System.out.println("-------------- Session Listener Created"); // DEBUG
}
// Always empty for some reason, despite constructor being called
static public Optional<HttpSession> findSessionById(String sessionId) {
if (!sessionHashMap.containsKey(sessionId))
return Optional.empty();
return Optional.of( sessionHashMap.get( sessionId ) );
}
#Override
public void sessionCreated(HttpSessionEvent se) {
HttpSessionListener.super.sessionCreated(se);
HttpSession session = se.getSession();
sessionHashMap.put( session.getId(), session );
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
HttpSessionListener.super.sessionDestroyed(se);
sessionHashMap.remove(se.getSession().getId() );
}
};
The controller entry points
#PostMapping("/register")
public String register(HttpSession session,
#RequestParam("email") String username,
#RequestParam("password") String password,
#RequestParam("password2") String pw2)
{
User user = new User();
user.setUsername(username);
user.setPassword(password);
user.setPrivilegeLevel( Role.USER_PRIVILEGE_NORMAL );
if(session.getAttribute(ATTRIBUTE_USER_ID) != null) {
return "Already registered";
}
if(!userService.isUserDataValid(user)) {
return "Invalid input for registry";
}
if(userService.usernameExists(user.getUsername())) {
return "User already exists";
}
session.setAttribute(ATTRIBUTE_REGISTER_DATA, user);
String token = userService.sendConfirmationEmail( session );
if(token != null) {
session.setAttribute(ATTRIBUTE_USER_ID, 0L );
session.setAttribute(ATTRIBUTE_REGISTER_TOKEN, token);
}
return "A link was sent to your email.";
}
#RequestMapping("/register/confirm/{sessionId}+{token}")
void confirmRegister(HttpSession sessionIn,
#PathVariable("sessionId") String sessionId,
#PathVariable("token") String token) {
Optional<HttpSession> optSession = SessionLookUpTable.findSessionById( sessionId );
if(optSession.isEmpty())
return;
HttpSession session = optSession.get();
// Multiple confirmations guard
Long userId = (Long)session.getAttribute(ATTRIBUTE_USER_ID);
if( userId != null && userId != 0L ){
return;
}
String sessionToken = (String)session.getAttribute(ATTRIBUTE_REGISTER_TOKEN);
if(!sessionToken.equals(token)) {
return;
}
User user = (User)session.getAttribute(ATTRIBUTE_REGISTER_DATA);
user.setDateRegistered( LocalDate.now() );
Long id = userService.register( user );
session.setAttribute(ATTRIBUTE_USER_ID, id);
}
I'm stuck at this stage for quite a while, so any help is appreciated. Thank you.

How to authenticate logged in user when refreshing single page application using AngularJS without "Routing"?

I searched a lot of resources but none was appropriate to my problem.I am working on single page application (SPA) project ,and I want a logged in user to stay logged in whenever he refreshes the page but without routing.
I have tried to call session authentication servlet in the main controller of the page(this servlet checks whether the session exists or not),but it did not work.
Note: The session is created once the user log in or sing up.
Here is SessionAuthServlet.java:
HttpSession session = request.getSession(true);
User u=(User) session.getAttribute("usersession");
try{
response.setContentType("application/json; charset=UTF-8");
PrintWriter out = response.getWriter();
if(u != null)
{
out.println("{\"+success+\"}");
out.close();
}
else
{
out.println("{ \"result\": \"fail\"}");
out.close();
}
}catch (IOException e) {
e.printStackTrace();
}
MainController in HTML single page application:
appvar.controller('MianController',['$scope','$http','$rootScope',function($scope, $http,$rootScope) {
$rootScope.sessionvalid=function(){
$http.get("http://localhost:8080/MyProject/SessionAuthServlet")
.success(function(response) {
if (response.result=="fail")
{
//***Show the view for not logged user
}
//***Show the view for logged user
}
$rootScope.sessionvalid();
});
}
}]);
Any ideas how to deal with this?
Please guide me
Thanks
Here is how you can stay logged after page refresh without using routing.
You will need below three things
A angular service to hold user information and if he is authenticated or not.
A window sessionstorage to save user information. Even if the page is refereshed the user information will persist in sessionstorage
An interceptor to set request and response.
Service code -
app.service('AuthenticationService', function() {
var auth = {
isLogged: false,
email:"",
isAdmin:false
}
return auth;
});
In your MainController, once user is logged in set the Service AuthenticationService.isLogged = true and $window.sessionStorage = userInfo
Interceptor code-
app.service('TokenInterceptor', function ($q, $window, $location, AuthenticationService) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
return config;
},
/* Set Authentication.isAuthenticated to true if 200 received */
response: function (response) {
if (response != null && response.status == 200 && $window.sessionStorage.token && !AuthenticationService.isAuthenticated) {
AuthenticationService.isAuthenticated = true;
}
return response || $q.when(response);
}
};
});
and in your app.config block add this -
app.config(function($httpProvider){
$httpProvider.interceptors.push(TokenInterceptor);
})
Now your AuthenticationService.isLogged will remain true even if the page is refershed and you can get the logged in user info in the service.

Setting a Attribute for Every Request (Spring REST)

I am doing a rest app in spring and i have a log out method like below. I dont have knowledge on spring so i just searched around and made this.
#RestController
public class LogoutController {
#Autowired
private DatabaseService databaseService;
#RequestMapping(value = "/myApp/user/logout", method = GET, produces = {"application/xml"})
public Users performLogout(#RequestHeader("AuthenticationID") String authID, HttpServletRequest request) throws DatatypeConfigurationException {
return handleLogout(request, authID);
}
private Users handleLogout(HttpServletRequest request, String authID) throws DatatypeConfigurationException {
LogService.info(this.getClass().getName(), "Received Logout Request");
final UsersXMLBuilder usersXMLBuilder = new UsersXMLBuilder();
Users usersXML = usersXMLBuilder.buildDefaultUsersTemplate();
HttpSession session = request.getSession();
AppUtilities utils = new AppUtilities();
try {
//Checking with RegEX
if (utils.isValidUUID(authToken)) {
//Get User Login Record from DB By the AuthID and Delete It
//Invalidate The Session
session.invalidate();
LogService.info(this.getClass().getName(), "Session Invaliated");
} else {
LogService.info(this.getClass().getName(), "Invalid AuthID Found. Not a Valid UUID");
usersXML.setResponseCode(-5);
usersXML.setResponseText("User Session is Not Valid");
}
} catch (Exception ex) {
LogService.error(this.getClass().getName(), ex);
usersXML.setResponseCode(-4);
usersXML.setResponseText("Error Occured!");
return usersXML;
} finally {
LogService.info(this.getClass().getName(), "LogOut Process Finished");
}
return usersXML;
}
}
Questions
1- Is it possible i can return a XML message when spring gives white label error page when i pass no authentication ID in request.
2- How can i get the Authentication Header and check it for null and give message that AuthID is missing.
3- How can i set a attribute explicitly and check for it in every controller that if it exists or not.
4- I plan to have a table where i can store user login time and give a session 10 mins time and Update it more 10 mins if i get any request from the user with the AuthID. So can i have a class or method which can check any incoming request? so i can detect the AuthID and Update My table.
Thank you for your time and help.
You can use an interceptor for that : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-handlermapping-interceptor
The interceptor will run for every request. It can stop the request and do a response itself.

How can I get the current logged in gmail user from cloud end point?

I am trying to get the current email id of the logged in google user. I tried something like the following which works in dev mode but not in production mode.
public class EndpointAPI {
#ApiMethod(httpMethod = HttpMethod.GET, path = "getuser")
public Container getLoggedInUser() {
UserService userService = UserServiceFactory.getUserService();
User guser = userService.getCurrentUser();
Container container = new Container();
container.user = "user not logged in";
if (null != guser)
container.user = guser.getEmail();
return container;
}
public class Container {
public String user;
}
}
I tried looking at the documentation (and tried adding client ids, scope etc) but could not successfully find what I need to do.
If someone can post a simple working example it will be much appreciated.
Regards,
Sathya
At simplest, you should register a client ID for a web application, and request a User object within the method signature of your API call. Example that supports requests from the JS client:
Ids.java:
public class Ids {
public static final String WEB_CLIENT_ID = "12345.apps.googleusercontent.com";
}
MyEndpoint.java:
#Api(clientIds = {Ids.WEB_CLIENT_ID})
public class MyEndpoint {
public getFoo(User user) throws OAuthRequestException {
if (user != null) {
// do something with user
} else {
throw new OAuthRequestException("Invalid user.");
}
}
}
user will automatically be populated with the current user represented by the token passed to your API, or null in the case of an invalid or missing token. The example above throws an exception when there isn't a valid user object, but you can also choose to allow unauthenticated access.
Check out the docs for more.

Categories

Resources