What's a secure way to store token & auth state? - reactjs

Here's my scenario:
When a user logs in, an API call is made and if his credentials are valid, a token is returned. I need to store the authentication state (to keep the user logged in) and the token somewhere. The token will be used to make other API calls.
I've read these posts: 1, 2, 3 (and some others) which all seem to contradict each other; whether it's localStorage, cookies or JWT, all are being deemed as unsafe by different people. I have also read about react-redux too but I'm not sure about it.
For now I am completely lost on which solution best suits my needs since I am new to reactJs. So, what is the proper way to go on about this?

The most common practice is storing your token in cookie and set HttpOnly to true, so that any javascript code cannot read your token programmatically.
I suppose you are using axios as ajax client, you can make a request like this
axios.get('https://example.com/api', {
withCredentials: true
})
by doing this, axios will send your cookies to remote server automatically

Related

How to ensure RTK Query does not cache certain endpoints which take sensitive data?

I have an endpoint for authenticating the user, which takes the user's username and password. The request returns a token which the app should then use for all subsequent requests, so I store it in the Redux store.
The tokens have a limited lifetime, so the app has to get a new token every so often. Because of this, I want to store the username and password using the device's secure chain storage (I'm using React Native), not on the Redux store, since doing so make it easy to extract the credentials from the app memory.
But since RTK Query by default caches requests based on the args passed to it, the user credentials will be cached in the Redux store.
So my question is two-fold:
Is what I'm saying even sensible? Does the logic for how I want this to work makes sense?
If yes, then how do I go about disabling the cache for one specific endpoint for RTK Query?
The point of RTK Query is to put things into a Redux store. That is the core mechanism for everything.
You can do all the requests without it, but if you do a request with it, it will be cached, at least for a while.
That generally would not be much of a concern since a Redux store is just a variable like const foo = "somethingSecret" though.
That said, you should probably not store username and password, but a "refresh token" that you get from your server.
And usually, you should not store that yourself, but your fetch implementation should do that for you - in the form of a cookie.
This also works in React Native.
Your server just sets the cookie for both the token and the refresh token as a httpOnly cookie and you cannot even access them from JavaScript. In JS, you just say credential: "include" and all your requests to that same domain will contain those tokens.
Manually handling those tokens if you can avoid it is always bad security practice.

Is it safe to save jwt accessToken on session in Next.js next-auth? Then using useSession hook to access it on client side?

I'm using next-auth for authentication and using the Credentials provider for logging in, my API returns an object containing an accessToken like this object:
{ "token" : "UzI1NiIsInR5cCI..." },
And I'm returning a similar object containing the token in authorize property of CredentialsProviders while my session callback looks like this:
session({ session, token }) {
session.data = {
...session.user,
...token.user,
};
session.accessToken = token.accessToken;
return session;
}
And I use the useSession hook in my client code like this:
const { data: session, status } = useSession();
to access the user's token to make API requests.
Is this safe? Or is there another ways to achieve this?
Keep in mind that this application is gonna be fully client side rendered despite the fact that I'm using Next.js, so keep that in mind. I'm asking this because most of the docs of Next.js is SSR focused.
Quite often, we use localStorage to store the token after authorization, of course, since localStorage is the browser api, this means that we store the token on the client, which is actually similar to your case. I don't see much of a security issue with this, although it does make the token vulnerable to XSS attacks, so cookies are considered a safer way to store the token, since it can't be retrieved from the script side (if you set the http-only cookie option).
If you're worried, you can put the token in cookies to be sure. Although even so, the way to use this token for malicious purposes. The best way is to protect yourself from XSS attacks and then there will be nothing to worry about, react (well, including next) out of the box has good protection mechanisms.
In general, since we are talking about an access token, this is the thing that identifies the user and this should not be a problem for you to use it into the client, you should be more careful with the secrets of the application, such as the secret that is used to encrypt the token, then there will be security problems if you give access to it by client.
If you still don't want to use a token on the client side, which you probably don't need, you can use getSession on the server side for authorized requests with nextjs and proxy your requests through next to backend, using next as api is also a common practice.
To summarize, since you are using the next-auth library, you have nothing to worry about, since it takes care of most of the security concerns.
https://next-auth.js.org/getting-started/introduction#secure-by-default

