I'm building a 3rd server to authenticate users with company accounts on google action. I use account linking with OAuth, Linking type: Implicit. I used ngrok.io to build.
auth.js:
const jwtHelper = require("../helpers/jwt.helper");
const accessTokenSecret = process.env.ACCESS_TOKEN_SECRET || "xxx";
let auth = async (req, res) => {
try {
const userFakeData = {
email: req.body.email,
};
const accessToken = await jwtHelper.generateToken(userFakeData, accessTokenSecret);
return res.status(200).json({"token_type": "Bearer", accessToke);
} catch (error) {
return res.status(500).json(error);
}
}
login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Login Form Tutorial</title>
</head>
<body>
<div class="login-form">
<h1>Login Form</h1>
<form action="login" method="POST">
<input type="text" name="email" placeholder="email" required>
<input type="submit">
</form>
</div>
</body>
</html>
jwt.helper.js
const jwt = require("jsonwebtoken");
let generateToken = (user, secretSignature, tokenLife) => {
return new Promise((resolve, reject) => {
const userData = {
email: user.email,
}
jwt.sign(
{data: userData},
secretSignature,
{
algorithm: "HS256",
expiresIn: tokenLife,
},
(error, token) => {
if (error) {
return reject(error);
}
resolve(token);
});
});
}
let verifyToken = (token, secretKey) => {
return new Promise((resolve, reject) => {
jwt.verify(token, secretKey, (error, decoded) => {
if (error) {
return reject(error);
}
resolve(decoded);
});
});
}
module.exports = {
generateToken: generateToken,
verifyToken: verifyToken,
};
middelware auth.js:
const jwtHelper = require('../helpers/jwt.helper');
const accessTokenSecret = process.env.ACCESS_TOKEN_SECRET || "xxx";
let isAuth = async(req, res, next) =>{
const tokenFromCLient = req.body.token || req.query.token || req.headers["access-token"];
if ( tokenFromCLient) {
//thuc hien giai ma xem co hop len khong
try{
const decode = await jwtHelper.verifyToken(tokenFromCLient, accessTokenSecret);
req.jwtDecoded = decode;
next();
}
catch (error) {
return res.status(401).json({ message: Unauthorized})
}
}
else {
return res.status(403).send({message: 'No token provided'})
}
}
module.exports = {
isAuth: isAuth
}
router.js
const express = require("express");
const router = express.Router();
const AuthMiddleWare = require("../middleware/auth");
const AuthController = require("../controllers/AuthController");
let initAPIs = (app) => {
router.post("/auth", AuthController.login);
router.use(AuthMiddleWare.isAuth);
return app.use("/", router);
}
module.exports = initAPIs;
server.js
const express = require("express");
const app = express();
const initAPIs = require("./routes/router");
var bodyParser = require('body-parser');
var path = require('path');
app.use(bodyParser.urlencoded({extended : true}));
app.use(bodyParser.json());
app.get('/', function(request, response) {
response.sendFile(path.join(__dirname + '/public/login.html'));
});
initAPIs(app);
app.listen(2310, () => {
console.log("server is working in localhost:2310")
})
My account linking setup
My Credentials Google Cloud :
This is what happened when I "talk to fun app"
After submit form :
Click link :
Click link:
Nothing happened in Google Console
Related
I've multiple newbie questions regarding Nodejs.
I've build api for the auth/login and i'm questionning about the connection frontend and backend.
so first, I would like to know in my form the action, should I put the url of my api? or another route?
<div class="container">
<div class="boxlogin">
<img id="img-logo" src="img/logo.png" alt="Logo">
<h1>Sign in</h1>
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<form action="????" method="POST">
<input type="email" name="email" id="email" placeholder="Email" class="input-box" required>
<input type="password" name="password" id="password" placeholder="Password" class="input-box" required>
<button type="submit" class="btn-signup">Log in</button>
<p class="compte-deja-cree">Don't have an Account yet ? Sign In</p>
</form>
</div>
<div class="imglogin"><img id="img-login" src="img/undraw_Active_support_re_b7sj.svg" alt="Logologin"></div>
</div>
also in my controller there is a try catch, I would like to display the errors (err of password, or err of email) and I don't know at all how I have to do it.
For information i'm using nodejs/mongodb/express/ejs.
here some pieces of my code for understanding.
server.js
app.use("/css", express.static(__dirname + "public/css"));
app.use("/img", express.static(__dirname + "public/img"));
app.set("view-engine", "ejs");
app.get("*", checkUser);
app.get("/jwtid", requireAuth, (req, res) => {
res.status(200).send(res.locals.user._id);
});
const userRoutes = require("./routes/user.routes");
const mainRoutes = require("./routes/main.routes");
app.use("/api/user", userRoutes);
app.use('/', mainRoutes);
authController :
const SellerModel = require("../models/sellerModel");
const SuperUserModel = require("../models/superUserModel");
const UserModel = require("../models/userModel");
const LoginModel = require("../models/loginModel");
const jwt = require("jsonwebtoken");
const { signUpErrors, signInErrors } = require("../utils/errorsUtils");
const maxAge = 3 * 24 * 60 * 60 * 1000;
const createToken = (id) => {
return jwt.sign({ id }, process.env.TOKEN_SECRET, {
expiresIn: maxAge,
});
};
module.exports.sellerSignUp = async (req, res) => {
const {company,email,password,telephone,address,zipcode,city} = req.body;
try {
const user = await SellerModel.create({company,email,password,telephone,address,zipcode,city});
res.redirect("/");
} catch (err) {
const errors = signUpErrors(err);
res.status(200).send({ errors });
}
};
module.exports.userSignUp = async (req, res) => {
const {email,password,telephone,address,zipcode,city} = req.body;
try {
const user = await UserModel.create({email,password,telephone,address,zipcode,city});
res.redirect("/");
} catch (err) {
const errors = signUpErrors(err);
res.status(200).send({ errors });
}
};
module.exports.superUserSignUp = async (req, res) => {
const { email, password } = req.body;
try {
const user = await SuperUserModel.create({ email, password });
res.redirect("/");
} catch (err) {
const errors = signUpErrors(err);
res.status(200).send({ errors });
}
};
module.exports.signIn = async (req, res) => {
const { email, password } = req.body;
try {
const user = await LoginModel.login(email, password);
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge });
if (user.role == "seller") {
res.redirect("/success");
} else if (user.role == "user") {
res.redirect("/success-user");
} else if (user.role == "superuser") {
res.redirect("/success-admin");
};
} catch (err) {
const errors = signInErrors(err);
res.status(200).json({ errors });
}
};
module.exports.logout = (req, res) => {
res.cookie("jwt", "", { maxAge: 1 });
res.redirect("/");
};
user.routes:
const router = require("express").Router();
const authController = require("../controllers/authController");
router.post("/register/seller", authController.sellerSignUp);
router.post("/register/user", authController.userSignUp);
router.post("/register/admin", authController.superUserSignUp);
router.post("/login", authController.signIn);
router.get("/logout", authController.logout);
module.exports = router;
main.routes:
const router = require("express").Router();
router.get("/", (req, res) => {
res.render("login.ejs");
});
router.get("/signup-user", (req, res) => {
res.render("signup-user.ejs");
});
router.get("/signup-seller", (req, res) => {
res.render("signup-seller.ejs");
});
router.get("/success", (req, res) => {
res.render("sucessful.ejs");
});
router.get("/success-user", (req, res) => {
res.render("sucessful-user.ejs");
});
router.get("/success-admin", (req, res) => {
res.render("sucessful-admin.ejs");
});
module.exports = router;
Wish you a awesome Sunday !
To resolve this, I add some dependencies :
connect-flash
express-session
express-messages
in my Server.js
app.use(session({
secret:"Secret",
cookie:{ maxAge : 60000},
resave: false,
saveUninitialized: true,
}))
app.use(flash());
app.use(require('connect-flash')());
app.use(function (req,res,next){
res.locals.messages = require('express-messages')(req,res);
next();
})
app.use(express.static("public"));
in authController
module.exports.sellerSignUp = async (req, res) => {
const {company,email,password,telephone,address,zipcode,city} = req.body;
try {
const user = await SellerModel.create({company,email,password,telephone,address,zipcode,city});
req.flash('info', `Successful Register`)
res.redirect("/");
} catch (err) {
const errors = signUpErrors(err);
console.log(errors);
req.flash('message', errors.email || errors.password );
res.redirect('/register-seller');
}
};
in my main route :
const router = require("express").Router();
const services = require('../service/render');
router.get("/", services.login);
router.get("/register-user",services.registerUser);
router.get("/register-seller",services.registerSeller);
router.get("/success",services.sucessful);
router.get("/success-user",services.sucessfull_t);
router.get("/success-admin",services.sucessful_a);
module.exports = router;
render.js:
exports.login = (req, res) => {res.render("login.ejs",{ errorMessage: req.flash('message'), successMsg: req.flash('info') })};
exports.registerUser = (req, res) => {res.render("register-user.ejs",{ message: req.flash('message')})};
exports.registerSeller = (req, res) => {res.render("register-seller.ejs",{ message: req.flash('message')})};
exports.sucessful = (req, res) => {
res.render("sucessful.ejs");
};
exports.sucessfull_t = (req, res) => {
res.render("sucessful-t.ejs");
};
exports.sucessful_a = (req, res) => {
res.render("sucessful-a.ejs");
};
I have a nodeserver serving two routes
/ = https://developers.google.com/gmail/api/quickstart/js
/json/ = {mydata: 'abc'};
node-server.js
import express from 'express';
import path from 'path';
const app = express();
app.set('view engine', 'ejs');
app.set('views', ['src/views']);
app.use('/', express.static(path.join(__dirname, '/')));
app.get('/', (req, res) => {
res.render('index');
});
app.get('/json', (req, res) => {
res.setHeader('Content-Type', 'application/json');
// if authenticated
res.end(JSON.stringify({mydata: 'abc'}));
// else
res.end(JSON.stringify({auth: false}));
});
src/views/index.ejs === (browser example) https://developers.google.com/gmail/api/quickstart/js
Question
After a user has authenticated via browser method, how can I share authenticated state with my nodeserver so that I only show json data if authenticated, otherwise an error?
ok I think it is not possible to access the token if created via the browser. Unless somone else can chip in with a solution.
Meanwhile I have created an alternative to the browser example provided by google quickstart but in node. That way I have control of the access_token from node server and can determine if user is signed in or not.
My solution:
Pre-requisites
Must have created a google workspace with a - first project
Must have created a client id and api key - https://developers.google.com/gmail/api/quickstart/js
Must downloaded the credentials.json file and keep local to nodeserver for use.
Must have supplied: [link OAuthClient Client] > Credentials] Authorised redirect URIs
http://localhost:8000/oauth2callback
This is a typescript implementation so will need cross-env or equivalent to transpile with babel wile running from package.json script.
nodeServer.ts
import yargs from 'yargs';
import express, { Response } from 'express';
import path from 'path';
import fs from 'fs';
import { google, Auth } from 'googleapis';
import url from 'url';
import { gmail_v1 } from 'googleapis/build/src/apis/gmail/v1';
type OAuth2Client = Auth.OAuth2Client;
interface Icredentials {
web: {
client_id: string;
project_id: string;
auth_uri: string;
token_uri: string;
auth_provider_x509_cert_url: string;
client_secret: string;
redirect_uris: string;
};
}
const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
const pathCredentials = path.resolve(__dirname, '../credentials/credentials-webserver.json');
const contentCredentials: string = fs.readFileSync(pathCredentials, 'utf8');
const credentials: Icredentials = JSON.parse(contentCredentials);
const { client_secret, client_id, redirect_uris } = credentials.web;
const oAuth2Client: OAuth2Client = new google.auth.OAuth2(
client_id,
client_secret,
redirect_uris[0]
);
const app = express();
app.set('view engine', 'ejs');
app.set('views', ['src/views']);
const { argv } = yargs;
const port = argv.port || 8000;
const host = argv.host || 'localhost';
const pathRoot = path.join(__dirname, '/');
type TgetSignedIn = (oAuth2Client: OAuth2Client) => Promise<boolean>;
const getSignedIn: TgetSignedIn = oAuth2Client =>
new Promise((resolve, reject) => {
if (!oAuth2Client.credentials.access_token) {
reject(Error('access_token does not exist'))
} else {
resolve(true);
}
});
type TgetLabels = (
auth: OAuth2Client
) => Promise<gmail_v1.Schema$ListLabelsResponse | gmail_v1.Schema$Label[]>;
const getLabels: TgetLabels = auth => {
const gmail = google.gmail({ version: 'v1', auth });
return gmail.users.labels.list({ userId: 'me' }).then(
res =>
// res.config.Authorization: 'Bearer Accesstoken....'
res.data.labels
);
};
type TrenderLabels = (res: Response, isSignedIn: boolean) => void;
const renderLabels: TrenderLabels = (res, isSignedIn) => {
getLabels(oAuth2Client)
.then(labels => {
res.render('index', { labels, isSignedIn });
})
.catch(err => {
res.render('index', { isSignedIn });
});
};
app.use('/', express.static(pathRoot));
app.get('/', (req, res) =>
getSignedIn(oAuth2Client)
.then(() => {
renderLabels(res, true);
})
.catch(() => {
res.render('index', { isSignedIn: false });
})
);
app.get('/getAuthCode', (req, res) => {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
res.render('index', { authUrl, isSignedIn: false });
});
app.get('/oauth2callback', (req, res) => {
const qs = url.parse(req.url, true).query;
const { code } = qs;
if (!code) {
return console.error('Error - unselected email');
}
const decodedCode = decodeURIComponent(code as string);
oAuth2Client.getToken(decodedCode, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
renderLabels(res, true);
});
});
app.get('/signout', (req, res) => {
delete oAuth2Client.credentials.access_token;
res.render('index', { isSignedIn: false });
});
app.get('/services', (req, res) => {
getSignedIn(oAuth2Client)
.then(() => {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ a: 1 }));
})
.catch(err => {
res.end(JSON.stringify({ error: err, custom: 'You are signed out!' }));
});
});
app.listen(port, host, () => {
// console.log('listening on ', host, ':', port);
});
index.ejs
<!DOCTYPE html>
<html>
<head>
<title>Gmail API Quickstart</title>
<meta charset="utf-8" />
<% if (typeof authUrl !== 'undefined') { %>
<meta http-equiv="refresh" content="0 url=<%= authUrl %>" />
<% } %>
</head>
<body>
<p>Gmail API Quickstart</p>
<button id="authorize_button" style="display: none;">Authorize</button>
<button id="signout_button" style="display: none;">Sign Out</button>
<% if (typeof labels !== 'undefined') { %>
<ul>
<% labels.forEach(item => { %>
<li><%= item.name %></li>
<% }) %>
</ul>
<% } %>
<script>
const handleAuthClick = () => {
location.href = '/getAuthCode';
};
const handleSignout = () => {
location.href = '/signout';
};
const init = (isSignedIn) => {
var authorizeButton = document.getElementById('authorize_button');
authorizeButton.style.display = isSignedIn ? 'none' : 'block';
authorizeButton.onclick = handleAuthClick;
var signoutButton = document.getElementById('signout_button');
signoutButton.style.display = isSignedIn ? 'block' : 'none';
signoutButton.onclick = handleSignout;
}
init(<%= isSignedIn %>);
</script>
</body>
</html>
This provides me the following paths:
It creates a similar experience as this: https://developers.google.com/gmail/api/quickstart/js
Now when user goes to /services/ path I display json if logged in or error if not.
Also if anyone has a better suggestion that just removing the access_token manually to detect if user is signed in, please advise.
So I have spent hours trying to figure out why express-fileupload is not working for me.. I have also tried using multer, but I keep getting req.files as either undefined or null. From looking around, it seems like it may have to do with my middleware bc the form is multipart data. I still can't figure out how to get this to work though. Forgive me if it's a stupid mistake.
express app (index.js)
const path = require('path')
const express = require('express')
const morgan = require('morgan')
const compression = require('compression')
const session = require('express-session')
const passport = require('passport')
const SequelizeStore = require('connect-session-sequelize')(session.Store)
const db = require('./db')
const sessionStore = new SequelizeStore({db})
const PORT = process.env.PORT || 8080
const app = express()
const socketio = require('socket.io')
const fileUpload = require('express-fileupload')
var methodOverride = require('method-override');
var multipart = require('multipart');
module.exports = app
// This is a global Mocha hook, used for resource cleanup.
// Otherwise, Mocha v4+ never quits after tests.
if (process.env.NODE_ENV === 'test') {
after('close the session store', () => sessionStore.stopExpiringSessions())
}
/**
* In your development environment, you can keep all of your
* app's secret API keys in a file called `secrets.js`, in your project
* root. This file is included in the .gitignore - it will NOT be tracked
* or show up on Github. On your production server, you can add these
* keys as environment variables, so that they can still be read by the
* Node process on process.env
*/
if (process.env.NODE_ENV !== 'production') require('../secrets')
// passport registration
passport.serializeUser((user, done) => done(null, user.id))
passport.deserializeUser(async (id, done) => {
try {
const user = await db.models.user.findByPk(id)
done(null, user)
} catch (err) {
done(err)
}
})
const createApp = () => {
// logging middleware
app.use(morgan('dev'))
// body parsing middleware
app.use(express.json())
app.use(express.urlencoded({extended: true}))
//file uploads
app.use(fileUpload()); //express-fileupload
// app.use(multer({dest:'./uploads/'})); //multer
// compression middleware
app.use(compression())
// session middleware with passport
app.use(
session({
secret: process.env.SESSION_SECRET || 'my best friend is Cody',
store: sessionStore,
resave: false,
saveUninitialized: false
})
)
app.use(passport.initialize())
app.use(passport.session())
// auth and api routes
app.use('/auth', require('./auth'))
app.use('/api', require('./api'))
// static file-serving middleware
app.use(express.static(path.join(__dirname, '..', 'public')))
// any remaining requests with an extension (.js, .css, etc.) send 404
app.use((req, res, next) => {
if (path.extname(req.path).length) {
const err = new Error('Not found')
err.status = 404
next(err)
} else {
next()
}
})
// sends index.html
app.use('*', (req, res) => {
res.sendFile(path.join(__dirname, '..', 'public/index.html'))
})
// error handling endware
app.use((err, req, res, next) => {
console.error(err)
console.error(err.stack)
res.status(err.status || 500).send(err.message || 'Internal server error.')
})
}
const startListening = () => {
// start listening (and create a 'server' object representing our server)
const server = app.listen(PORT, () =>
console.log(`Mixing it up on port ${PORT}`)
)
// set up our socket control center
const io = socketio(server)
require('./socket')(io)
}
const syncDb = () => db.sync()
async function bootApp() {
await sessionStore.sync()
await syncDb()
await createApp()
await startListening()
}
// This evaluates as true when this file is run directly from the command line,
// i.e. when we say 'node server/index.js' (or 'nodemon server/index.js', or 'nodemon server', etc)
// It will evaluate false when this module is required by another module - for example,
// if we wanted to require our app in a test spec
if (require.main === module) {
bootApp()
} else {
createApp()
}
app.post('/photos/upload', async (req, res, next) => {
try {
console.log(req.files, 'req.files ------')
if (req.files === null) {
res.status(400).send("no file uploaded");
}
console.log(req.files, 'req.files!!!----')
const file = req.files.file;
file.mv(`${__dirname}/client/public/uploads/${file.name}`, err => {
if(err) {
console.error(err);
return res.status(500).send(err);
}
res.json('hello')
// res.json({ fileName: file.name, filePath: `uploads/${file.name}`});
})
// const {name, data} = req.files.picture;
// await knex.insert({name: name, img: data}).into('picture');
} catch (err) {
next(err)
}
}
)
File upload form (CreateListingTest.js)
import React, {useEffect, useState} from 'react'
import {connect} from 'react-redux'
import {useForm} from 'react-hook-form'
import {addNewListing} from '../store/listings'
import axios from 'axios'
/**
* COMPONENT
*/
export const CreateListing = props => {
// const {register, handleSubmit, errors} = useForm()
const [file, setFile] = useState('');
const [filename, setFilename] = useState('Choose File')
const [uploadedFile, setUploadedFile] = useState({});
const onChange = (e) => {
setFile(e.target.files[0]);
setFilename(e.target.files[0].name);
console.log('onChange' , file, filename)
}
const onSubmit = async e => {
e.preventDefault();
const formData = new FormData();
formData.append('files', file);
try {
const res = axios.post('/photos/upload', formData, {
headers: {
'Content-Type' : 'multipart/form-data'
}
});
console.log(res.data, 'res.data in test')
const { fileName, filePath } = res.data;
setUploadedFile({ fileName, filePath });
} catch(err) {
console.log(err, 'error')
}
}
return (
<div className="create-listing">
<h2>Create New Listing</h2>
<div className="all-listings">
<form className="create-listing-form" onSubmit={onSubmit} action="/upload" method="POST">
<input
type="file"
id="img"
name="file"
accept="image/*"
onChange={onChange}
/>
<label>{filename}</label>
<div className="create-listing-form-section">
<input type="submit" value="Upload"/>
</div>
</form>
</div>
</div>
)
}
/**
* CONTAINER
*/
const mapState = state => {
return {
// myListings: state.listings.myListings,
user: state.user
}
}
const mapDispatch = (dispatch, state) => {
return {
addNewListing: (userId, listing) => dispatch(addNewListing(userId, listing))
}
}
export default connect(mapState, mapDispatch)(CreateListing)
I try to deploy a web app. But i got a problem using a chat in my web site. It's work perfectly fine in local but not online. I got a response 401 "Unauthorized" when i try to access to my chat. I use socket.io
Here is the code :
Index.js
const express = require('express');
const bodyparser = require('body-parser');
const security = require('./middleware/security');
const userRouter = require('./routes/user');
const AnnonceRouter = require('./routes/annonce');
const securityRouter = require('./routes/security');
const commentRouter = require('./routes/comment');
const mailRouter = require('./routes/mail')
const path = require('path');
const isDev = process.env.NODE_ENV !== 'production';
const PORT = process.env.PORT || 5000;
const app = express();
const cors = require('cors');
var chat = require('https').createServer(app)
var io = module.exports.io = require('socket.io').listen(chat)
const SocketManager = require('./SocketManager')
io.on('connection', SocketManager)
if (process.env.NODE_ENV === 'production') {
app.use(express.static('../client/build')); // serve the static react app
app.use(cors());
app.use(bodyparser.json());
app.use(security.verifyToken);
app.use('/', securityRouter);
app.use('/annonce', AnnonceRouter);
app.use('/user', userRouter);
app.use('/comment', commentRouter);
app.use('/mail', mailRouter);
app.get(/^\/(?!api).*/, (req, res) => { // don't serve api routes to react app
res.sendFile(path.join(__dirname, '../client/build/index.html'));
})
console.log('Serving React App...');
};
app.listen(PORT, function () {
console.error(`Node ${isDev ? 'dev server' : 'cluster worker '+process.pid}: listening on port ${PORT}`);
});
a part of my layout.js
import React, { Component } from 'react';
import io from 'socket.io-client'
import { USER_CONNECTED, LOGOUT } from '../Events'
import LoginForm from './LoginForm'
import ChatContainer from './chats/ChatContainer'
const socketUrl = "https://teachonline.herokuapp.com"
export default class Layout extends React.Component {
constructor(props) {
super(props);
this.state = {
socket:null,
user:null
};
}
componentWillMount() {
this.initSocket()
}
/*
* Connect to and initializes the socket.
*/
initSocket = ()=>{
const socket = io(socketUrl)
socket.on('connect', ()=>{
console.log("Chat Connected");
})
this.setState({socket})
}
When i try to access to my chat here is the logs in Heroku
2019-08-26T22:25:04.828537+00:00 app[web.1]: TypeError: Cannot read property 'replace' of undefined
2019-08-26T22:25:04.828550+00:00 app[web.1]: at verifyToken (/app/server/middleware/security.js:13:29)
Here is my security.js
const verifyJWTToken = require('../libs/auth').verifyToken;
const access_routes = ["/login_check", "/user", "/mail/send", "/landing-page", "/security/login", "/chat","/socket.io"]
const verifyToken = (req, res, next) => {
if(access_routes.indexOf(req.path) > -1) {
next();
} else {
const auth = req.get('Authorization');
if(!auth || !auth.startsWith('Bearer ')) {
res.sendStatus(401);
}
verifyJWTToken(auth.replace("Bearer ", ""))
.then((decodedToken) => {
req.user = decodedToken;
next();
})
.catch((error) => res.status(400).send({
error: "JWT TOKEN invalid",
details: error
}));
}
}
module.exports = {
verifyToken
}
if it's needed the auth.js
const jwt = require('jsonwebtoken');
const JWT_SECRET = "MaBelleJonquille";
const createToken = function (user = {}) {
return jwt.sign({
payload: {
userName: user.user_name
}
}, JWT_SECRET, {
expiresIn: "7d",
algorithm: "HS256"
});
};
const verifyToken = function (token) {
return new Promise((resolve, reject) => jwt.verify(token, JWT_SECRET, (err, decodedToken) => {
if(err || !decodedToken) {
reject(err);
}
resolve(decodedToken);
}));
};
//fonction pour hasher le password rentré
module.exports = {
createToken,
verifyToken
}
The example of a request
let myHeaders = new Headers();
myHeaders.append("Content-type", "application/json");
myHeaders.append("Authorization", "Bearer "+localStorage.getItem('tokenJWT'));
fetch (URL + localStorage.getItem('user_name'),
{
method:'GET',
mode: "cors",
headers : myHeaders
})
.then(response => response.json())
.then(data => {
data.user_skill.map(x => {
this.skill.push({label: x, value: x});
});
})
.catch(error => (error));
I tried severals things found in the internet but none of them worked for me, so if you have any idea of what am i doing wrong, i'm listening.
Thanks for reading me
I am trying to retrieve my jwt token after login within my Profile component and store the token as within localstorage. I know that localstorage isnt the best option but this will be a native mobile app so localstorage from what I know is my best option. However when I sign in , I receive a token , here is an token that Ive received recently
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaGVjayI6dHJ1ZSwiaWF0IjoxNTYzMDUzMzk4LCJleHAiOjE1NjMwNTQ4Mzh9.tw8ks-jFZID5rmwhoNTkWV8598niE3zMFIDk4Gz-sVg
when my Profile component trys to retrieve /current it use jwt malformed
Server.js
var express = require('express');
var cors = require('cors');
var bodyParser = require('body-parser');
var app = express();
var port = process.env.PORT || 5000;
var morgan = require('morgan');
const auth = require('./middleware/auth');
const User = require('./models/User');
app.use(cors());
app.use(
bodyParser.urlencoded({
extended: false
})
);
app.use(bodyParser.json());
/*========= Here we want to let the server know that we should expect and allow a header with the content-type of 'Authorization' ============*/
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Headers', 'Content-type,Authorization');
next();
});
// use morgan to log requests to the console
app.use(morgan('dev'));
var Users = require('./routes/Users');
app.use('/users', Users);
// Create a Server
const PORT = process.env.PORT || 5000; // Environment variable or port 5000
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
module.exports = app;
User.js
const express = require('express');
const users = express.Router();
const cors = require('cors');
const jwt = require('jsonwebtoken');
var exjwt = require('express-jwt');
var jwtauth = require('../middleware/authjwt.js');
const bcrypt = require('bcrypt');
const bodyParser = require('body-parser');
const User = require('../models/User');
const config = require('config');
const auth = require('../middleware/auth');
const secret = 'dasdsad';
users.use(
bodyParser.urlencoded({
extended: true
})
);
users.use(bodyParser.json());
users.use(cors());
users.post('/register', (req, res) => {
const today = new Date();
const userData = {
first_name: req.body.first_name,
last_name: req.body.last_name,
email: req.body.email,
password: req.body.password,
created: today
};
User.findOne({
where: {
email: req.body.email
}
})
//TODO bcrypt
//Need validation to appear on console and in view
.then(user => {
if (!user) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
userData.password = hash;
User.create(userData)
.then(user => {
res.json({ status: user.email + 'Registered!' });
})
.catch(err => {
res.send('error: ' + err);
});
});
} else {
res.json({ error: 'User already exists' });
}
})
.catch(err => {
res.send('error: ' + err);
});
});
users.post('/authenticate', (req, res) => {
User.findOne({
where: {
email: req.body.email
}
}).then(user => {
if (user) {
if (bcrypt.compareSync(req.body.password, user.password)) {
const payload = {
check: true
};
const token = jwt.sign(payload, config.get('myprivatekey'), {
expiresIn: 1440 // expires in 24 hours
});
res.json({
message: 'authentication done ',
token: token
});
console.log('Successful Login');
console.log(user.first_name);
} else {
res.json({ message: 'please check your password !' });
console.log('incorrect password');
}
} else {
res.json({ message: 'user not found !' });
console.log('user cannot be found');
}
});
});
//users.get('/something', [express.bodyParser(), jwtauth], function(req, res) {
// do something
//});
// app.all('/api/*', [express.bodyParser(), jwtauth]);
users.get(
'/current',
exjwt({ secret: config.get('myprivatekey') }),
async (req, res) => {
const user = await User.findById(req.user._id).select('-password');
console.log(user);
res.send(user);
}
);
Login
import React, { Component } from 'react';
class Login extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
errors: {}
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
onSubmit(e) {
e.preventDefault();
const user = {
email: this.state.email,
password: this.state.password
};
var token = window.localStorage.getItem('token');
fetch('http://localhost:5000/users/authenticate', {
method: 'POST', // or 'PUT'
body: JSON.stringify(user), // data can be `string` or {object}!
headers: {
'Content-Type': 'application/json'
}
});
}
render() {
return (
<div className='container'>
<div className='row'>
<div className='col-md-6 mt-5 mx-auto'>
<form noValidate onSubmit={this.onSubmit}>
<h1 className='h3 mb-3 font-weight-normal'>Please sign in</h1>
<div className='form-group'>
<label htmlFor='email'>Email address</label>
<input
type='email'
className='form-control'
name='email'
placeholder='Enter email'
value={this.state.email}
onChange={this.onChange}
/>
</div>
<div className='form-group'>
<label htmlFor='password'>Password</label>
<input
type='password'
className='form-control'
name='password'
placeholder='Password'
value={this.state.password}
onChange={this.onChange}
/>
</div>
<button
type='submit'
className='btn btn-lg btn-primary btn-block'
>
Sign in
</button>
</form>
</div>
</div>
</div>
);
}
}
export default Login;
This is how I am currently fetching the token within My Profile component
componentDidMount() {
var token = localStorage.getItem('token');
fetch('http://localhost:5000/users/current', {
//cahng taht
method: 'GET',
mode: 'cors',
headers: { Authorization: 'Bearer ' + token }
});
}
I guess the problem is on the expiresIn. 'expiresIn' expressed in seconds or a string describing a time span. You set 1440, which means 1440/60 = 24minutes => expires after 24 minutes not after 24 hours... Could you please try to set as below:
const token = jwt.sign(payload, config.get('myprivatekey'), {
expiresIn: '24h' // expires in 24 hours
});