Invalid signature error when trying to refresh a token - javascript

I have this weird error every time I try to refresh a token after the session token expired
{ success: false, message: 'invalid signature' }
This is the function where I try to refresh my token:
async function updateToken(currentToken, refreshToken) {
const response = await fetch(`${basePath}/auth/token`, {
method: 'GET',
headers: getTokenAndRefreshAuthHeaders(currentToken, refreshToken)
});
const token = await response.json();
await saveTokenInLocalStorage(token);
return token;
}
This is the /token endpoint:
router.get('/token', async (req, res) => {
try {
const token = await TokenController.getTokenFromRequest(req);
const refreshToken = await TokenController.getRefreshTokenFromRequest(req);
await TokenController.verifyRefreshToken(refreshToken);
const decoded = jwt.decode(token);
res.json({
token: TokenController.generateToken(decoded.email, decoded.type, decoded.id)
});
} catch (error) {
ErrorController.errorCallback(error, res);
}
});
and this is my getTokenFromRequest function:
static getTokenFromRequest(req) {
let token = req.body.token || req.headers['x-access-token'] || req.headers['authorization'] || req.params.token;
return TokenController.getNormalizedToken(token);
}
I'm not sure what I'm doing wrong, some console.log() that I placed in those 3 places suggest that the culprit is the getTokenFromRequest but I'm not quite sure

Related

How to handle 401 error status code error in Node.js/Express?

I am working on login functionality in my project, now, flow looks like this (from front-end to back-end):
async login() {
await login({
password: this.userPassword,
login: this.userLogin,
twoFactor: this.twoFactor
}).then((res) => {
if (res.error) {
//
} else {
console.log(res)
}
})
}
And here is starts problems, as you can see if something goes wrong, I return status code 401 and some error message. When I login with correct data, there is no problem with getting token, but when I provide wrong data I have external pending login endpoint in development tools in browser and then, after some time, Error: Request failed with status code 401 in front end terminal. Without this status(401) with just JSON it works fine, but when I try to add 401 code, application crashes.
const userService = require('./../services/userService')
const crypto = require('./../services/cryptoService')
const jwt = require('./../services/jwtService')
const twoFactorService = require('node-2fa')
module.exports = {
login: async (req, res) => {
let { login, password, twoFactor } = req.body
password = crypto.encrypt(password, process.env.APP_KEY)
const result = await userService.getUserToLogin(login, password)
if (!result) {
res.status(401).json({
error: 'Unauthorized'
})
} else {
const faCode = result.twofatoken
const result2F = twoFactorService.verifyToken(faCode, twoFactor);
if ( !result2F || result2F.delta !== 0 ) {
res.status(401).json({
error: 'Unauthorized'
})
} else {
const userId = crypto.encrypt(result.id, process.env.CRYPTO_KEY)
const token = await jwt.sign({
uxd: userId,
});
res.json(token);
}
}
}
}
Actually, I have no idea on what to do with that and how to handle this error.
Ok, here is the answer. Actually, you just need to handle this error in your router:
router.post('/login', async (req, res) => {
try {
const data = await api.post('/login', req.body)
res.json(data.data)
} catch (e) {
// Probably you have here just console.log(e), but this way, you can handle it
res.status(e.response.status).json(e.response.data)
}
})

How can I store my JWT Token in localstorage?

