4

I'm using two JWT tokens - Refresh Token(expires after 7 days) and Access Token (expires after 15 min). They are stored on httpOnly cookies and can be accessed via server. Refresh methods signs new token and store it on a cookie. I need to check if these tokens are expired after every request like this:

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    constructor(private authService: AuthService, private cookieService: CookieService) { }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            const expirationToken = this.cookieService.get('tokenexp'); // access token expiration
            const expirationTokenRefresh = this.cookieService.get('tokenrefexp'); // refresh expiration
            
            // Refresh Token needs to be checked first
            if (Number(expirationTokenRefresh) < Date.now()) {
                // new refresh token is stored on cookie
                this.authService.refreshTokenRefresh();
                // this.authService.refreshToken().subscribe(() => { ... });
            }
            // next we check Access Token
            if (Number(expirationToken) < Date.now()) {
              // new access token is stored on cookie
              this.authService.refreshToken();
         // this.authService.refreshTokenRefresh().subscribe(() => { ... });
            }
            return next.handle(req.clone({
                withCredentials: true
            }));
    }

}

// auth service
refreshToken() {
  return this.http.get(`${BACKEND_URL}/refreshtoken`);
}
refreshTokenRefresh() {
  return this.http.get(`${BACKEND_URL}/refreshtokenref`);
}

Here is Express back-end methods:

//routes
const express = require('express');
const router = express.Router();
router.get('/refreshtoken', user.refreshToken);
router.get('/refreshtokenref', user.refreshTokenRefresh);

// refresh access token method
const jwt = require('jsonwebtoken');
const moment = require('moment');
const User = require('../models/user');
exports.refreshToken = wrap(async(req, res, next) => {
    const user = await User.findOne({ refresh_token: req.cookies['tokenref'] });
    if (user) {
        const newToken = await jwt.sign(
            { email: user.email, userId: user._id, role: user.role },
            process.env.JWT_Key,
            { expiresIn: '15m' });
        const expiresAt = moment().add(900, 'second');
        res.cookie('tokenexp', JSON.stringify(expiresAt.valueOf()), { maxAge: 3000000000, secure: true});
        res.cookie('token', newToken, { maxAge: 3000000000, secure: true, httpOnly: true });
        res.status(200).json({success: true});
    } else {
        res.status(401).json({success: false, message: 'Sessão expirou.'});
    }
});

How can I make it work using RxJS Observables? I may send one request to refresh a token, and then another request to refresh second token, and finally the original request with updated cookies. In summary, I may need to send requests before my original request. Also there is problem: AuthInterceptor shouldn't be called after request one or two (tokens).

1 Answer 1

1

Using mergeMap to sequentially check the validity of token.

return of(Number(expirationTokenRefresh) < Date.now()).pipe(
  mergeMap(expire => expire
    ? this.authService.refreshTokenRefresh()
    : of(Number(expirationToken) < Date.now())
  ),
  mergeMap(expire => expire
    ? this.authService.refreshToken()
    : of(true)
  ),
  mergeMap(ok => next.handle(req.clone({ withCredentials: true })))
)
Sign up to request clarification or add additional context in comments.

3 Comments

How to avoid multiple HttpInterceptor calls while navigating between mergeMap? I'm trying to send requests with HttpBackend like this const http = new HttpClient(this.handler); return this.http.get(${BACKEND_URL}/refreshtoken); but it doesn't work very well
Good question. it's not a very straight forward setup for this case. Could you open up a new question for that, i'l try to answer it.
ok made another question. There you go: link

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.