0

I store my session in database, I have columns with user id, UUID and expiration date. Before any request from Angular I would like to send request to the method in api which check expiration date and let angular send another request if is valid or logout user and remove local storage with token with message about expiration date of my session. I'm looking for similar solution to HTTPInterceptor which add headers automatically to every request instead of add headers to any method with request before.

I'm using Angular 10 and Spring Boot 2.3.1.

EDIT.

I found the solution for catching errors in any request in my interceptor on the Angular side.

@Injectable()
export class HttpInterceptorService implements HttpInterceptor {

  constructor() {}

  intercept(req: HttpRequest<any>, next: HttpHandler) {

    if (localStorage.getItem('username') && localStorage.getItem('basicauth')) {
      req = req.clone({
        setHeaders: {
          Authorization: localStorage.getItem('basicauth')
        }
      })
    }

    return next.handle(req).pipe(
      catchError(response => {

        console.log(response.status)
        // do something, example clear LocalStorage...

        return throwError(response);
      })
    )

  }
}

EDIT 2.

I made Interceptor with preHandle() method in Spring-Boot to check session expiration date before request and if session is expired I set unique response status to 495 which tell Angular to logout the user and clear LocalStorage.

preHandle() method in Spring-Boot:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    //if (request.getRequestURL().toString().endsWith("/api/basicauth"))
    String GUID = request.getHeader("GUID") == null? "1" : request.getHeader("GUID");

    if (sessionService.getSessionByGuid(GUID).isPresent()) {

        Session session = sessionService.getSessionByGuid(GUID).get();

        if(session.getExpirationDate().isBefore(LocalDateTime.now())) {

            sessionService.deleteSession(GUID);
            response.setStatus(495);

            return false;

        } else {

            sessionService.renewSession(session);
            return true;

        }

    }

    return true;
}

Interceptor method in Angular:

  intercept(req: HttpRequest<any>, next: HttpHandler) {

    if (localStorage.getItem('username') && localStorage.getItem('basicauth')) {
      req = req.clone({
        setHeaders: {
          Authorization: localStorage.getItem('basicauth'),
          GUID: localStorage.getItem('GUID')
        }
      })
    }

    return next.handle(req).pipe(
      catchError(response => {

        if (response.status == 495) {

          this.auth.removeSessionAndStorage();
          this.openSnackBar("Twoja sesja wygasłą!",);
          this.router.navigate(['login', 'session-expired']);

        }

        return throwError(response);
      })
    );

  }
Seldo97
  • 611
  • 1
  • 8
  • 17
  • 4
    this is pretty inefficient tbh. just send your request, check the token, and respond 401 if the token is expired and reauth at that point. save yourself a round trip to the server. – bryan60 Jul 20 '20 at 17:44
  • @bryan60 and clear my local storage stuff in `subcribe` in error section? – Seldo97 Jul 20 '20 at 17:48
  • probably will want to use a `catchError` operator in your interceptor and just check the status code for what to do. – bryan60 Jul 20 '20 at 17:49
  • @bryan60 can you look at my solution on the Angular side? I edited post. – Seldo97 Jul 20 '20 at 19:13
  • 1
    here is an answer i did once about auth interceptors: https://stackoverflow.com/questions/46017245/how-to-handle-unauthorized-requestsstatus-with-401-or-403-with-new-httpclient/46017463#46017463 – bryan60 Jul 20 '20 at 20:16

1 Answers1

1

I think your design is a little flawed. It really sounds like you are wanting to do JWT authentication but you're getting confused with how the token is validated before actually following through and executing your request.

In your interceptor, as long as you are adding Authorization: Bearer <token> with every request, you just need to create some auth middleware in your backend. This middleware would check the header of every request, grab the JWT and validate it. If it's valid, it then follows through and executes the request. If it fails, it returns an Unauthorized/Forbid result (up to you on what you want to return).

There's a couple of different ways to set this up, but here's a nice medium article that describes how to do it with Node.js. Some other frameworks, like .NET, make it a lot easier. So, you may want to evaluate the options before going with one.

Example code from article:

let jwt = require('jsonwebtoken');
const config = require('./config.js');

let checkToken = (req, res, next) => {
  let token = req.headers['x-access-token'] || req.headers['authorization']; // Express headers are auto converted to lowercase
  if (token.startsWith('Bearer ')) {
    // Remove Bearer from string
    token = token.slice(7, token.length);
  }

  if (token) {
    jwt.verify(token, config.secret, (err, decoded) => {
      if (err) {
        return res.json({
          success: false,
          message: 'Token is not valid'
        });
      } else {
        req.decoded = decoded;
        next();
      }
    });
  } else {
    return res.json({
      success: false,
      message: 'Auth token is not supplied'
    });
  }
};

module.exports = {
  checkToken: checkToken
}

So, essentially, the process you should be going for is made up of a couple of things:

  1. The token is sent as a header with every single request
  2. A middleware exists on your backend to validate the token before any API method execution

https://medium.com/dev-bits/a-guide-for-adding-jwt-token-based-authentication-to-your-single-page-nodejs-applications-c403f7cf04f4

If you stick with Spring Boot, it looks like there is some sample code on how to set this up here

mwilson
  • 12,295
  • 7
  • 55
  • 95
  • What do you think about saving JWT with expiration date after log in in database and then write `interceptor` method in spring boot which will be validating user before any request? – Seldo97 Jul 20 '20 at 18:18
  • You don't validate the user *before the request*. You validate it *with the request* – mwilson Jul 20 '20 at 18:19
  • Basically, your thought process of having two requests for each request (one to validate the user and one to execute the request) is not a good idea for quite a few reasons, but ultimately efficiency and security. – mwilson Jul 20 '20 at 18:29
  • I know but security is the priority in this application. Tommorow I'll edit my post with result. – Seldo97 Jul 20 '20 at 18:35
  • Yes, your current design is technically less secure. – mwilson Jul 20 '20 at 18:54