When should you validate a JWT token in a React App? - reactjs

I recently implemented JWT tokens in my React + ASP.NET Core 6 Web application.
When a user signs in, the request is sent via HTTP request to the server to issue a JWT token
back to the client. The client then sends another request to validate the JWT token received, in which the server sends a "success" or "rejected" response back to the client.
Now, this is done once when a user signs in, and the JWT token is stored in a Cookie. The Cookie expires 5 days after it is issued, so if a user closes the tab or browser, if they reopen the application, they will automatically be logged in due to the Cookie stored. Note: The JWT token from the Cookie is validated again once a user comes back.
Here is the tricky part...
Since this is a SPA, the JWT token validation happens on the useEffect() method in the AuthContext that handles User Auth.
When a user clicks into a new page, only the child components are rendered, and the AuthContext / Navbar are not, since they are Higher Order Components acting as wrappers. Because of this, the JWT token is not revalidated each time a user visits a new page.
Is this secure? Should a revalidation trigger every single time a user visits a new "page"? Are there any security concerns?

Related

Authentication with JWT in HTTP only cookie without refresh token

The motto of the upcoming question is "I don't know what I don't know".
I would like to know if there are downsides or security risks with an authentication implementation.
Right now, I'm storing a JWT in an HTTP only cookie to send it from the client (React application) to the server (Spring Boot/Kotlin application). This removes the XSS vulnerability.
The JWT follows the basic principles (encoded secret, expiration date, issuer check, etc.). The server has servlet filters that check an existing cookie for validity on every request. When signing in via the client, the server responds with a valid JWT:
The client will send its cookie with every request, which is furthermore checked for validity by the server's servlet filters on every request. When these checks are positive, the user is authenticated with Spring Security.
As described above, the JWT expires after 1 hour, 1 day, or whatever is configured. That's why I need to refresh it some way or another. Now, instead of using a refresh token and placing it in the client's local storage, I decided to just make a small request to the server and create a new JWT, which is send back via the response again:
Again, when the refresh endpoint is called, the servlet filters will check for validity. So only an already authenticated user will receive a new JWT token that way. Some invalid JWT will not receive a new JWT.
I'm calling this endpoint from the client in an interval and therefore regularly extend the expiration date of the JWT inside the cookie.
What I'm aware of:
With the current refresh mechanism, an access token can be valid indefinitely (when the user is signed in regularly).
I also don't persist valid tokens or sessions in the database, so I can't really invalidate specific user sessions "globally" that way, as the cookie is the only source of truth. But this has nothing to do with the refresh token, I could create such whitelist/blacklist via the user ID instead.
If something really bad happens, I could still
...change the expiration date of all JWT to 0, so every authenticated user will be unauthenticated the next time he sends a request.
...or I could change the JWT secret, from which one no request will be authenticated anymore.
My question is: Has what I'm doing (replacing the JWT inside the cookie) any more downsides?

Springboot + JWT +OAuth2 + AngularJS Stateless session

I am trying various Java Spring based security implementations as follows
1. JWT Authentication
User access /
Springboot identifies as protected resource and redirects user to /login
User enters credentials and browsers does a POST to /authenticate
Server validates the credentials and generates JWT token. Set into response header and redirects to /
Browser loads /. AngularJS recognizes JWT token in the response header and stores the same in the localStorage
All subsequent calls will have the Bearer token in header (injected through httpInterceptor)
Note: Stateless Session
2. OAuth2 authentication
User access /
Springboot identifies as protected resource and redirects user to /login
/login is intercepted by Spring security. Redirects to Oauth2 authorization server with a generated state and redirect URL back to application
User enters credentials
Oauth server redirects back to application URL "/login?code=xxx&state=yyy"
/login is intercepted by Spring security. Recognizes the code and state, generates Cookie and sets in response header. Redirects to /
Browser loads /. Browser recognizes cookie in the response header and stores the same.
If a call is made to /user, the Principal object is populated with the JWT which I am able to extract as follows
#RequestMapping(value= {"/user")
public ResponseEntity<Map<String, String>> user(Principal principal) throws Exception {
OAuth2Authentication obj = (OAuth2Authentication) principal;
authentication = obj.getUserAuthentication();
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) obj.getDetails();
String jwt = oAuth2AuthenticationDetails.getTokenValue();
All subsequent calls will have the Cookie in the Request
Note: A Stateful Session is created in server side to store the session details. This required to decrypt the cookie and identify the user
Now I want to implement security using Oauth2+JWT but stateless at same time as follows
3. OAuth2 + JWT + Stateless
User access /
Springboot identifies as protected resource and redirects user to /login
/login is interecepted by Spring security. Redirects to Oauth2 authorization server with a generated state and redirect URL back to application
User enters credentials
Oauth server redirects back to application URL "/login?code=xxx&state=yyy"
/login is intercepted by Spring security. Recognizes the code and state, extract JWT token by invoking
OAuth2AuthenticationDetails.getTokenValue() and set in response
header. Redirect to /
Browser loads /. AngularJS recognizes JWT token in the response header and stores the same in the localStorage
All subsequent calls will have the Bearer token in header (injected through httpInterceptor)
Question
I am trying to figure out how to implement the highlighted step above
Just an idea/direction, if I got you right:
You can create a GenericFilterBean and add that to the HttpSecurity filter chain.
When using JWT, there should be something similar (a filter, which extracts the bearer-token from the header) and then populates an Authentication object for Spring Security.
So the new filter could grab the token from the request and set the response accordingly.
You could also handle that in an unprotected (!) callback endpoint like login/callback?..., which than sets the cookie for you.
In our application, the server (spring boot) is totally stateless and does not have any oauth nor stateful stuff. Obviously it never redirects anything or has any other views/endpoints than / for AngularJS (and some REST-APIs under /api/...). Thus, the OAuth-flow is totally handled by AngularJS, which in turn checks the callback from the oauth-server and locally sets the JWT-Token (like in your first approach). In first versions we also tried to mix up redirects with stateless JWT and stateful sessions etc., but this led to very strange behavior with the logins - the state (logged in or not) was not always clear and in some cases redirects were wrong etc.
This might help you implement your desired solution.
The author proposes that once a user successfully authenticates with Oauth2 providers (Google etc), you send a short-lived token as a URL param to your frontend application and use this short-lived token to exchange it for a longer-lived token.

