How to store JWT Secret in Reactjs front end? - node.js

I am new to JWT, not new to react, but am very confused on how to decode a JWT from the front end. I initially thought that I can store the JWT Secret in the .env file but many sources say that it is a very bad idea to do so. I have the backend setup to send me a JWT when you login. But without storing the secret key in the front end as well, how would I decode the information?
Backend:
if(bcrypt.compareSync(ctx.params.password, hashed_db_password)) {
ctx.status = 200;
const payload = { data: tuples[0] };
const options = { expiresIn: '1h', issuer: 'testIssuer'};
const secret = process.env.JWT_SECRET;
const token = jwt.sign(payload, secret, options);
ctx.body = token;
return resolve();
}
How I thought front end should have been:
let data = JWT.verify(result.data, process.env.REACT_APP_JWT_SECRET, options);
I have also read alot that the backend should do validation but then wouldnt that just be a huge security risk to validate, then send back unsecure raw user information? Any information would be greatly appreciated.
BTW, I am using Reactjs, Node.js, Express, and MySql

You should not store JWT secret in client side.
To decode token, you don't need the JWT secret.
You can decode the token using jwt-decode package.
Or if you want to decode without using a package, you can look at here.

You can store it in your main components state, Redux store, React Context, localstorage and so on..
You should get the JWT only when your authentication is successful and you should send it with each request to the server, you don't need to decode it on the front-end you just pass the encoded value to the server and decode it somewhere on the backend (some kind of middle-ware)

Related

How to authorize user using next auth with nodejs server?

I am using next auth for user authentication in my nextjs app, but my end points are on nodejs server (while next auth logic is in api/auth/[...nextauth] file). How can I verify that the user who sent the request from nextjs app to my nodejs server is authorized or not.
Is there a way to send and verify jwt token set by next auth to node js backend and see if user is authenticated or not?
It should be technically possible. For example, you can pass the JWT created by NextAuth to the Node.JS APIs via the Authorization header and decode the JWT using the value of NEXTAUTH_SECRET.
const jwt = require("jsonwebtoken");
const decode = jwt.verify(token, "secret");

Login user from backend (firebase + node.js)