Is it safe to store access token in next-auth session?

What I want is to access the customer details in the client side in next.js and I need to pass the customer access token to the backend API. Using next-auth, I was able to store the access token to the session but is it safe or is it better to use the next.js api route and use getToken function? I'm still a beginner in frontend security, I don't know if I'm just being a paranoid but I can't find a topic/post that states it is completely safe.
So by default the session strategy is set to jwt, which means your session is encoded / signed and safe to store sensitive info in.
However, NextAuth.js can be used to automatically put that accessToken you get from your OAuth provider in the JWT token via the jwt callback. Therefore you can pull it out anywhere with the getToken() method you mentioned and authenticate against Google APIs for further use (get drive contents, contacts, etc, etc, etc).
Check out this example for how to do that in the jwt callback: https://github.com/nextauthjs/next-auth-refresh-token-example/blob/57f84dbc50f30233d4ee389c7239212858ecae14/pages/api/auth/%5B...nextauth%5D.js#L67

How do authentication tokens and authorization work in reactjs?

I'm new to reactjs and I've ben trying to understand how the authentication token works to protect routes. In various tutorials people get that token from an api when logging in a user, and then store it along with a "isAuthenticated" variable set to true in localStorage. Then when routing they check if isAuthenticated is true, without any api call to verify the token. Is that safe? I was trying to implement authorization in the same way, by just adding some isAuthorized variable, but can't both of these be tampered with since react works client side?
Of course they can. Whatever is on the client is fully controlled by the user. There is no "client-side authorization". Such features (access control to certain functionality on the client) is usually a user experience feature, like why show something to the user that won't work anyway.
All authorization must be done server-side.
This inherently means that it's usually ok to have page structures (views) without data in the client for anybody to see, the point is that data from the backend will be authorized and will only be available to appropriate users.

How to store generated api keys in React?

If a user logs in with user and password, he gets an new api key,
so react can access the Rest Api with his user account. How do you save this api key? And what happend if the user clicks 'refresh page'?
Of course I can initalize the Rest App every time with
<script>
window.REP_LOG_API_KEY = '19e8317a38b24af82da056f6ed36e831ea6b8f9bfcad996aaa56ec773f9f2e1d';
</script>
<script src="build/reactapp.js"></script>
but dont look very secure (but I like the idea of changing this key
every page request, if you have no single page application and react
is only used here and there).
To store the Api Key in a cookie would be also possible (secure but not
httponly, normally I only use safe cookies). Is this the only way?
I'm still not quite sure how to use react with a rest api
with individual api keys. Thany you.
The API key you are talking about is probably cookies/authentication token. If it is cookies, you need to enable httpOnly to prevent attacks. For authentication token, the most common way to store is in localStorage or sessionStorage. However, it is insecure, even with HTTPS and short expiry dates (and you do HAVE to use them). Putting it in Redux store is the same as putting it in a global js object where everyone can see.
What will protect your app is to check standard headers to verify the request is same origin (source and target origins check) and CSRF token. Also a common pattern is to verify the token signature before storing and using it. You can check out Auth0 blog on where to store it here: https://auth0.com/docs/security/store-tokens
There are several ways to do this. If you are using a state management library like Redux, MobX, Flux etc, then you can put it there.
Another place to store them is in the browser local storage. This will keep the token saved even if the user refreshes the page or open a new tab etc. Yet I am not 100% sure whether it's a safe thing to do.
Or you can attach it to the Rest Client itself. IMO, this is the best way to do it. I will summarize the steps to do that in brief.
Create a Rest Client by wrapping a solution like fetch-api or axios.
Add a module state, where you can store any data
Whenever making a call, check if there is a token, if a token is not there in the state, then authenticate first.
If the token is there, use it to make the api call. If the request fails with a 403(or may be 401. It depends) error, that means the token has possibly expired. So authenticate again and update the token.
Something like this,
class ApiClient {
constructor() {
this.token = null;
}
login(username, password) {
// make the call to login
// set this.token with the response
}
request() {
// Make the API call using the token
}
}
What will happen with a refresh? Then since the token is not there, the authentication will need to happen again. This will not be a problem if you use cookies to manage sessions.
You can put it in Redux store. I think it is the best implementation.

Resources