userAction.js -> Frontend, Action
export const login = (userID, password) => async (dispatch) => {
try {
dispatch({ type: USER_LOGIN_REQUEST });
const url = "http://localhost:8080/authenticate/";
const config = {
auth: {
username: userID,
password,
},
};
const data = {};
const response = await axios.post(
url,
data,
config,
)
dispatch({ type: USER_LOGIN_SUCCESS, payload: config});
if (response.status === 200) {
// Login succeeded
const token = response.data.token;
console.log("TOKEN\n" + token);
config.token = response.data.token;
console.log(response.data.token);
}
localStorage.setItem("userInfo", JSON.stringify(config) );
}
My login function in REST Server :
exports.login = async (req,res) =>{
const b64auth = (req.headers.authorization || '').split(' ')[1] || '';
const [userID, password] = Buffer.from(b64auth, 'base64').toString().split(':');
const user = await User.findOne({ userID: userID });
if(!user) return res.status(400).send('User not found');
const validPass = await bcrypt.compare(password, user.password);
if(!validPass) return res.status(400).send('Incorrect Password');
//const token = generateToken(user.userID);
let payload = {
userID: user.userID
}
const token = generateToken(userID);
res.header('Authorization', 'Bearer ' + token).json(user);
return token;
}
I generate my token this way :
const generateToken = (_id) => {
console.log('Signing token for ID ', _id);
console.log('Secret key is ', process.env.JWT_KEY);
const token = jwt.sign({ _id}, process.env.JWT_KEY, {
expiresIn: "30d",
});
console.log('Signed token: ', token);
return token;
};
I try to store my token in my "userInfo" State .. but only username and password is displayed not token ..It works before .. but I don´t know why it´s not working anymore ^^ I´m completely at a loss
I hope someone sees the error
my Console gives me the detail:
TOKEN
undefined
You are expecting response.data to be an object. In that case update your API handler to return property token in an object:
exports.login = async (req,res) =>{
const b64auth = (req.headers.authorization || '').split(' ')[1] || '';
const [userID, password] = Buffer.from(b64auth, 'base64').toString().split(':');
const user = await User.findOne({ userID: userID });
if(!user) return res.status(400).send('User not found');
const validPass = await bcrypt.compare(password, user.password);
if(!validPass) return res.status(400).send('Incorrect Password');
//const token = generateToken(user.userID);
let payload = {
userID: user.userID
}
const token = generateToken(userID);
return res.header('Authorization', 'Bearer ' + token).json({ user, token });
}
Hopefully that helps!

error retrieving user Id token assigned by firebase in the client side

I am using JWT based authentication using firebase Admin SDK in express js.
according to the sign in with custom token when we sign the user with the function signInWithCustomToken(token) firebase sets a user-id token for that user.
according to retrieve id tokens
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
we can get the token if the user is logged in
but executing this I get error that getIdToken value is null.
i changed the code to
const getUser = async () => {
const token = await firebase.auth().currentUser.getIdToken(/* forceRefresh */true).catch(function(error) {
console.log(error)
});
const userToken = await token;
const getData = async (userToken) => {
const response = await fetch('/getUser', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({idToken: userToke})
})
const data = await response.json()
console.log(responnse)
}
}
getUser();
but still receiving the same error
I looked up for some solutions and found similar answers to the question one of which I implemented was solution
it used onAuthStateChanged method and I am using
<script src="https://www.gstatic.com/firebasejs/7.14.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.14.1/firebase-auth.js"></script>
in cdn but now am getting
Uncaught (in promise) TypeError: firebase.auth.onAuthStateChanged is not a function
at profile:40
at new Promise (<anonymous>)
at getIdTokenRefreshed (profile:37)
at profile:50
I changed the above code to this
firebase.initializeApp(firebaseConfig);
const getIdTokenRefreshed = async () => {
return new Promise(async (resolve, reject) => {
const unsubscribe = await firebase
.auth
.onAuthStateChanged(async user => {
unsubscribe()
const refreshedToken = await user
.getIdToken(true)
.catch(err => console.error(err))
resolve(refreshedToken)
console.log(refreshedToken)
}, reject)
});
}
getIdTokenRefreshed();
still getting the second error where onAuthStateChanged is not defined
how do I retrieve the user id token?
UPDATE
const getIdTokenRefreshed = async () => {
try {
const user = firebase.auth().currentUser
if (user) {
const token = await user.getIdToken(true)
console.log(`Token: ${token}`)
return token
} else {
console.log("No user is logged in")
}
} catch (e) {
console.log(`Something went wrong: ${e}`)
}
}
after implementing the above code this is the error
await is only valid in async functions and the top level bodies of modules
First, I'd recommend updating Firebase SDK to latest version which is 8.9.1 at the time of writing this.
<script src="https://www.gstatic.com/firebasejs/8.9.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.9.1/firebase-auth.js"></script>
If you take a look at onAuthStateChanged part in the documentation, it should be:
firebase.auth().onAuthStateChanged(...)
// ^^
// not firebase.auth.onAuthStateChanged
The onAuthStateChanged won't be triggered unless you call the getIdTokenRefreshed function. You can simply refactor that function to:
const getIdTokenRefreshed = async () => {
try {
const user = firebase.auth().currentUser
if (user) {
const token = await user.getIdToken(true)
console.log(`Token: ${token}`)
return token
} else {
console.log("No user is logged in")
}
} catch (e) {
console.log(`Something went wrong: ${e}`)
}
}
Lastly, the variable name is userToken but in request body it is body: JSON.stringify({idToken: userToke}) and you don't need an await before a variable name. Try refactoring the getUser function to:
//const token = await firebase.auth().currentUser.getIdToken(/* forceRefresh */true).catch(function(error) {
// console.log(error)
//});
//const userToken = await token;
const getUser = async () => {
const token = await firebase.auth().currentUser.getIdToken(true)
const response = await fetch('/getUser', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({idToken: token})
})
const data = await response.json()
console.log(data)
return data
}
getUser().then(data => {
console.log("Data received")
})

How do I correctly await the return of an async function in javascript?