I'm looking for a way to send user's email and password from the client to my Firebase Backend Functions and make the login from the backend, I find out info about Id tokens and stuff like that, but I need just simple function that receives email and password and make the request to Firebase Auth.
On firebase late versions you might have something like "signInWithEmailAndPassword(email, password)", So I'm looking the exact operation just for the SDK for node.js, and I dont seems to find one.
Thank you.
Firebase Auth only allows you to use signInWithEmailAndPassword on client side.
You should not authenticate users on backend (although it's possible), because it might have unintended consequences, but you can authenticate user with signInWithEmailAndPassword on the browser and then verify "ID Token" to your backend.
Frontend (using firebase):
const payload = {
uid: user.id,
idToken: await user.getIdToken()
}
//send payload to server
Backend (using firebase-admin):
const decodedToken = await getAuth(app).verifyIdToken(body.idToken)
//check decodedToken.uid equals body.uid
You can read more into "ID token verification" here:
https://firebase.google.com/docs/auth/admin/verify-id-tokens#web

API-client authentications with JWT — Node/TypeScript

I have an API and a web client developed using node and TypeScript. Users can authenticate using JWT. The system works, but I would like to know if it is secure, or if it has flaws:
The system is as follows:
Client POST to /login endpoint on API
On API, when POST /login request is received, a JWT token is generated using some user data as content, and a secret string that is stored as an environment variable:
// SECRET is an environment variable == "bd0b2760-5869-11ec-bf63-0242ac130002"
const userData = {
id: 1,
name: "John Doe",
};
const token = jwt.sign(JSON.stringify(userData), SECRET);
In the response for POST /login API sends two cookies: one holding the token, and another one with raw user data:
return res
.cookie('sessionData', userData, {
httpOnly: true,
path: '/',
domain: "example.com",
})
.cookie('sessionToken', token, {
httpOnly: true,
path: '/',
domain: "example.com",
}).send();
The client receives the token. The client can be sure that sessionToken is valid, as it was sent by the API. It wont verify it, as to do it the SECRET is needed, and we don't want to expose it to the client.
On reload, client will use the sessionData cookie to know that user is logged in, using this data to load the client-side user data.
As those cookies are http cookies, both cookies are attached to every request send to the API, and are received by the API. On every request to endpoints requiring auth, the API will decrypt sessionToken and match it against sessionToken cookie: if they doesnt match, API will delete cookies in the response, effectively logging out the client.
// SECRET is an environment variable == "bd0b2760-5869-11ec-bf63-0242ac130002"
const sessionToken = req.cookies.sessionToken;
const sessionData = req.cookies.sessionData;
const decodedToken = jwt.verify(sessionToken, SECRET);
if(decodedToken.id !== sessionData.id || decodedToken.name !== sessionData.name ) {
return res
.clearCookie('sessionToken', { path: '/', domain: "example.com" })
.clearCookie('sessionData', { path: '/', domain: "example.com" })
}
As previously said, this system works, and it seems secure. But maybe I'm missing something, so better ask. All code is kinda pseudocode.
Any help will be welcome!
It wont verify it, as to do it the SECRET is needed, and we don't want to expose it to the client.
If you want the client to be able to verify this JWT you can use asymmetric signing. Then the client uses a public key, which can be used only to verify that the JWT is OK. It can't be used to sign new JWTs.
On reload, client will use the sessionData cookie to know that user is logged in, using this data to load the client-side user data.
You're using http only cookies so that won't work. Your client is not able to read that cookie.
the API will decrypt sessionToken
Minor thing, but worth remembering - the API will decode the sessionToken cookie, not decrypt. Signed JWTs are encoded, not encrypted. You can create encrypted JWTs (JWE), in which case no one, apart from the API will be able to read the content of the token, but JWEs are much harder to maintain.
the API will decrypt sessionToken and match it against sessionData cookie
I don't see how this gives you any additional security. The session token is a signed JWT, so you can easily verify it to check whether it's been tampered with. If someone managed to steal the sessionToken cookie they might as well have stolen the sessionData cookie and use them together. You're using http only cookies so XSS won't be able to steal the contents of those cookies. You could also use secure flag to make sure that those cookies are not sent through unencrypted connections.

Is a good idea pass decoded jwt data via express req parameters?

In my NodeJs application im using jwt to manage the user session, inside a jwt token i store user_role and user_id. This is my route:
routes.post('/manga/post', Authorize("Scan"), MangaMiddleware.valid_manga_store, MangaController.store);
In the middleware Authorize("Scan") I verify the jwt token with "jwt.verify", if its valid i going to check if there is a active user with the token id and if his permission allow him to access this route, if so i use next()
In MangaController.store i going to save a new manga, and i need to save in the document the user_id who made the request.
That's my point, i already decoded the token in Authorize middleware but the decoded data do not persist out of the middleware. To access the user_id from MangaController i have to verify the token again.
I think i should avoid verify the same token twice, so in the middleware Authorize after verifying i was saving the user_id (encrypted) inside req.auth and after use it in the controller, i was setting req.auth = null. This way the user_id is stored in req.auth for a short period of time.
req.auth = user_id //after encrypting
My friend told me this is a bad idea storing decoded data inside req parameters, but i don't think it is this bad.
In a nutshell. Do i need to verify the token twice? Is there another way to retrieve this data? It is that bad storing decoded data in req parameters? I do appreciate your time and help.
Verifying and decoding JWT are two different things. When you verify, it's checking for its integrity, ie making sure it has not been tampered with, while decoding JWT means converting from base64 to readable format (UTF-8?). So it doesn't have to be verified twice.
Assuming you sent your token in headers as "token":"base64encodedJwt", then after successful verification, whenever you need user_id, you can then simply decode the JWT. Use some JWT decode library.
let token = req.get('token') || req.headers['token'];
let payload = decodeJWT(token);
let userId = payload.user_id;
If you are not storing it in req object, then you will have to decode it everytime you need it. So req.auth = userId should be fine.

node js JWT get current user

I work on app with an authentication using Node JS, JWT and Sequelize for the API and I'm using React JS / redux for the front part. I'm successfully implemented the login/logout/register parts for the application, but now I need to access to the current_user logged in.
I put a JWT in the localStorage, but I want to have access to the user ID, user email, user name and more informations about my user currently logged in.
Should I use Cookies ? LocalStorage ? Or should I create a currentUser method in my API ?
I'm a bit lost with this, if someone could help me find some usefull resources or advices !
Thank !
If you put that information in the payload of the JWT, then you can get it without decoding on the server or needing the secret, and can therefore put the token in LocalStorage for use whenever. By the spec, a JWT is <headerINfo>.<payloadInfo>.<signature>. So on the client, you can just do:
// given a payload object of { username: 'bob', userid: 1, email: 'bob#example.com' }
const tokenParts = token.split('.');
const encodedPayload = tokenParts[1];
const rawPayload = atob(encodedPayload);
const user = JSON.parse(rawPayload);
console.log(user.username); // outputs 'bob'
Obviously, this info is available to any client that has access to the Token, so you only want to put stuff in the payload that that's OK for.
Storing the token in LocalStorage is fine. If you need to fetch the user details, create an endpoint in your API such as getUser. You can then use jwt.decode(accessToken, JWT SECRET HERE) and return the decoded value (which will be your user) assuming the accessToken is valid.
You can make a middleware if you haven't already that will ensure that user info is always available to those routes that require it:
const auth = jwt({
secret: JWT_SECRET,
userProperty: 'payload',
algorithms: ['HS256']
});
module.exports = auth;
Then you should have req.payload with user details. Alternatively you can check the Authorization property in your headers depending on how you set up your app.
When logging in, server should send token and user data, you can store that data in Redux store. Then simply request data from there. When user reloads page, send API request with JWT token and server should return user data, that you will again put in Redux store.

Resources