getting online users using JWT tokens

I want to create a service that returns a list of online users. The user logs on using JWT tokens — but I am unsure how to get online users — or/and return a list of users but indicate if they are online or not.

Would I have to stash session tokens/emails that have logged on/logged off in the mongodb – and what if they don’t log out?

My current code looks like this

@CrossOrigin
@GetMapping("/api/getActiveUsers")
public ResponseEntity<Object> activeUser(HttpServletRequest request) {
    TokenManagement user = new TokenManagement();
    try {
        // return the user authenticate
        HttpSession session = request.getSession(true);
        List<Object> userListHttp = (List<Object>) Arrays.asList(session.getAttribute("user"));
        // alternative way for get user logged
        List<String> userList = getUsersFromSessionRegistry();
        return ResponseEntity.ok().body(userListHttp); 
    } catch (Exception e) {
        e.printStackTrace();
        throw e;
    }
}


public List<String> getUsersFromSessionRegistry() {
    return sessionRegistry.getAllPrincipals()
            .stream()
            .filter((u) -> !sessionRegistry.getAllSessions(u, false)
            .isEmpty())
            .map(o -> {
                if (o instanceof Person) {
                    return ((Person) o).getEmail();
                } else {
                    return o.toString();
                }
            }).collect(Collectors.toList());
}

Answer

You can extract the user from the JWT with its expiring.

Then you can use a cache (consider Redis for example) storing the user on a record that automatically expires when the JWT expires.

If a user explicitly logout simply remove that user from the cache.

So to count the users, you need only to count items in the cache.

This will not grant you that if a client has an error and disconnects without an explicit logout you will have an error on the logged user number, but it is mitigated by the expiring of the cache

I suggest to use a Redis instead of a local cache because it will works also if you are in a microservice environment with multiple instances of your micro service, because the logged users are stored in an external cache common to all microservices instances


You can use a Filter to intercept all the HTTP incoming requests:

A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both.

public class SessionFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        HttpServletRequest req = (HttpServletRequest) request;
        String bearer = req.getHeader("authorization");
        String jwt = bearer.substring(7); // Remove Beared at the beginning 
        String username = extractUsername(jwt);
        Date expiringDate = extractExpiring(jwt);
        insertInCache(username, expiringDate);
        chain.doFilter(request, response);
    }

   ...

    private String extractUsername(String jwt) {
        // Use libraries to extract the username from the jwt 
    }

    private Date extractExpiring(String jwt) {
        // Use libraries to extract the expiring from the jwt 
    }

    private void insertInCache(String username, Date expiringDate) {
        // Insert username in the cache with automatic expiring
    }
    

}

This code will intercept all incoming requests, extract the token, parse it and insert the user in the cache. Consider using any java library to extract the informations from the JWT to create the methods extractUsername and extractExpiring.

This code is just a base code to program. You need to complete it with:

  • manage not authenticated requests
  • manage explicit logout (in the controller)
  • add a method to count users inquiring cache (in the controller)