I'm currently working on a Google Sign-in Auth app with a React frontend and an Express backend and I'm currently stuck in the part of the process where I'm validating tokens on the backend. The docs for this process show this code to validate the token:
const {OAuth2Client} = require('google-auth-library');
...
const client = new OAuth2Client(CLIENT_ID);
async function verify() {
const ticket = await client.verifyIdToken({
idToken: token,
audience: CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend
// Or, if multiple clients access the backend:
//[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
});
const payload = ticket.getPayload();
const userid = payload['sub'];
// If request specified a G Suite domain:
//const domain = payload['hd'];
}
verify().catch(console.error);
I've implemented this code in my own project here:
//verify token
async function verify(token, client) {
const ticket = await client.verifyIdToken({
idToken: token,
audience: keys.google.clientID,
});
const payload = ticket.getPayload();
const userid = payload['sub'];
const domain = payload['hd'];
const email = payload['email']
console.log('User ID: ' + userid);
console.log('Domian: ' + domain);
console.log('Email: ' + email);
var message = '';
var cookie = {};
await User.find({email: email}, (error, user) => {
if(error) {
message = error;
} else if (user.length === 0) {
message = 'this user is not in the database';
} else {
message = 'this user is in the database';
const session = new Session({
email: email,
session_token: token
});
cookie = {
email: email,
session_token: token
};
session.save((error, session) => {
if (error) {
console.log(error);
} else {
console.log('session saved');
}
});
console.log(message);
}
});
return Promise.resolve(cookie);
}
//recieve token id from frontend, verify it, and send session back in response
router.post('/google', (req, res) => {
const body = req.body.tokenID;
const client = new OAuth2Client(keys.google.clientID);
let cookie = verify(body, client).catch(console.error);
console.log('Cookie:' + cookie);
return res.send(cookie);
});
Currently when this runs everything inside the async function executes, but the return statement only returns the empty promise object. I think I'm making a mistake using async and await incorrectly, but I don't know how to correctly get the function to wait for all the work verifying the token and then update the DB before returning.
Not sure if this will help, but when I call the route my console gives me this output:
(I took out my personal info from the output fields, but assume these lines actually have gmail account info)
...
Cookie:[object Promise]
User ID: <GOOGLE ID>
Domian: <DOMAIN>
Email: <USER EMAIL>
this user is in the database
session saved
Thanks for reading!
Since "verify" function is an async function, you should add "await" before calling it. For catching errors you can simply place it in a try/catch:
router.post('/google', async (req, res) => {
const body = req.body.tokenID;
const client = new OAuth2Client(keys.google.clientID);
try {
let cookie = await verify(body, client);
console.log('Cookie:' + cookie);
return res.send(cookie);
} catch(error) {
// handling error
console.log(error);
return res.send("error")
}
});
`
You're mixing async/await with callback based calls. I don't know the internals of the library you're using, but the pattern should look more like this:
var cookie = {};
try{
const user = await User.find({email: email});
if (user.length === 0) {
console.log('this user is not in the database');
}
else {
console.log('this user is in the database');
const session = new Session({
email: email,
session_token: token
});
try{
await session.save();
console.log('session saved');
} catch(err){
console.log(err);
}
return {
email: email,
session_token: token
};
} catch(error){
console.log(error);
}

Fetch API response with react and Express.js won't show any result with console.log

I have a login form that sends data to an Express.js backend using fetch. On the client side, when I want to display the results of the fetch call when it completes nothing is displayed (and it never reaches the data callback). I don't seem to be getting any errors, but I know that the data is successfully being sent to the backend.
Here's the Express.js server code:
const express = require('express');
const User = express.Router();
const bcrypt = require('bcrypt');
const user = require('../Models/user');
this is edited
function loginRouteHandler(req, res) {
user.findOne(
{
where: {
userName: req.body.userName,
},
},
)
.then((data) => {
if (bcrypt.compareSync(req.body.password, data.password)) {
req.session.userName = req.body.userName;
req.session.password = req.body.password;
console.log(req.session);
res.status(200).send('Success!');
} else {
res.status(400).send('some text');
}
});
}
User.route('/').get(getRouteHandler);
User.route('/register').post(postRouteHandler);
User.route('/login').post(loginRouteHandler);
module.exports = User;
And here's the fetch call:
fetch('http://localhost:4000/login',{
method: 'POST',
headers: {
'Accept': 'application/json,text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify({
userName: this.state.userName,
password: this.state.password,
}),
}).then((response)=>{
if(response.ok){
console.log(response)
}
else {
console.log("a problem")
}
}).then((data)=>{
console.log(data)
});
In your loginRouteHandler, if the bcrypt compare succeeds nothing is returned in the response. So in the first branch of the if statement, put res.send('Success!') or something similar.
Here's an example:
function loginRouteHandler(req, res) {
user.findOne(
{
where: {
userName: req.body.userName,
},
},
)
.then((data) => {
if (bcrypt.compareSync(req.body.password, data.password)) {
req.session.userName = req.body.userName;
req.session.password = req.body.password;
console.log(req.session);
res.status(200).send('Success!');
} else {
res.status(400).send('some text');
}
});
}
UPDATE: you're also not getting the output of the fetch response with .text() or .json(). You have to update the fetch call to the following:
fetch(/* stuff */).then((response)=>{
if(response.ok){
console.log(response)
}
else {
console.log("a problem")
}
return response.text()
}).then((data)=>{
console.log(data)
});
Remove ok from response.ok
Remove .then((data)=>{ console.log(data) });
And check console log.
}).then((response)=>{
if(response){
console.log(response)
}
else {
console.log("a problem")
}
}).then((data)=>{
console.log(data)
});

Categories

Resources