How to securely store JWT tokens in react/next.js application? - javascript

I need to store JWT token which is generated when a valid user login after proper registration through REST API. I read and find these ways to store JWT in client site: local storage, session storage, cookies, HttpOnly cookie, Browser memory (React state).
Need suggestion to store JWT in the proper method and also can access some certain APIs for get with JWT token as post request header parameter user-related data.
here is my login code part where I store JWT token to window object at this point, saved previously on local storage but now need to store safely in other ways except local storage or cookies.
const handleSubmitLogin = evt => {
evt.preventDefault();
var cart = new Cart();
var request = new NFRequest();
var response = request.api(HTTP_METHOD.POST, '/auth', {
'email_address': allValuesLogin.email_login,
'password': allValuesLogin.password_login,
'cart_list': cart.getCartPostData(),
});
response.then((res) => {
if (res.type === 'success') {
window.$token = res.data.token
setLoginSuccess('Successfully Login')
setTimeout(()=> {
setLoginSuccess('');
}, 3000)
cart.handle({ action_type: "RESET_ITEMS" });
Router.push('/account')
} else {
setLoginError('Wrong Email or Password')
setTimeout(()=> {
setLoginError('');
}, 3000);
}
});
}
here I store the JWT token:window.$token = res.data.token
Thank you.

It's up to you on how to store it. Generally, this is the most to least secure:
Browser memory
Http-only cookie
local storage
session storage / cookie
The most important thing is to make sure your website is protected against XSS and CSRF attacks.

Update: It's not a good idea to store JWT tokens in local storage.
Read this article => https://www.rdegges.com/2018/please-stop-using-local-storage/
set the token with localStorage
if (res.type === 'success')
localStorage.setItem('token', res.data.token);
setLoginSuccess('Successfully Login')
setTimeout(()=> {
setLoginSuccess('');
}, 3000)
cart.handle({ action_type: "RESET_ITEMS" });
Router.push('/account')
}
to remove the token you use:
localStorage.removeItem('token');

Storing JWT tokens within localStorage or session storage is suggested of course with this in production a proper SSL certificate should be used to help prevent this like a man in the middle attack.
Also there are different advantages to local/session
sessionStorage is removed after browser is closed however localStorage is shared between tabs. This is handy for sharing state between tabs. However you ned to manage removing the JWT token.

Related

php jwt login storage

I'm building a web app with CodeIgniter 4 where I implemented a REST API for register and login and a profile page just a test my login.
I have a login form that sends a javascript fetch post request to my api and I receives a jwt token. This is working.
Now I am in the situation where I think I did not understand the principle.
I want that the user stays logged in and doesn't need to re-login every time. The token expires after 12h.
And I want to use php (if possible) as the entire app runs on php.
Currently, I have a little javascript function to store my token:
const store = {};
store.setJWT = (data) => {
this.JWT = data;
};
But this is not secure against page reload.
Additionally I am creating a cookie with php, when the user logs in:
helper('cookie');
set_cookie([
'name' => 'login',
'value' => $token,
'expire' => $exp,
'httponly' => true,
'secure' => true,
]);
I am able to fetch data from the API using the cookie or the store object.
const token = getCookie('login');
const res = await fetch('/profile', {
headers: {
'Authorization': `Bearer ${token}` // or store.JWT
}
});
So.... what I want is:
The user goes to a protected url e.g. https://myapp.com/profile and if he is logged in, he has access. If not, he gets redirect to the login page.
Is using the cookie to store the jwt a good idea? Or did I completely misunderstood the idea of JWT and it is not used to be used for a login?
Additionally: I still don't know if biulding the login as an API was the best idea.
First of all there is nothing wrong with building "login with API". It is common practice. And JWT is perfectly suited for auth.
You sure can store JWT token inside a cookie, but it is a little bit wrong in my opinion. Usually JWT tokens are stored in the local storage on the client side. It will persist after page reload.
Set token in the local storage:
localStorage.setItem('token', token);
Get token from the local storage:
token = localStorage.getItem('token');
To better understand the conception of the JWT you can copy some token (without Bearer) and paste it in jwt.io. Basically JWT contain all the required information about the user and server can trust this information.
And when you set the cookie like this
set_cookie([
'name' => 'login',
'value' => $token,
'expire' => $exp,
'httponly' => true,
'secure' => true,
]);
It is an overkill. JWT possible already contains all this information and you can extract it from the token.

firebase provider authentication refresh flow

