2

I'm working on this Spring Security implementation with OAuth2 and JWT:

According to the author I can access resources using token this way:

To access a resource use (you'll need a different application which has configured ResourceServer):

http localhost:8080/users 'Authorization: Bearer '$ACCESS_TOKEN

About this step:

To use the refresh token functionality:

http --form POST adminapp:password@localhost:9999/oauth/token grant_type=refresh_token refresh_token=$REFRESH_TOKEN

It's not clear for me when I need to refresh the token and how to handle this part into Angular. When the Token expires do I need to first send request to the endpoint for refreshing the token and then to the login page?

How this case should be implemented?

Romil Patel
  • 12,879
  • 7
  • 47
  • 76
Peter Penzov
  • 1,126
  • 134
  • 430
  • 808
  • Do not use something like `HttpServletRequest` in your endpoints to get the "requester username", that should be included in the code of your project used to check if every request verifies your "security requirements". At that point, you should included the username, roles, etc in the Spring principal user allowing to get such information in other parts of your code – doctore Jun 30 '20 at 18:52
  • You should always to send the JWT in your requests, that is the way you will be able to check if the given one pass the security functionality you have included in your project. Regarding to `refresh token`, your login action should return, at least, an `access` and `refresh token`. Use the access token in every request and the refresh one when your security funtionality returns "something like" `You have no problems related with security stuff, but the provided access token has expired` (with the suitable Http code for that) – doctore Jun 30 '20 at 18:55
  • Is there any good code example that you can show me? – Peter Penzov Jun 30 '20 at 18:59
  • Here you have an explanation for that: https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/ – doctore Jul 04 '20 at 10:36
  • @PeterPenzov, in the angular side, you can listen for a token expired response from backend using an interceptor. Once you get a token expired response, you need to request for new token using the refresh token you have currently(in browser storage). If the refresh token is valid(means not expired) you will get a new set of tokens(access token and refresh token). With this you can retry the failed REST api call. In case refresh token itself is expired then redirect to login page. I think this may help you for the integration – Midhun Mohan Jul 10 '20 at 05:01

1 Answers1

4

At the time of authentication, two JWTs will be created - access token and refresh token. Refresh token will have longer validity. Both the tokens will be written in cookies so that they are sent in every subsequent request.

On every REST API call, the tokens will be retrieved from the HTTP header. If the access token is not expired, check the privileges of the user and allow access accordingly. If the access token is expired but the refresh token is valid, recreate new access token and refresh token with new expiry dates and sent back through Cookies


Access tokens carry the necessary information to access a resource directly. In other words, when a client passes an access token to a server managing a resource, that server can use the information contained in the token to decide whether the client is authorized or not. Access tokens usually have an expiration date and are short-lived.

Refresh tokens carry the information necessary to get a new access token. In other words, whenever an access token is required to access a specific resource, a client may use a refresh token to get a new access token issued by the authentication server. Common use cases include getting new access tokens after old ones have expired, or getting access to a new resource for the first time. Refresh tokens can also expire but are rather long-lived.


High level code

authenticate()

public ResponseEntity<OAuth2AccessToken> authenticate(HttpServletRequest request, HttpServletResponse response, Map<String, String> params) {
        try {
            String username = params.get("username");
            String password = params.get("password");
            boolean rememberMe = Boolean.valueOf(params.get("rememberMe"));
            OAuth2AccessToken accessToken = authorizationClient.sendPasswordGrant(username, password);
            OAuth2Cookies cookies = new OAuth2Cookies();
            cookieHelper.createCookies(request, accessToken, rememberMe, cookies);
            cookies.addCookiesTo(response);
            if (log.isDebugEnabled()) {
                log.debug("successfully authenticated user {}", params.get("username"));
            }
            return ResponseEntity.ok(accessToken);
        } catch (HttpClientErrorException ex) {
            log.error("failed to get OAuth2 tokens from UAA", ex);
            throw new BadCredentialsException("Invalid credentials");
        }
    }

refreshToken()

Try to refresh the access token using the refresh token provided as a cookie. Note that browsers typically send multiple requests in parallel which means the access token will be expired on multiple threads. We don't want to send multiple requests to UAA though, so we need to cache results for a certain duration and synchronize threads to avoid sending multiple requests in parallel.

public HttpServletRequest refreshToken(HttpServletRequest request, HttpServletResponse response, Cookie refreshCookie) {
        //check if non-remember-me session has expired
        if (cookieHelper.isSessionExpired(refreshCookie)) {
            log.info("session has expired due to inactivity");
            logout(request, response); //logout to clear cookies in browser
            return stripTokens(request); //don't include cookies downstream
        }
        OAuth2Cookies cookies = getCachedCookies(refreshCookie.getValue());
        synchronized (cookies) {
            //check if we have a result from another thread already
            if (cookies.getAccessTokenCookie() == null) { //no, we are first!
                //send a refresh_token grant to UAA, getting new tokens
                String refreshCookieValue = OAuth2CookieHelper.getRefreshTokenValue(refreshCookie);
                OAuth2AccessToken accessToken = authorizationClient.sendRefreshGrant(refreshCookieValue);
                boolean rememberMe = OAuth2CookieHelper.isRememberMe(refreshCookie);
                cookieHelper.createCookies(request, accessToken, rememberMe, cookies);
                //add cookies to response to update browser
                cookies.addCookiesTo(response);
            } else {
                log.debug("reusing cached refresh_token grant");
            }
            //replace cookies in original request with new ones
            CookieCollection requestCookies = new CookieCollection(request.getCookies());
            requestCookies.add(cookies.getAccessTokenCookie());
            requestCookies.add(cookies.getRefreshTokenCookie());
            return new CookiesHttpServletRequestWrapper(request, requestCookies.toArray());
        }
    }
Romil Patel
  • 12,879
  • 7
  • 47
  • 76
  • I'm not sure but I suspect that `HttpServletRequest` won't be useful because I use Angular frontend then I make Rest API requests to Spring. Is there other way to configure this using Spring Beans for example? – Peter Penzov Jul 05 '20 at 20:52
  • 1
    You will get the cookies from `HttpServletRequest` and other required details with the angular. You may need to manage the token using sprint boot and client angular will be only responsible to send the cookies and required headers. I have implemented the same using sprint boot and angular and it works great. – Romil Patel Jul 06 '20 at 03:45
  • @RomilPatel In my setup as well we leverage session to store access token and angular just sends the session reference cookie(JSESSIONID), here I wanted to understand how are you refreshing the access token ? Also, do we really need to refresh the access token, once i have cached the access token in my session , the app will continue to use the same, when does it requires the refresh token ? I am new to this spring security and oauth2 world, your insights are much appreciated. – Dharm Jun 13 '22 at 07:09