Good morning people
I am setting up the front-end for testing, In my application I use Passport, Token Jwt for authentication. At Postman, everything is ok! I can authenticate myself.
Now in the Browser, I log in it generates the token, however when trying to execute a route protected by the passport it returns me unauthorized
Follow my code. I'm a beginner, first post on the stackoverflow.
Routes.js
app.route('/rifas')
.all(app.config.passport.authenticate())
.post(app.api.rifas.saverifa)
Passport.js
const authSecret = process.env.authSecret
const passport = require('passport')
const passportJwt = require('passport-jwt')
const { Strategy, ExtractJwt } = passportJwt
module.exports = app => {
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: authSecret
}
const strategy = new Strategy(options,(payload, done) => {
app.db('users')
.where({id: payload.id})
.first()
.then( user => {
if(user) {
done(null, {id: user.id, email: user.email})
}
else {
done(null,false)
}
})
.catch(err => done(err,false))
})
passport.use(strategy)
return {
initialize: () => passport.initialize(),
authenticate: () => passport.authenticate('jwt', {session: false})
}
}
Archive api.js
import axios from 'axios'
const api = axios.create({
baseURL: 'localhost:3333',
})
export default api
Archive of front-end
async function handleLogin (e) {
e.preventDefault()
try {
const {data: token} = await axios.post('http://localhost:3333/signin', {
name: name,
email: email,
password: password
})
api.defaults.headers.Authorization = `Bearer ${token}`
window.location.href="http://localhost:3000/Header"
alert(`Logado com sucesso ${name}`)
}catch(e) {
alert('Erro no login')
}
}
Related
I am using JWT token for authentication in my MERN app. when i click on this endpoint "http://127.0.0.1:1000/api/v1/users/login" in postman to login a user and this endpoint "http://127.0.0.1:1000/api/v1/users/verify" to verify a user's token, the token is returned. However, when i do the same operation in my react frontend app, the token returns undefined. Please what am i doing wrong? Here is my React code. P.S the token is stored in the cookies.
function Welcome() {
const [user, setUser] = useState("");
const sendRequest = async () => {
const res = await axios
.get("http://127.0.0.1:1000/api/v1/users/verify", {
withCredentials: true,
})
.catch((err) => console.log(err));
const data = await res.data;
console.log("RESPONSE", data);
return data;
};
React.useEffect(() => {
sendRequest().then((data) => console.log(data));
}, []);
console.log(user);
return <div>Welcome</div>;
}
Here is the Verify token code in my express app
exports.verifyToken = async (req, res, next) => {
const cookies = await req.headers.cookie;
const token = cookies.split("=")[1];
if (!token) {
res.status(404).json({ message: "no token found" });
}
jwt.verify(String(token), process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(400).json({message: "Invalid token" });
}
req.id = user.id;
});
next();
};
User cookie parser
const express = require("express");
const app = express();
const cookieParser = require("cookie-parser");
app.use(express.json());
app.use(cookieParser());
app.get("/", (req, res) => {
res.setHeader("Set-Cookie", "name=langesh;SameSite=None;");
res.send("hello");
});
app.get("/get-cookie", (req, res) => {
res.send(req.cookies);
});
app.listen(9000);
output
{name : "langesh"}
I personally don't have much experience with axios, I prefer fetch() method, it works perfectly.
That said, I think this issue could arise because token isn't identified in the request. Try adding headers: { Authorization: `Bearer ${token}`} to your get method and see if it is fixed.
Your code should look like :
axios.get("http://127.0.0.1:1000/api/v1/users/verify", {
headers: {
Authorization: `Bearer ${token}`
},
withCredentials: true,
})
I used the postman for testing my url,and it has response data(It's correct).
Url:http://localhost:8000/api/products/find/6337d5d73ec2c05d7c11bc63(ProductId).
In postman,it could get response data.
I wonder I do give it Bearer token in my requestMethods.js,which imported in /pages/Product.jsx.
But when I use the same id in axios.get,it said error:
Error:GET http://localhost:8000/api/products/find/6337d5d73ec2c05d7c11bc63 401 (Unauthorized)
can someone just help me solve this question?
In /pages/product.jsx line 145,157 are got tag in the photo.
Here are my relative Codes:
requestMethods.js
import axios from "axios";
const BASE_URL = "http://localhost:8000/api/"
const TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9......"
export const publicRequest = axios.create({
baseURL: BASE_URL,
});
export const userRequest = axios.create({
baseURL: BASE_URL,
headers: { token: `Bearer ${TOKEN}` },
});
/pages/Product.jsx
It's mainly codes of website.
import { useLocation } from 'react-router-dom'
import { useState, useEffect } from 'react';
import { publicRequest,userRequest } from './requestMethods';
const Product = () => {
// 回傳路徑
const location = useLocation();
const id = location.pathname.split("/")[2];
const [product, setProduct] = useState({});
useEffect(() => {
const getProduct = async () => {
try {
const res = await publicRequest.get("/products/find/" + id);//HERE USE REQUESTMETHODS.JS and it's it sad line:145
console.log(res.data)
setProduct(res.data);
}
catch { } }
getProduct();//line 157
}, [id])
}
export default Product
index.js
const express = require("express");
const app = express();
const mongoose = require("mongoose");
const dotenv = require("dotenv");
const userRouter = require("./routes/user");
const authRouter = require("./routes/auth");
const productRouter = require("./routes/product");
const cors=require("cors");
dotenv.config();
mongoose.connect(
process.env.MONGO_URL)
.then(() => console.log("connect"))
.catch(((err) => {
console.log(err);
}));
app.use(express.json());
app.use(cors());
app.use("/api/users", userRouter);
app.use("/api/auth", authRouter);
app.use("/api/products", productRouter);//USE THE ROUTES/PRODUCT.JS
app.use("/api/orders", orderRouter);
app.use("/api/cart", cartRouter);
app.use("/api/checkout",stripeRouter)
app.listen(process.env.PORT || 8000, () => {
console.log("Run server")
})
routes/product.js
router.put("/:id", verifyTokenAndAdmin, async (req, res) => {
try {
const updatedProduct = await Products.findByIdAndUpdate(req.params.id, {
$set: req.body
}, { new: true }
);
res.status(200).json(updatedProduct);
} catch (err) {
res.status(500).json(err);
}
});
//delete
router.delete("/:id", verifyTokenAndAdmin, async (req, res) => {
try {
await Products.findByIdAndDelete(req.params.id);
res.status(200).json("Products has been deleted.")
} catch (err) {
res.status(500).json(err);
}
})
//get product
router.get("/find/:id", verifyTokenAndAdmin, async (req, res) => {
try {
const product = await Products.findById(req.params.id);
res.status(200).json(product);
} catch (err) {
res.status(500).json(err);
}
})
verifyToken.js
const jwt = require("jsonwebtoken");
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(401).json("Token is not valid!")
req.user = user;
next();
})
}
else {
return res.status(401).json("You are not authentication!" );
}
}
module.exports = { verifyToken, verifyTokenAndAuth, verifyTokenAndAdmin };
The reason is you are using publicRequest axios function which does not include bearer token header. Use userRequest function.
Try using the code below in your axios request from frontend:
const res = await userRequest.get("/products/find/${id}");
Instead of double quotes use string literal. Stack overflow is using my string literal as code snippet that's why I wrote in double quotes.
Also remove the extra slash after api from BASE_URL like this:
const BASE_URL = "http://localhost:8000/api"
Also check in your verifyToken.js file if you have written the functions verifyTokenAndAuth and verifyTokenAndAdmin which you are exporting. If not then please write those functions in verifyToken.js file and then export because you are using verifyTokenAndAdmin middleware in your api. Also please check that server is running and not crashing before making a request.
For whatever reason, the token generated by jsonwebtoken never expires.
Here is my code so far.
auth.ts Middleware.
// Libs
import { Express, Request, Response, NextFunction } from "express";
import { PassportStatic } from "passport";
import { Strategy as JWTStrategy, ExtractJwt } from "passport-jwt";
// Users
import { usersDB } from "../users";
const setupAuth = (api: Express, passport: PassportStatic) => {
const strategy = new JWTStrategy(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: "123456qwerty",
algorithms: ["HS256"],
},
(payload, cb) => {
try {
const { sub } = payload;
const user = usersDB.find((u) => u.username === sub);
if (user) {
return cb(null, user);
} else {
return cb(null, false);
}
} catch (e) {
return cb(e);
}
}
);
api.use(passport.initialize());
passport.use(strategy);
};
export default setupAuth;
Login route
import { Request, Response } from "express";
import { usersDB, validatePassword } from "../../users";
import { genJWT } from "../../utils/auth";
const login = (req: Request, res: Response) => {
const { username, password } = req.body;
const user = usersDB.find((u) => u.username === username);
if (!user) {
return res
.status(401)
.json({ status: "fail", message: "Invalid username or password" });
}
if (!validatePassword(password, user.salt, user.hash)) {
return res
.status(401)
.json({ status: "fail", message: "Invalid username or password" });
}
const token = genJWT(user.username);
res.status(200).json({ status: "success", token });
};
export default login;
And the jwt token generator
import jwt from "jsonwebtoken";
export const genJWT = (username: string) => {
const token = jwt.sign({ sub: username, iat: Date.now() }, "123456qwerty", {
expiresIn: "1min",
algorithm: "HS256",
});
return token;
};
Then the secured routes
// Lib
import { Express } from "express";
import { PassportStatic } from "passport";
// GET
import root from "./GET/root";
import currentUser from "./GET/current-user";
import privateContent from "./GET/private-content";
// POST
import register from "./POST/register";
import login from "./POST/login";
import logout from "./POST/logout";
const setupRoutes = (api: Express, passport: PassportStatic) => {
api.get("/", root);
api.get(
"/current-user",
passport.authenticate("jwt", { session: false }),
currentUser
);
api.get(
"/private-content",
passport.authenticate("jwt", { session: false }),
privateContent
);
api.post("/register", register);
api.post("/login", login);
api.post("/logout", logout);
};
export default setupRoutes;
So the API is working, able to generate jwt token, able to authenticate with the token. It is able to validate too if I modify the token. But the problem is I can forever use the token. It never expires.
Is there something I missed?
Thanks in advance.
Ok when I removed
iat: Date.now()
from the jwt.sign, now the token does expire. So never put iat, let jsonwebtoken generate it.
I'm trying to make a login system which is part of my project with react, express, MySQL and Axios but I keep getting this error- Uncaught (in promise) Error: Request aborted
Server side:
const express = require("express");
const cors = require("cors");
const mysql = require("mysql");
const app = express();
app.use(express.json())
app.use(cors());
const db = mysql.createConnection({
host: "localhost",
user: "root",
password: "cool12345",
database: "users"
})
db.connect(err => {
if(err){
return err;
}
})
console.log(db)
app.post("/register", (req, res) => {
const username = req.body.username;
const password = req.body.password;
const email = req.body.email;
db.query("INSERT INTO teachers (name, email, password) VALUES (?,?)", [username, email, password], (error, result) => {
console.log(error)
})
db.query("INSERT INTO teachers (name, email, password) VALUES ('test', 'test2', 'test3')")
})
app.listen(4000, () => {
console.log("Listening on port 4000")
})
client side:
import React, { useState } from 'react'
import "../styling/SignUp.css";
import { useHistory } from "react-router-dom"
import Axios from "axios";
function SignUp() {
const [usernameReg, setUsernameReg] = useState("")
const [emailReg, setEmailReg] = useState("")
const [passwordReg, setPasswordReg] = useState("")
const register = () => {
Axios.post("https://localhost:4000/register", {username: usernameReg, email:emailReg, password: passwordReg}).then((response) => {
console.log(response)
})
}
the function in the client-side returns a form but there's too much code so I left it out of this question. The signup button has an onClick handler function which runs the register.
I doubt your localhost has an ssl certificate
try:
Axios.post("http://localhost:4000/register", {username: usernameReg, email:emailReg, password: passwordReg}).then((response) => {
console.log(response)
})
http instead of https
EDIT: I was too hasty to jump to a conclusion. What is that object you are passing to axios? I think it should be:
Axios.post("http://localhost:4000/register", {
data: {
username: usernameReg,
email:emailReg,
password: passwordReg
}
}).then((response) => {
console.log(response)
}).catch(err => console.log(err))
You can also catch the error. (So it won't be 'uncaught')
Axios.post("http://localhost:4000/register", headers: {
'Content-Type': 'application/json',
}, {
data: {
username: usernameReg,
email:emailReg,
password: passwordReg
}
}).then((response) => {
console.log(response)
}).catch(err => console.log(err))
Try setting a header called Content-Type/application/json when you are using a post request. application/json is used to transfer json data through requests.
THE app is suppose to register the new user and send the new users info to the MongoDB, but when i attempt to register the user it throws an error of 500 internal error. the console says the error is in the user file, the terminal say this is the error, Proxy error: Could not proxy request /api/users from localhost:3000 to https://localhost:5000.
[1] See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (EPROTO).
I've already tried changing the proxy in the packet.json by giving it a different path and target but its not working. maybe i'm overlooking something. enter code here
import React, { useReducer } from 'react';
import axios from 'axios';
import AuthContext from './authContext';
import authReducer from './authReducer';
import {
REGISTER_SUCCESS,
REGISTER_FAIL,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT,
CLEAR_ERRORS
} from '../types';
const AuthState = props => {
//initial state
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: null,
user: null,
loading: true,
error: null
};
const [ state, dispatch ] = useReducer(authReducer, initialState);
// load user
const loadUser = () => console.log('load user') ;
// register user
const register = async formData => {
const config = {
headers: {
'Content-Type': 'application/json'
}
}
try {
const res = await axios.post('api/users', formData, config);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data
});
} catch (err){
dispatch({
type: REGISTER_FAIL,
payload: err.response.data.msg
});
}
}
// login user
const login = () => console.log('login') ;
//logut
const logout = () => console.log('logout') ;
// clear errors
const clearErrors = () => console.log('clearErrors') ;
return (
<AuthContext.Provider
value= {{
token: state.token,
isAuthenticated: state.isAuthenticated,
loading: state.loading,
user: state.user,
error: state.error,
register,
loadUser,
login,
logout,
clearErrors
}}>
{props.children}
</AuthContext.Provider>
);
};
export default AuthState;
//this is my server.js file with the routes
const express = require('express');
const connectDB = require('./config/db')
//connect MongoDB
connectDB();
const app = express();
//init middleware
app.use(express.json({extended: false}));
app.get('/', (req, res) => res.json({ msg: 'hello welcome'})
);
//define routes
app.use('/api/users', require('./routes/users'));
app.use('/api/auth', require('./routes/auth'));
app.use('/api/contacts', require('./routes/contacts'))
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`server is working on ${PORT}`))
// this is mongoDB code
const mongoose = require('mongoose');
const config = require('config');
const db = config.get('mongoURI');
const connectDB = async () =>{
try{ await
mongoose.connect(db, {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false
});
console.log('mongo connected..')
} catch (err){
console.log(err.message);
process.exit(1)
}
};
module.exports = connectDB;
// this the users file where the console is throwing the 500 internal error.
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const config = require('config');
const { check, validationResult } = require('express-validator');
const User = require('../models/User')
// This route Post request to api/users,
// description register a user,
// access to public to register an become a user
router.post('/', [
check('name', 'Name is require').not().isEmpty(),
check('email', 'please include email').isEmail(),
check('password', 'enter a password with atleast 6 characters'
).isLength({min: 6})
],
async (req, res) =>{
const errors = validationResult(req);
if(!errors.isEmpty()){
return res.status(400).json({ errors: errors.array()});
}
const { name, email, password } = req.body;
try{
let user = await User.findOne({email});
if(user){
return res.status(400).json({msg: 'user already exist'})
}
user = new User({
name,
email,
password
});
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
// object to send in the token
const payload = {
user: {
id: user.id
}
}
jwt.sign(payload, config.get('jwtSecret'), {
expiresIn: 36000
}, (err, token) => {
if(err) throw err;
res.json({token});
});
} catch (err){
console.log(err.message);
res.status(500).send('server error')
}
});
module.exports = router;
I figure out the problem!!!
I had an unexpected token in my users file that simple colon was interfering with the code