I am trying to use firebase authentication to get a google access token in order to call the youtube API.
I am able to initially get the access token like this:
const provider = firebase.auth.GoogleAuthProvider();
cosnt scopes = []; // ...scopes
scopes.forEach(scope => provider.addScope(scope));
firebase.auth().signInWithPopUp(provider)
.then(userCredentials=> {
const oauthCredentials = userCredentials.credentials;
// using credentials for API calls
axios.get("some-google-api-path", { params: { access_token: oauthCredentials.accessToken } }); // and so on...
});
This works fine until the access token expires.
As far as I can tell, it seems like firebase automatically refreshes the session, but I can find a way to get the new access token.
I tried:
firebase.auth().onAuthStateChange(user => {
// could not find the access token on the user object
});
And since that failed, I tried to do it manually using:
const token = firebase.auth.GoogleAuthProvider.credential(
oauthCredentials.idToken,
oauthCredentials.accessToken
);
const authResult = await firebase.auth().signInWithCredential(token);
The issue is that authResult will only contain the idToken or the accessToken, but not both, depends on what I give the firebase.auth.GoogleAuthProvider.credential function.
Am I missing something?
Is there another/better way to do it?
There are two tokens in play here:
The refresh token, which is only available after the user actively signs in. Since it doesn't change, you can cache this token - which is what the Firebase SDKs actually also do.
The ID token, which is available at any moment from the current user. This token expires in an hour, and is auto-refreshed by the Firebase SDKs.
So if you want to retain access to the refresh token, you will have to store (typically in local storage) yourself when the user first actively signs in.
So apparently firebase doesn't refresh tokens of other providers for you (not event google) according to this (thank you Frank van Puffelen!)
So what I did instead, is authenticate manually to google (since I use react, I used react-google-login), and got all tokens from there.
Then, once the session is initiated/refreshed, I create a firebase session using:
const token = firebase.auth.GoogleAuthProvider.credential(
oauthCredentials.idToken,
oauthCredentials.accessToken
);
const authResult = await firebase.auth().signInWithCredential(token);
I hope that this helps anyone, and I will accept another answer if firebase ever changes this.

React: Store JWT

I have a backend where the user gets a JSON Web Token if he is logged in successfully. This token is needed for other API calls and I want to store it application-wide to access it from every point in the application. What is the best point to store it and access it from anywhwere?
You can store it on local storage on login like
export async function login(username, password) {
const { data: jwt } = await http.post(apiEndpoint, { username, password });
localStorage.setItem("token", jwt.token);
}
and than you can access it every time like
export function getCurrentUser() {
try {
const jwt = localStorage.getItem("token");
return jwtDecode(jwt);
} catch (error) {
return null;
}
}
I have used widely this approach and works like charm.
I'd highly suggest not to store in localStorage. I'd highly recommend cookies.
See this link.
In addition, also See another link.
Storing JWT token in localstorage or session storage of a browser is not preferable, as it can be accessed easily by anyone who has even a little knowledge of browser developer options (especially developers like us).
I suggest you use http only cookie to store them, that way it can be accessed whenever you send a HTTP request.
You can read about it more - https://blog.logrocket.com/jwt-authentication-best-practices/

How can I store a JWT in cookies using Axios?