How to safely persist logged-in state of user in React website?

I have a website that runs on example.com. The website makes AJAX calls to my backend API which sits at api.example.com.
I employ a double-submit verification strategy for my authentication.
The backend has protected endpoints which check the JWT token with each request. The JWT token is stored in a httpOnly cookie. It also contains the CSRF token.
Alongside the JWT cookie I also send a CSRF cookie which is not httpOnly.
Each request that the client makes must contain the value of this cookie in a special header.
So far so good, but, I want to make sure that the client does everything in its power to prevent users from making pointless unauthenticated requests. So in my React app I have declared a few private routes which check if the user is logged in and if they are not, the user is redirected to the login page. The logged-in state is kept in the Redux store.
The issue is that on a full refresh the Redux store is reset. So as I see it, my options are:
Option 1) Check the existence of a CSRF cookie
The way I check if a user is authenticated is by checking if they have the CSRF cookie; I also do this during the store initialisation. If they do have the cookie, they are allowed to navigate to the protected page. All subsequent requests on this page are still verified on the backend for a JWT and CSRF token.
Option 2) Check against the backend each time
I can create a simple endpoint on the backend that is used to check if the user is logged in. So instead of just checking the cookie, the client can submit a request to this endpoint and verify that the token in the cookie is still valid.
Option 3) Persist the Redux store
I could implement a persisted store (there are some libraries that do this out there) so that the initial problem is automatically resolved, i.e.: the logged-in state is preserved upon full refresh. However, this yields a bunch of issues with caching and token expiration and it may not be worth the effort.

.net core, google sign in, and jwt cookies

I have a web app that uses Google sign in only as a way of signing in. I generate a jwt and it gets correctly to my app. But then I save the token on localStorage, which I am trying to move away from, so I want to send the token via a cookie.
Edit (as I was wrong with the root cause)
My back end properly returns the cookie as can be seen on the first image, when hitting the API's endpoint 'googleLogin'. However, I don't know how to then send that cookie on subsequent requests to my API so it is used to validate the logged user
My back end is .net Core, I generate the jwt there and also send the cookie, then I have set app.UseCookieAuthentication to check for that specific cookie, but it is not sent on subsequent requests so I am not able to authorize the user.
I don't know if it matters but the front end is a SPA using react

Handle access token in angularJS

I have Web Api and AngularJS to consume web Api.
when user login with credential user get access_token and refresh token with issued and expires field.
access_token is expired in each 1 minutes and allocate new token to user.
now the problem is
The time between token expired and allocation of new token to user.
if user do a page refresh then its makes an api call to load data of that page, but the access token was just expired and user does not got a new token, so old token is set in header of api call, hence user got 401 unauthorized as response and application throw user to log out.
I am using token in first time so not have much information about access_token and refresh_token
So, I do not know how to handle this situation.
advises is appreciable.
Whenever you make an API call to load data on the page, in the callback you should check the status code. If the status code is 401, get the refresh token and then make another call to the same API and then only initialize the app. Otherwise initialize the app with the old response value.
Thanks #Kushal and #sahil to provide idea.
When user's token is expired app redirect them to login page so on login page added api call to fetch token by user's refresh_token and if user has correct refresh token then assign them to new token and redirect to page which user refreshed by tracking / maintain log of current page of user are. and its working.
Thanks again.

Resources