I am able to create a jwt token:
fastify.post('/signup', (req, reply) => {
const token = fastify.jwt.sign({
payload,
})
reply.send({ token })
})
that can return something like:
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MjM3MDgyMzF9.HZqqiL7wwPaEQihUGoF7Y42Ia67HgKJ-1Ms38Nvcsmw"}
but if I try to decode the username from token
fastify.get('/decode', async (request, reply) => {
const auth = request.headers.authorization;
const token = auth.split(' ')[1]
fastify.jwt.verify(token, (err, decoded) => {
if (err) fastify.log.error(err)
fastify.log.info('username : ' + decoded.username)
reply.send({
foo: decoded,
})
})
})
the response is:
{"foo":{"iat":1523660987}}
this is a working example for your need. Pay attention on what you are signing:
const fastify = require('fastify')({ logger: true })
const fastifyJwt = require('fastify-jwt')
async function customJwtAuth(fastify, opts) {
fastify.register(fastifyJwt, { secret: 'asecretthatsverylongandimportedfromanenvfile' })
fastify.get('/signup', (req, reply) => {
const token = fastify.jwt.sign({ username: 'John Doo', hello: 'world' })
reply.send({ token })
})
fastify.get('/decode', async (request, reply) => {
const auth = request.headers.authorization;
const token = auth.split(' ')[1]
fastify.jwt.verify(token, (err, decoded) => {
if (err) fastify.log.error(err)
fastify.log.info('username : ' + decoded.username)
reply.send({ foo: decoded })
})
})
}
fastify.register(customJwtAuth)
fastify.listen(3000)
curl http://localhost:3000/signup
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IkpvaG4gRG9vIiwiaGVsbG8iOiJ3b3JsZCIsImlhdCI6MTU0OTg2ODk3MX0.T8kv8jbyp-3ianO8-CsfxZ5gePZG9PSjY8NvhdNV7uM"}
curl 'http://localhost:3000/decode' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IkpvaG4gRG9v
IiwiaGVsbG8iOiJ3b3JsZCIsImlhdCI6MTU0OTg2ODk3MX0.T8kv8jbyp-3ianO8-CsfxZ5gePZG9PSjY8NvhdNV7uM'
{"foo":{"username":"John Doo","hello":"world","iat":1549868971}}
Related
After deployment to Vercel cookies cannot be set in the function below:
const logIn = (req, res) => {
const { email, password, cookieAge } = req.body;
const throwError = (res) => res.status(500).send({ message: "The credentials are incorrect. Please try again." });
let userFound;
User
.findOne({ email: email })
.then(user => {
userFound = user;
return bcrypt.compare(password, user.password);
})
.then(passwordValidation => {
if (passwordValidation) {
const token = jwt.sign({ userId: userFound.id }, process.env.JWT_SECRET);
// console.log("token:", token);
res.cookie("access-token", token, { maxAge: cookieAge, domain: ".vercel.app" });
res.json(true);
} else {
throwError(res);
}
})
.catch(() => throwError(res));
};
Here are the response headers:
So what could be the problem?
I am a beginner with node js. I want to make an authentication server using jwt (jsonwebtoken).
The problem is when I test my end point "/api/posts?authorisation=Bearer token..." in postman with method POST with the right token, it gives me forbidden.
Here is my code:
const express = require('express')
const jwt = require('jsonwebtoken')
const app = express()
app.get("/api", (req, res) => {
res.json({
message: "Hey there!!!"
})
})
app.post('/api/posts', verifyToken, (req, res) => {
jwt.verify(req.token, "secretkey", (err, authData) => {
if (err) {
res.sendStatus(403) //forbidden
res.send(`<h2>${err}</h2>`)
} else {
res.json({
message: "Post Created...",
authData
})
}
})
})
app.post('/api/login', (req, res) => {
const user = {
id: 1,
username: "John",
email: "john#gmail.com"
}
jwt.sign({ user: user }, "secretkey", (err, token) => {
res.json({
token
})
})
})
function verifyToken(req, res, next) {
const bearerHeader = req.headers["authorization"]
if (typeof bearerHeader !== "undefined") {
const bearerToken = bearerHeader.split(" ")[1]
req.token = bearerToken
next()
} else {
res.sendStatus(403) //forbidden
}
}
app.listen(5000, () => {
console.log("Server is running :)")
})
I expected it to work because I brought it from a tutorial.
Your code works
The problem is in your request invocation:
According to the oauth2 spec, the Authorization token should be a header and your code expect that
So the token should be sent as http header, not as a query param like foo/bar?authorization=Bearer token...".
Here some samples
Postman
Axios (javascript)
let webApiUrl = 'example.com/getStuff';
let tokenStr = 'xxyyzz';
axios.get(webApiUrl,
{ headers: { "Authorization": `Bearer ${tokenStr}` } });
Advice
Read about oauth2 and jwt
Perform the token validation in the middleware to avoid the validation on each route
I have an API in which uses VerifyToken authentication with JWT. This works through postman, however it appears there's an issue passing this through to the frontend to ensure the token is verified.
For one, I have a code block to create verifyToken:
const verifyToken = (req, res, next) => {
const authHeader = req.headers.token;
if (authHeader) {
const token = authHeader.split(" ")[1];
jwt.verify(token, process.env.JWT_SEC, (err, user) => {
if (err) res.status(403).json("Token is not valid!");
req.user = user;
next();
});
} else {
return res.status(401).json("You are not authenticated!");
}
};
If I run the following in Postman, it works all good, header and all.
localhost:5000/api/users/updateUser/62a9be62a8262145b72feee9
This is then handled in requestMethods,
import axios from "axios";
const BASE_URL = "http://localhost:5000/api/";
const user = JSON.parse(localStorage.getItem("persist:root"))?.user;
const currentUser = user && JSON.parse(user).currentUser;
const TOKEN = currentUser?.accessToken;
export const publicRequest = axios.create({
baseURL: BASE_URL,
});
export const userRequest = axios.create({
baseURL: BASE_URL,
bearer: { token: `Bearer ${TOKEN}` },
});
However when I pass this to the frotnend, for example through a request like this,
const updateUser = async () => {
//include all in the state earlier with spread
//update only position, which is input
userRequest.put(`users/updateUser/${user._id}`, userData);
console.log('your data has updated to', userData)
//include the put request here for API
}
I am now getting an error for a 401: Error: Request failed with status code 401
It appears the token isn't being passed to the frontend correctly. What am I doing wrong?
Passing token in headers in Axios is not tuned correctly. Notice the headers config.
export const userRequest = axios.create({
baseURL: BASE_URL,
headers: { token: `Bearer ${TOKEN}` },
});
Another way to pass the token is where you are calling the API:
const updateUser = async () => {
//include all in the state earlier with spread
//update only position, which is input
userRequest.put(`users/updateUser/${user._id}`, userData, {
headers: {token: `Bearer ${TOKEN}`}
});
console.log('your data has updated to', userData)
//include the put request here for API
}
Here is another tip: in your auth middleware by detecting a wrong token, do not go to next and return!
const verifyToken = (req, res, next) => {
const authHeader = req.headers.token;
if (authHeader) {
const token = authHeader.split(' ')[1];
jwt.verify(token, process.env.JWT_SEC, (err, user) => {
if (err) {
res.status(403).json('Token is not valid!');
return;
} else {
req.user = user;
next();
}
});
} else {
return res.status(401).json('You are not authenticated!');
}
};
I tried to add passport-jwt to my MEVN-stack application, login is successful, but when app tries to redirect to home page after login, I get 401 Unauthorized error in console. I pass token as value of Authorization header in get request on the home page, but it did not help.
This is the code of server.js (the entrypoint of server side):
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cors = require('cors');
const morgan = require('morgan');
const fs = require('fs');
const jwt = require('jsonwebtoken');
const passport = require('passport');
const passportJWT = require('passport-jwt');
const ExtractJWT = passportJWT.ExtractJwt;
const JWTStrategy = passportJWT.Strategy;
const jwtOptions = {};
jwtOptions.jwtFromRequest = ExtractJWT.fromAuthHeaderWithScheme('jwt');
jwtOptions.secretOrKey = 'movieratingapplicationsecretkey';
const app = express();
const router = express.Router();
const User = require('./models/User');
app.use(morgan('combined'));
app.use(bodyParser.json());
app.use(cors());
app.use(passport.initialize());
passport.use(new JWTStrategy(jwtOptions, (jwt_payload, done) => {
User.findOne({ id: jwt_payload.id }, (err, user) => {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
mongoose.connect('mongodb://localhost/movie_rating_app', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log('Connection is established');
})
.catch((err) => {
console.error(`App starting error: ${err.stack}`);
});
// include controllers
fs.readdirSync('controllers').forEach(file => {
if (file.substr(-3) === '.js') {
const route = require(`./controllers/${file}`)
route.controller(app)
}
})
router.get('/', (req, res) => {
res.json({ message: 'API was initialized!' });
});
const port = process.env.API_PORT || 8081;
app.use('/', router);
app.listen(port, () => {
console.log(`api running on port ${port}`);
});
This is the movies.js controllerm, which contains passport.authenticate() method in get request:
const Movie = require('../models/Movie')
const Rating = require('../models/Rating')
const passport = require('passport')
module.exports.controller = app => {
// fetch all movies
app.get(
'/movies',
passport.authenticate('jwt', { session: false }),
(req, res) => {
Movie.find({}, 'name description release_year genre', (error, movies) => {
if (error) console.error(error)
res.send(movies);
})
})
// fetch a single movie
app.get('/movies/:id', (req, res) => {
Movie.findById(req.params.id, 'name description release_year genre', (error, movie) => {
if (error) console.error(error)
res.send(movie);
})
})
}
This is the users.js controller:
const User = require('../models/User');
const passportJWT = require('passport-jwt');
const jwt = require('jsonwebtoken');
const ExtractJwt = passportJWT.ExtractJwt;
const jwtOptions = {};
jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('jwt');
jwtOptions.secretOrKey = 'thisisthesecretkey';
module.exports.controller = app => {
// register a user
app.post('/users/register', (req, res) => {
const newUser = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password,
})
User.createUser(newUser, (error, user) => {
if (error) {
res.status(422).json({
message: 'Something went wrong. Please try again after some time'
})
}
res.send({ user });
})
})
// login user
app.post('/users/login', (req, res) => {
if (req.body.email && req.body.password) {
const email = req.body.email,
password = req.body.password;
User.getUserByEmail(email, (err, user) => {
if (!user) {
res.status(404).json({ message: 'The user does not exist' })
} else {
User.comparePassword(password, user.password, (error, isMatch) => {
if (error) throw error;
if (isMatch) {
const payload = { id: user.id };
const token = jwt.sign(payload, jwtOptions.secretOrKey);
res.json({ message: 'ok', token })
} else {
res.status(401).json({ message: 'The password is incorrect' })
}
})
}
})
}
})
}
This is the script part of Home.vue, when I try to receive response:
<script>
import axios from 'axios';
import MovieCard from '#/components/MovieCard.vue';
export default {
name: 'Home',
components: {
MovieCard,
},
data: () => ({
movies: [],
}),
mounted() {
this.fetchMovies();
},
methods: {
async fetchMovies() {
const token = window.localStorage.getItem('auth');
return axios({
method: 'get',
url: 'http://localhost:8081/movies',
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
})
.then((response) => {
console.log(response);
});
// return axios.get('http://localhost:8081/movies')
// .then(({ data }) => {
// this.movies = data;
// })
// .catch((error) => {
// console.error(error);
// });
},
},
};
</script>
Please don't mark my question as duplicate, because I have already tried some advices such as change ExtractJWT.fromAuthHeaderWithScheme('jwt') to ExtractJWT.fromAuthHeaderWithScheme('bearer'), but it did not help me.
How to fix 401 error?
I replaced all ExtractJwt.fromAuthHeaderWithScheme('jwt') with ExtractJwt.fromAuthHeaderAsBearerToken(), and set all jwtOptions.secretOrKey values as 'thisisthesecretkey'. 401 error does not exist now
I'm trying to create an authentication with JWT, when I created my method it's a function called verifyJWT () it starts firing this error ...
NOTE: Before constructing this function and using it in the route, I can send it calmly, the problem is when I do res.status (200) .send ({auth: true, token: jwtToken}); and then res.redirect ('/ home/ v1'); But really I do not know why this problem ..
What is the correct mode of user authentication and on subsequent routes?
LOGIN ROUTER:
const { body } = require('express-validator/check');
module.exports = (app) => {
app.route('/')
.get((req, res) => { app.controllers.login.controller.redirectRouter(app, req, res) });
app.route('/login')
.get((req, res) => { app.controllers.login.controller.login(app, req, res); })
.post([
body('username').isString().isLength({ min: 1 }).not().isEmpty(),
body('password').isString().isLength({ min: 1 }).not().isEmpty()
], (req, res) => { app.controllers.login.controller.checkLoginForm(app, req, res); });
}
LOGIN CONTROLLER ROUTER
const { validationResult } = require('express-validator/check');
const jwt = require('jsonwebtoken');
module.exports.redirectRouter = (app, req, res) => res.redirect('/login');
module.exports.login = (app, req, res) => {
const renderConfig = new app.models.services.utilities.Render("LOGIN", true);
renderConfig.httpJSPResponseStatusCode = 200;
res.render('login/view', renderConfig.config);
}
module.exports.checkLoginForm = (app, req, res) => {
/** #description: RECEBE O RESULTADO DA VALIÇÃO DO FORMULÁRIO PELO {EXPRESS-VALIDATOR} */
const errors = validationResult(req);
/** #description: VERIFICA SE HOUVE ERRO NA VALIDAÇÃO DO FORMULÁRIO COM O {EXPRESS-VALIDATOR} */
if (!errors.isEmpty()) {
/** #description: CASO ALGUM DADO ESTEJA INCONSISTENTE RETORNA {ERROR: 401} DE {NÃO AUTORIZADO} */
res.status(401).send("401 Não autorizado");
} else {
/** #description: CASO OS DADOS FOREM VÁLIDOS, ADICIONA OS VALORES DO BODY NAS VARIÁVEIS */
const username = req.body.username,
password = req.body.password;
/** FUNÇÕES TEMPORÁRIAS */
if(username === "1" && password === "1"){
const userId = 10;
const jwtToken = jwt.sign({ userId }, process.env.SECRET, {
expiresIn: 300 // EXPIRA EM 5MIN
});
//res.status(200).send({ auth: true, token: jwtToken });
res.redirect('/home/v1');
} else {
res.status(401).send("401 Não autorizado");
}
}
}
HOME ROUTER
const jwtClass = require('../../models/services/JWT/controller.js');
module.exports = (app) => {
app.route('/home/v1/')
.get(jwtClass.verifyJWT, (req, res) => {
console.log("é get")
//app.controllers.home.controller.home(app, req, res);
}).post(jwtClass.verifyJWT, (req, res) => {
console.log("é post")
//app.controllers.home.controller.home(app, req, res);
});
}
** HOME CONTROLLER ROUTER **
module.exports.home = (app, req, res) => {
res.render('home/view');
}
JWT FUNCTION METHOD
const jwt = require('jsonwebtoken');
module.exports.verifyJWT = (req, res, next) => {
const jwtToken = req.headers['x-access-token'];
if (!jwtToken) {
return res.status(401).send({ auth: false, message: 'No token provided.' });
} else {
jwt.verify(jwtToken, process.env.SECRET, (error, decoded) => {
if (error) {
return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' });
} else {
console.log("ae")
req.userId = decoded.id;
next();
}
});
}
}
//res.status(200).send({ auth: true, token: jwtToken });
res.redirect('/home/v1');
You can't redirect after sending Response. Refer the way Request and Response work in REST api. What you can do here is Redirect first and Have the Auth Token sent after Redirection (in the Redirection Route). You need to Write login after Redirection.
Update :
Try adding the line res.status(200).send({ auth: true, token: jwtToken });
inside HOME ROUTER
Also please import Proper Modules to HOME ROUTER.
const jwtClass = require('../../models/services/JWT/controller.js');
module.exports = (app) => {
app.route('/home/v1/')
.get(jwtClass.verifyJWT, (req, res) => {
console.log("é get")
//ADD HERE
res.status(200).send({ auth: true, token: jwtToken });
//app.controllers.home.controller.home(app, req, res);
}).post(jwtClass.verifyJWT, (req, res) => {
console.log("é post")
//ADD HERE
res.status(200).send({ auth: true, token: jwtToken });
//app.controllers.home.controller.home(app, req, res);
});
}
** HOME CONTROLLER ROUTER **
module.exports.home = (app, req, res) => {
res.render('home/view');
}