質問

I am designing out an app that would have an Angular frontend and Spring Boot (Java) backend.

I was considering (but not married to) the prospect of JWT-based authentication:

  1. User logs in with username and password and submits a form; this form POSTS to, say, /auth/sign-in
  2. A Spring Security filter checks to see if the username and password are valid, and if so, generates a JWT with a reasonable (say, 15 minute) expiry and returns the JWT as a response header
  3. Angular client then reuses this JWT until it expires, at which point, Spring Security will send back a specific 400-level error code which will prompt the Angular frontend to redirect the user to the login page and log back in

This is pretty straightforward to implement, but will be a bad UX for my users (if they have to log back in every 15 minutes).

Instead I'm thinking of something along these lines:

  1. Maintain an in-memory cache of some kind mapping each unique user ID to the last activity timestamp (a timestamp representing the last activity they were doing; basically the last time that user did anything in the app that resulted with a call to the backend being made) --> userActivityMap : Map<User,Date>
  2. Every time the user (via the Angular client) does anything in the app that prompts Angular to make a call to the backend, this user activity map is updated with the current timestamp
  3. User logs in with username and password and submits a form; this form POSTS to, say, /auth/sign-in
  4. A Spring Security filter checks to see if the username and password are valid, and if so, generates a JWT with a reasonable (say, 15 minute) expiry and returns the JWT as a response header
  5. Angular client then reuses this JWT until it expires, at which point, the Spring Security filter that detected it as being expired subsequently consults the user activity map. If the user has been active within the last, say, 15 minutes, then a new JWT "refresh token" is generated for the user and sent back to the Angular client with a specific 400-level error code
  6. The Angular client sees this specific error code and knows to pluck the refresh token off the error response, use the value of that token as the main access token/JWT moving forward, and retries the same command with the new (refreshed) access token

I think I could get this to work, but in my Google searches to find what others have done I was surprised to see a million beginner-level "how to" articles but nothing really concrete for this use case. What are typical solutions for this given stack and the specific scenario at hand? Is my solution close to generally-accepted best practices or did I miss the mark considerably?

役に立ちましたか?

解決

It sounds like you're reinventing the wheel a bit here.

JWT authentication has a well-documented "protocol" already defined for this. When the user initially logs in, you provide both a bearer token and a refresh token. The bearer token is the short-living token you've already mentioned, the refresh token is a longer-living token used to get a new bearer token when the current one expires.

How you handle the refresh is up to you; you can go for a "better to ask forgiveness than permission approach" or a "look before you leap" approach. The former being waiting for a request to fail with an unauthorised status code, and then requesting a new bearer token and retrying the request. The latter being checking the exp claim of the bearer token either on an interval or before each request, and requesting a new token when you're within a specified threshold of the expiration time.

JWT authentication is a fairly loose concept, you can implement the client and server sides in a way that meets your requirements, but sticking to a bearer + refresh token approach is definitely recommended.

Whether you cache the refresh token or save it in a database is up to you. Keep in mind, caching introduces a lot of complexity when it comes to blacklisting refresh tokens for recently blocked users.

There are third-party solutions available such as AWS Cognito that you could use. They're far from perfect, but I would consider all options available if I were you.

ライセンス: CC-BY-SA帰属
所属していません softwareengineering.stackexchange
scroll top