JWT created on server but not being sent/set in browser - javascript

I am trying to set a JWT cookie for the response of a successful login in my Express app.
In the following code the user has been returned correctly from the DB and the jwt.sign() function is working correctly, ie. the token prints in the console.log
app.get('/api/login', (req, res) => {
... // perform database lookup here
// Set up JWT for the returned user
const secret = 'secretKey';
const options = { expiresIn: '1h' };
const token = jwt.sign(user, secret, options);
console.log('the token -> ', token);
res.cookie('token', token, { httpOnly: true });
console.log('res.headers (check for cookie) -> ', res);
// this logs the entire response object at the end of
// which Set-Cookie is present and contains the previously
// created JWT.
res.send(user)
}
The JWT is clearly being set in the response headers under Set-Cookie.
The problem is the cookie is not appearing in the dev tools of the client. As a result it is not being sent in the headers of subsequent requests.
It's the first time I have tried to implement JWT so I could be missing something basic. If anyone has any suggestions how to remedy this I'd much appreciate the help.
EDIT
I have tested the JWT that is being produced in the server route using the tool at jwt.io and it comes back as an invalid signature even though it shows the correct user data in the decoded window. I'm not sure if this itself is causing the token not to be sent to/stored on the client but there is obviously something going wrong while encoding the JWT.

Related

In Nuxt.js how do you restrict some routes only to clients with a valid JWT token?

In Nuxt.js this is one way to implement authentication :
The client authenticates by sending an HTTP request with its credentials in the body to an API route of the Nuxt backend ;
The Nuxt backend responds with a JWT token that allows the client to access protected routes ;
Finally, when the authenticated user tries to access such a route, they make an HTTP request to the Nuxt backend with their JWT token inserted in the header ;
The backend validates the JWT token and responds with the requested page JSON data to the client.
What I don't understand is how to make the Nuxt backend aware that for some protected routes it has to check the JWT token of the client before providing the page JSON data. I mean, where exactly in Nuxt can I implement this kind of validation ?
Well i am confused a bit first you say API data the other sentece you say JSON page.. however. If you want to protect an PAGE then you create an middleware
middleware/auth.js
export default async function ({ store, $axios, redirect }) {
let valid = await $axios.$post('/api/checktoken')
if (!valid) {
redirect('/')
}
}
You need to create an API where you check the token. Usually you need to put the token in your header like Authentication: Bearer token... however i simply save my token inside an cookie. Because if you send an HTTP request to the server the cookies gets automatically sended with it so i dont need to do some extra work.
Next step is you go to some page and set your middleware auth.
page.vue
<script>
export default {
middleware: "auth"
}
</script>
However if you want to protect some backend routes you can do it like this. Create again an middleware
async authenticate(req, res, next) {
let token = await cookieService.getTokenFromCookie(req)
if (!token) return errorService.resError(res, 400, 'Authorization failed')
let tokenValid = await tokenService.verifyToken(token)
if (!tokenValid)
return errorService.resError(res, 400, 'Authorization failed')
let decodedData = tokenService.decode(token)
if (!decodedData)
return errorService.resError(res, 400, 'Authorization failed')
res.locals.userId = decodedData.userId
res.locals.role = decodedData.role
next()
}
In this case you basically need to read the token out of your cookie. (in case you dont use cookies you will need to read it out of your header so for this you should create an function that reads your token out of the header)
Check if token is even there.
Verify if token is valid.
Decode the token so you can access the data in it
Now you can also put the data to your res.locals. The advantage is that this data is scoped to this current request and you can access it in the next middleware / endpoint.
then you call next() to step to the next middleware / endpoint
function endpoint(req, res) {
let { userId, role } = res.locals
do something....
}
So the route looks something like this:
app.use("/some/api/point", authenticate, endpoint)
The good thing about is you can put authenticate in every API route you want to protect.

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.

I don't want to send form data back with express, but it does, or does it?

I know the title of this question might sound confusing, but my problem is actually simple. I have these two handlers for /login get and post requests:
loginRender(req, res) {
let options = { title: 'Login', layout: 'auth.hbs' }
res.render('login', options)
}
login (req,res){
let user = Routes.findUser(req.body.username)
let passwordCorrect = Routes.hashCompare(
req.body.password,
user.password
)
if (passwordCorrect) {
let token = Routes.jwtsign(req.body.username)
let refreshToken = Routes.jwtRefreshToken(req.body.username)
Routes.authRedirect(res, token, refreshToken)
} else {
Routes.badRequestRedirect(res, '/login')
}
}
authRedirect(res, token, refreshToken )
{
let options = {
cssPath: 'styles/querystyle.css',
}
res.cookie('access_token', `${token}`, { httpOnly: true })
res.cookie('refresh_token', `${refreshToken}`, { httpOnly: true })
res.status(200).render('query', options)
}
// app.use(urlencoded)
// app.use(cookieParser)
// app.post('/login', login)';
// app.get('/login', loginRender)
Please, ignore all unrelated stuff.
So, everytime I complete login, I get my webpage rendered and I can actually open inspector and see this:
Page Inspector
Address line
How can I fix that? I want my user to be redirected to dashboard-like page and not to receive his sensitive data in insecure form.
UPD
there's also auth middleware that only appends req.username in case we did parse jwt successfully, and there's a little bit of interaction with it, but it does not appear on page until I go to this page manually by writing the address in address line.
If you don't send the data to the Express server, then you can't read it in you login function and you can't authenticate the user.
It is not a problem is the user can use the tools in their own browser to inspect the data that they entered.
You need it to be encrypted in transport (i.e. use HTTPS and not plain HTTP, at least in production) but you don't need to worry about the user finding out their own password.

express node how to avoid sending user data on every request?

i was wandering what is the best way to avoid sending the user data on every request,
lets say i want to add product from user's account, i have to send the user. or i want to order something, i have to send the user.
i thought about something like this:
app.use(async (req, res, next) => {
if (!req.body.userId) {
return next();
}
const user = await enteties.User.findByPk(req.body.userId);
req.user = user;
next();
});
but it also requires me to send the user on evey request..
there must be a way to avoid sending the user data to the server on almost every request.
also, it will make all my requests of type "post" since i have to send the user, and even "get" requests are now become "posts", for sure this is not correct
If you implement your JWT token correctly you don't need to send the logged in user id.
JWT tokens contain a payload section that is basically any JSON data you want to set. This is basically your decentralized session stored in the user's machine. When creating a JWT token you'd normally do something like:
const jwt = require('jsonwebtoken');
const config = require('./config');
function generateToken(user) {
let payload = {
sub: user.id
};
return jwt.sign(payload, config.secret, {
algorithm: 'HS512', // choose algorithm appropriate for you
expiresIn: config.expires
})
}
That payload part allows you to send user identifying information. In the case above, the user id. To get that id from a request simply verify it:
app.use((req, res, next) => {
const token = req.get('Authorization');
jwt.verify(token, config.secret, (err, payload) => {
if (err) {
next(err);
}
else {
req.user = payload; // user.sub is the user id
next();
}
});
});
Or you can use a library such as express-jwt to do it for you:
const expressJwt = require('express-jwt');
const express = require('express');
const config = require('./config');
const app = express();
app.use(expressJwt({ secret: config.secret }); // use express-jwt like any
// middleware, you can even install
// it on specific routes.
Now in your controller/route you can simply extract the payload in the req.user object. Invalid tokens or requests without tokens will completely skip your handler and immediately return an error or unauthorized response:
app.get('/some/endpoint', (req, res) => {
console.log('user is', req.user.sub); // note: req.user is our payload
});
Additional tricks:
As I mentioned, the payload is basically user defined. If you need to keep track of other user information such as roles or permissions you can store them in the JWT token:
// Example payload
let payload = {
sub: user.id,
admin: user.role === 'admin',
gender: user.gender
};
This reduces the number of database requests needed to process the user. Making the authentication system completely decentralized. For example you may have a service that consumes this JWT token that is not connected to your user database but need to check if user is admin. With the right payload that service does not even need to have access to the user database.
Note however that the payload is not encrypted. It is just base64 encoded. This means that the information in the token can be easily read by anyone with access to it (normally the user but beware of 3rd party scripts). So ideally you shouldn't store dox-able information in the payload if you have 3rd party scripts on your website (then again, it is highly unusual these days for anyone to write the entire front-end from scratch without any libraries or frameworks)
Also note that the more you put in your payload the larger your token will be.

Unexpected end of JSON input getting Instagram JSON data with request

I'm trying to display the JSON data form instagram's URL: https://www.instagram.com/apple/?__a=1.
If I put this in browser, it shows the JSON, but I want to be able to get it with express so I can attempt to capture any data I need to with that URL
const express = require("express");
const Instagram = require("node-instagram").default;
const keys = require("./config/keys");
const app = express();
const request = require("request");
app.get("/", (req, res) => {
const url = "https://www.instagram.com/apple/?__a=1";
request(url, (err, response, body) => {
const json = JSON.parse(body);
console.log(json);
res.json(request.json);
});
});
app.listen(5000);
When I'm trying to deploy this in localhost, I'm getting an error:
SyntaxError: Unexpected end of JSON input
If you are logged in to instagram in your browser, you are getting authentication via cookies, and everything works fine, but if you are not logged in, the url https://www.instagram.com/apple/?__a=1 responds wit a 403 (access denied) error. This is the case for your back-end code since you are not sending cookies or authentication headers.
You are probably getting an error but not checking for errors in your request callback (the err parameter).
According to the instagram developer documentation, you need to register your app and authenticate before you can perform the request with OAuth credentials.
Check request's documentation on OAuth signing for more info.

Categories