I am using JWT in my React application and Axios to handle API calls. I am looking for a way to store the token in cookies so that I am not redirected to login again every time the browser is refreshed.
Here is my setup for Axios and my login call:
let authToken = null;
const axios = axiosAPI.create({
baseURL: baseURL
});
// User login
export const loginUser = (data) => {
return new Promise((resolve, reject) => {
axios.post(`${baseURL}/jwt-auth/v1/token`, data)
.then((res) => {
authToken = res.data.token;
// Adds the token to the header
axios.defaults.headers.common.Authorization = `Bearer ${authToken}`;
resolve(res.data);
})
.catch((error) => {
reject(error);
});
});
};
I am not certain where I should be setting the cookie and how to set it?
EDIT:
I have rewritten my code using js-cookie so it looks like the comment.
import axiosAPI from 'axios';
import Cookies from 'js-cookie';
let authToken = null;
const axios = axiosAPI.create({
baseURL: `${baseURL}`
});
// Check if user is logged in.
(function () {
if (Cookies.get('token') === null) {
// This means that there's no JWT and no user is logged in.
axios.defaults.headers.common.Authorization = null;
} else {
// This means that there's a JWT so someone must be logged in.
axios.defaults.headers.common.Authorization = `Bearer ${authToken}`;
}
}());
// User login
export const loginUser = (data) => {
return new Promise((resolve, reject) => {
axios.post(`${baseURL}/jwt-auth/v1/token`, data)
.then((res) => {
authToken = res.data.token;
Cookies.setToken('token', authToken);
// Adds the token to the header
axios.defaults.headers.common.Authorization = `Bearer ${authToken}`;
resolve(res.data);
})
.catch((error) => {
reject(error);
});
});
};
However this prevents me from logging in at all and I get the error 'wrong number of segments'. Any idea why this isn't working?
There are a few different options that you can take here to solve the problem you are having, which is simply finding somewhere to store the JWT so that you can use it even after you refresh the page.
Save the JWT in localStorage or sessionStorage in your axios.post callback so that you can have access to it even after the page refreshes. To learn which storage is most suitable for your app, see this.
To keep it short, values stored in localStorage will persist until you explicitly delete them (you can do this via your JS code). Also, any tabs you open in your browser to that domain will have access to this (very useful if you want to still be logged in with the new tabs). On the other hand, values stored in sessionStorage only live until the tab is closed. They can't be shared across tabs either.
Using this is as simple as:
localStorage.setItem("JWT", authToken); or sessionStorage.setItem("JWT", authToken); in your callback after your authToken = res.data.token;
So now that you have a place where you have stored the JWT, all you need to do is make sure to check if a JWT exists in storage when your app initializes on page load (or refresh). Here's a basic example of how to use localStorage:
// This should be one of the first things that run on your app.
const axios = axiosAPI.create({
baseURL: baseURL
});
// Check if user is logged in.
(function() {
let authToken = localStorage.getItem("JWT");
if (authToken === null) {
// This means that there ISN'T JWT and no user is logged in.
axios.defaults.headers.common.Authorization = null;
} else {
// This means that there IS a JWT so someone must be logged in.
axios.defaults.headers.common.Authorization = `Bearer ${authToken}`;
}
})();
This will make sure that the user is not logged out on page load, if previously logged in.
Save the JWT in a client side cookie. Here, the cookie is being used as a storage mechanism since you are not actually working with server side cookies given that your authentication is all build around JWT. You can follow the same code pattern as above but instead will be using document.cookie = "key=value" to set a cookie and document.cookie to view all cookies.
This second way is less common because it forces you to do a lot of manual labor like parsing through all the cookies and making sure to set the proper cookie path attribute so that the cookie only gets sent up for the required endpoints (otherwise you're just creating unnecessary overhead). If you exercise this option, read this to help you create your cookie to fit your needs. You can also use a helper JS library like js-cookie to help manipulate client side cookies.
Additionally, I would read through https://stackoverflow.com/a/40376819/11048825 to dive further into these two options and understand the pros and cons associated with each.

Where to store JWT

I barely started reading about JWT and I beliave I understand what a JWT token is. I am also fairly familiar with SESSIONS. And I believe I understand the pros of each as well as their cons. However, there are a couple of parts where I am confused.
When requesting a protected resource, you need to send the jwt on each request, as opposed to having a session stored on the server. But:
1) how do you store your JWT token and where. From what I read I understood that you send your request to authenticate to the server and the server sends you a JWT token if you are successfully authenticated. Then what do you do?, do you store the JWT in a cookie as I have read in some sites? If so, how do you do it (using php, using javascript). And how do you read it.
2) When using session, more or less you just check that there is a session to check the user is logged in. How do you accomplish this when using JWT.
Also I have seen this on some pages:
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
How is this related to this (if related at all)
From client side, the good practice is store JWT in cookie, with mode http_only=true, is_secure (so that only send through https), so that JWT is not accessible by javascript. Then, we don't worry about XSS attach.
We dont need to store the session on server side. A JWT contains two parts, the payload data, and signature, signed by a secret key stored on server side, and only the server could know. When we receive the token from client, we check the payload data is valid or not (user information, who assigned that token, assigned that token to whom, which roles granted with the token, expired time), and we check the signature to make sure that the token is assigned by the server, not faked. Then the user will be authenticated.
It's like a passport the government give to its citizen, the data (payload) is readable for everybody, but the signature can only created by the government, and it can verify against that.
JWT is mostly a way to authenticate a user on rest APIs as, like you said, you send it to the client, and it negates the need to store it in a session.
however, if you are making a browser application, i don't see the need to use JWT authentication, as you can use the session and cookies to do so.
JWT is mainly for those cases, when you have a, say, mobile application frontend, where maintaining a session is not suggested, and maybe impossible.
however, if you are making a hybrid application, then storing it in local storage of the "browser" is the way to go. the JWT is NEVER stored server side.
In my case I use use Illuminate\Foundation\Auth\AuthenticatesUsers;, use JWTAuth; and use Tymon\JWTAuth\Exceptions\JWTException; in my LoginController. Then I need to get my JWTToken like that
use AuthenticatesUsers;
try {
$token = JWTAuth::attempt($request->only('username', 'password'), [
'exp' => Carbon::now()->addWeek()->timestamp,
]);
} catch (JWTException $e) {
return response()->json([
'error' => 'Could not authenticate: ' . $e.message,
], 500);
}
Of cause I do somthing more, if I don't get a token. In my case I go like that:
if (!$token) {
return response()->json([
'error' => 'Could not authenticate.'
], 401);
} else {
$data = [];
$meta = [];
//all what i need from users table if auth
$data['id'] = $request->user()->id;
$data['email'] = $request->user()->email;
$meta['token'] = $token;
//now comes the part, where I set my sessions:
Session::put('auth-user', (array)$request->user());
Session::put('jwt-token', $token);
Session::save();
return response()->json([
'data' => $data,
'meta' => $meta
])->withCookie('jwt-token', $token, config('jwt.ttl'), '/', null, true, true);
}
This is actually that what I do in LoginController I also have a middleware where I have a function to handle stuff like behavior after refresh page an so on.
'my.auth' => \App\Http\Middleware\MyAuth::class
I also handle a lot in javaScript's localStorage and sessionStorage in vuex store. To define routes in web.php or api.php I'll use now the class what I defined in
middleware before Route::group(['middleware' => 'my.auth'], function(){...}
Some resources that helped me out:
LaravelCookies, LaravelSession
Hope you get a bit inspiration to create your JWT auth.

Categories