Why am I getting token as undefined when using middleware? - javascript

I am using nodejs and express to develop a small application which uses android for the front-end. I wanted to implement session tokens for which I am using Json-Web Tokens.
I have a separate route defined for the middleware to check if user is authorised.
router.use(function(req,res,next)
{
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['xtoken'];
if (token)
{
// verifies secret and checks exp
jwt.verify(token,"SECRET-KEY", function(err, decoded)
{
if (err)
{
return res.json({ success: false, message: 'Failed to authenticate token.' });
}
else
{
console.log("Successful token is ",decoded.token)
req.decoded = decoded;
next();
}
});
}
else
{
return res.status(403).send(
{
success: false
});
}
});
I need a separate route to read a form which consists of some data and multiple files. The form details are posted to /user/new-order, a request is made within the above route separately to a new route /user/file/upload where each file is uploaded one-by-one
Here is the problem.
I want to read the session token on every route. When I first make a request to POST some form data, the middleware reads the session token and accepts the form fields BUT as it goes to the route to upload a single file, the token is undefined and I am prevented from accessing the page.
I have tried logging the values of the session token in the route /user/file/upload AND while the middleware stores the session token as undefined, The route user/file/upload is able to read the token.
The Android front-end uses a single screen to accept form and file upload.
The user/new-order route is as below :
router.post('/new-order',function(req,res)
{
var subject = req.body.subject;
var Format = req.body.Format; //Reference FORMAT or FORMATS ?
var email = req.email
var token = req.body.token
console.log("Form token is ",token)
});
The user/upload/file route is below :
router.post('/upload/file',uploadtype,function(req,res)
{
var file = req.file
var token = req.body.token
console.log("File token is ",token)
});
The middleware to read session data is above the assignment routes.
app.use('/user',session)
app.use('/user',assignment)
This is the output :
Successful token is : token_value
Form token is : token_value
Successful token is : undefined
File token is : token_value
I am not sure what I am doing wrong, I would really appreciate some tips and pointers.
Please let me know if what I have provided is not enough, I will add it immediately.
Much appreciated.

Related

Validating JWT Token in vue.js Router

I am using the following code to generate a JWT token:
jwt.sign(id, TOKEN_SECRET, { expiresIn: '24h' });
Once generated, I send the token to the client, which stores it within a cookie:
document.cookie = `session=${token}` + ';' + expires + ';path=/'
Furthermore, I am using vue.js Router for my navigation. From my understanding, if one adds the following code in the router file, one can insert middle-ware in order to protect some routes.
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth) {
let token = Vue.cookie.get('session')
if (token == null) {
next({
path: '/',
params: { nextUrl: to.fullPath }
})
}
} else {
next()
}
})
However, I am having difficulty understanding how can one verify the validity of the JWT token using this approach, which needs to be done on the server, where the TOKEN_SECRET is stored, and not on the client side.
Let me start with this: your goal in guarding routes is to prevent the user from having a bad experience by proceeding to a page that will attempt to retrieve information that they are not authorized to view.
So, you don't need to validate the token on the client side. Since a token will only be in hand if the server validated the user and returned a token, you - the author of the client code - can use the presence of the token as a means to inform what route to take the user through.
In other words, the client having a token is all the validation you need to allow the user through to protected routes.
Remember, it is not as though a protected page has private data in and of itself. A protected page will always retrieve that protected data from the server, which means that the server has the chance to authenticate the token after all.

Token Authentication - JWT

When we use jsonwebtoken in Node, we sign a particular token for the user and send it back. However, when we verify the token when the user sends it in the header (Authentication: <token>), how does the jwt know that that token which it is verifying is for that particular user and not for some other user who also sent a request at the same time? Does it store the token somewhere internally?
At the time of login, you sign a token where payload is the userId, which is nothing but the _id field in the queried user object.
loginUser: async (req, res, next) => {
try {
const { email, password } = req.body
const user = await User.findOne({ email })
const token = auth.signToken({ userId: user._id })
res.status(200).json({ user, token })
} catch (error) {
return next(error)
}
}
auth.js
function signToken(payload) {
return jwt.sign(payload, JWTSECRET)
}
function verifyToken(req, res, next) {
const token = req.headers.Authorization || req.headers.authorization || ""
if (!token) {
return res.status(403).json({ error: "Not authorized" })
}
jwt.verify(token,JWTSECRET, (err, decodedObj) => {
if (err) {
return res.status(403).json({ error: "Not authorized" })
}
req.user = decodedObj
next()
})
}
module.exports = { signToken, verifyToken }
In the callback of jwt.verify, you get a decodedObj which is like:
{ userId: '5edb3ae6d6b129183c1393bc', iat: 1591425786 }
where iat is the time at which the jwt was issued.
req.user = decodedObj
Here, we're "attaching" the decoded data to the user object so that when a request is made to the protected route, we can get the userId from the request object, like req.user.userId, and then query it from the database.
When you sign a token, you provide a payload which can be a userId and a secret. So, the token gets signed. After that, you need to verify the token in case you're trying to access some protected page that requires a token.
So, when you send a request to that protected route, like this:
router.get("/me", auth.verifyToken, usersController.identifyUser)
where identifyUser is a controller function that just identifies the logged in user by checking the userId(remember the user object contains the decoded object data).
how does the jwt know that that token which it is verifying is for that particular user and not for some other user who also sent a request at the same time? Does it store the token somewhere internally?
It's because of the payload that you give, which is unique to the user.
The Authentication token is stored in an Authentication Server, so when you send the Authentication token in your request header, the Authentication Server authenticated your client.
After being authenticated by Authentication Server, the client can now pass JWT to make API calls to the Application Server. Since client is allowed to make API calls, Application Server can verify the JWT token the client has sent and can process the API call.
Note that for making API calls, the client has to send a Authorization: Bearer <token> for each API call, which is stored at the server (aka Authorization Server)
the token is most store in the client
when the token verifying successfully, we will get some user info, etc account id, so we can use account id to find more user info in the database, and check the use is really exist
maybe it is useful for you?
You will typically sign the token with the user id when sending it from the server. So when the client then sends back that token you decode it and it will return the id to you. Which you then use to find the user in the data base

Session object storing JWT + handling refresh tokens in node.js backend

I am building my application using Angular and node.js (express) and I am using JWT for authentication and authorization. Currently, I am storing JWT in session object on server in memory (req.session.jwt = user.generateToken()) and also, I am sending refresh token in HttpOnly cookie to the client.
When JWT expires, I want server to give me new JWT, if refresh token from cookie is equal to the refresh token in database - asociated with user.
I tried to implement refresh token logic in my auth middleware in the catch block, but it did not work.
(If token validation failed, then compare RF token from cookie to RF in database, if true, set new JWT and run the routehandler.)
module.exports = function(req, res, next) {
const token = req.session.jwt;
if (!token) return res.status(401).send("Access denied.");
try {
const decoded = jwt.verify(token, config.get("jwtPrivateKey"));
req.user = decoded;
next();
} catch (ex) {
(async () => {
let user = await User.findOne({ username: req.session.username });
if (!user) return res.status(500).send("Invalid session");
if (user.refreshToken === req.signedCookies.refreshToken) {
req.session.jwt = user.generateToken();
next();
} else {
return res.status(401).send("Invalid session");
}
})();
}
};
I had implemented this refresh token logic, when I was storing token and refresh token in localstorage on client, but this was not great implementation, so I tried to make it like this.
I had token.interceptor in my Angular part of the project - from this tutorial (https://angular-academy.com/angular-jwt/#refresh-token)
Here is my github repo of the backend https://github.com/TenPetr/dashboard_backend
If you have any idea, how to implement this logic, please let me know.
Thanks for your advices.
When you generate a new JWT token, as the request is going to continue to next middleware step, probably you will need to set the user, same as you do when the jwt is correct.
Something like this, try it, and let me know if it helps:
if (user.refreshToken === req.signedCookies.refreshToken) {
req.session.jwt = user.generateToken();
const decoded = jwt.verify(req.session.jwt, config.get("jwtPrivateKey"));
req.user = decoded;
next();
}

How to redirect user to a page that requires an access token without exposing token in a URL query

I am trying to implement a way of only allowing authorized admins to access pages with sensitive information within my Web App/API. I am currently using Node.JS with Express to handle routing. Views are generated with Jade/Pug View Engine. CORS requests are handled using the request framework on github. (https://github.com/request/request)
Currently, clients are stored within a MongoDB collection. When they enter their login information, their passwords are compared to a Hash stored in the DB. If everything checks out, a JWT is generated for the user and saved in the client DB and also stored within the browser in localStorage.token.
The user is then redirected to a splash page which has an authentication function as middleware. The authentication function accepts the token in three forms:
var token = req.body.token || req.query.token || req.headers['x-access-token'];
I feel like the way redirection is handled is a bit hacky. I am using window.location = route?token=[token]
Token is handed over to the authentication function within req.query.token, but this is exposed in URL. Here is my login function:
function submitLogIn() {
var credentials = {
email: logInForm.userEmail.value,
password: logInForm.pwField.value
}
fetch('/login', {
headers:{
'Content-Type': 'application/json'
},
method: 'POST',
body: JSON.stringify(credentials)
}).then(function(res){
if(!res.ok) alert("Error!")
else return res.json().then(function(result){
localStorage.token = result.token;
//This is how I'm doing redirecting:
window.location = '/selection?token=' + result.token;
}).catch(function(err){
console.log(err);
throw err;
});
});
Just for reference, this is the front-end route that it's going to at port 3001 which swings it over to 3000 as a reverse-proxy request:
router.post('/login', function(req, res, next) {
request.post({
url: 'http://localhost:3000/authorize',
form: req.body
}).pipe(res)
});
My main question is this: How can I handle redirection at the front-end within fetch calls?
My splash screen is basically a menu of buttons which will take the user to different pages. The pages will contain information that only admins should see. Let's say I want to click on this navigation button. It goes to a /GET route that requires the token to be sent for an OK status. if no token, it returns a 403.
router.get('/main', authorize.adminRequired, function(req, res, next) {
request.get({
url: 'http://localhost:3000/menus'
}, function(response, sensitiveItems) {
return res.render('index', {menu: JSON.parse(sensitiveItems)});
});
});
That authorize.adminRequired function needs a token in the form of a query, x-access-token, or req.body. Can't send a body in a GET request. Don't want to expose in URL, so:
Here's what I tried. I have this redirect fetch call as an onclick function.
function redirectToSensitivePage() {
fetch('/main', {
headers: {
'x-access-token': localStorage.token
},
method: 'GET'
});
};
This gives me a 200, OK. But I don't load the page. The res.render(index) in the route does not fire. The response does contain all of the data, but I am not redirected to the visual representation.
I could do the same hacky redirect using window.location=/main?token=[token], but I don't want to expose the URL token. I could load everything I need into the webpage upon login instead of having separate pages, have those divs hidden out of sight until keypress to make that div the main visible div, but I would rather figure out how to do it via redirect.
I'm still getting my bearings with using Node, so please let me know if I have made any stupid glaring oversights.
Edit: Should I simply try
var token = localStorage.token || req.body.token || req.query.token || req.headers['x-access-token'];
Is there any disadvantage to simply scraping from the localStorage automatically with each request?

How to verify google auth token at server side in node js?

My front end application is authenticated using gmail account.
I retrieve id_token after the authentication is successful and send it as Authorization Header as bearer token.
E.g.
http://localhost:4000/api
Authorization Bearer token_id
At nodejs server side, I call the following method to verify the token.
exports.verifyUser = function(req, res, next) {
var GoogleAuth = require('google-auth-library');
var auth = new GoogleAuth();
var client = new auth.OAuth2(config.passport.google.clientID, config.passport.google.clientSecret, config.passport.google.callbackURL);
// check header or url parameters or post parameters for token
var token = "";
var tokenHeader = req.headers["authorization"];
var items = tokenHeader.split(/[ ]+/);
if (items.length > 1 && items[0].trim().toLowerCase() == "bearer") {
token = items[1];
}
if (token) {
var verifyToken = new Promise(function(resolve, reject) {
client.verifyIdToken(
token,
config.passport.google.clientID,
function(e, login) {
console.log(e);
if (login) {
var payload = login.getPayload();
var googleId = payload['sub'];
resolve(googleId);
next();
} else {
reject("invalid token");
}
}
)
}).then(function(googleId) {
res.send(googleId);
}).catch(function(err) {
res.send(err);
})
} else {
res.send("Please pass token");
}
}
When I call the above method, I always get Invalid token response with following error.
Error: No pem found for envelope: {"alg":"RS256","kid":"c1ab5857066442ea01a01601
850770676460a712"}
at OAuth2Client.verifySignedJwtWithCerts (\node_modules\google-auth-libr
ary\lib\auth\oauth2client.js:518:13)
Is this the right approach to verify token?
Do I send the id_token as Authorization bearer? Or is it for authorization only?
How do I send the id_token to the sever side? Thru url, header?
What am I doing wrong?
Any help is highly appreciated.
OAuth2Client.verifyIdToken take an idToken in arguments, from the library source :
/**
* Verify id token is token by checking the certs and audience
* #param {string} idToken ID Token.
* #param {(string|Array.<string>)} audience The audience to verify against the ID Token
* #param {function=} callback Callback supplying GoogleLogin if successful
*/
OAuth2Client.prototype.verifyIdToken = function(idToken, audience, callback)
You have passed the whole header value bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxYWI1OD U3MDY2NDQyZWEwMWEwMTYwMTg1MDc3MDY3NjQ2MGE3MTIifQ so you will have to split the headers value as :
var authorization = req.headers["authorization"];
var items = authorization.split(/[ ]+/);
if (items.length > 1 && items[0].trim() == "Bearer") {
var token = items[1];
console.log(token);
// verify token
}
Is this the right approach to verify token ?
Yes, this is the right way to verify token. For debugging, you can also verify token with the tokeninfo endpoint if you have any doubt or for quick testing :
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123
Do I send the id_token as Authorization bearer? Or is it for
authorization only?
How do I send the id_token to the sever side? Thru
url, header?
You can send JWT token in Authorization header but it could lead to usecase where you have multiple Authorization headers. It's best to URL encode or embed the token in the body. You can check Google example here
Moreover, the following are required by Google :
the token must be sent via HTTPS POST
the token integrity must be verified
To optimize your code, you could also move your Google auth object to your app.js at the root of your app instead of redefining it each time the token should be verified. In app.js :
var app = express();
var GoogleAuth = require('google-auth-library');
var auth = new GoogleAuth();
app.authClient = new auth.OAuth2(config.passport.google.clientID, config.passport.google.clientSecret, config.passport.google.callbackURL);
and in verifyUser call it from req.app.authClient :
req.app.authClient.verifyIdToken(...)
I finally found the answer today.
The Firebase tool will connect the native Google to the third-party login token, and then encapsulate another layer. The token obtained at this time is no longer the original token given to us by Google.
A1:
Original Token: GoogleDesignInAccount Account = Task.getResult(ApiException.class);
Account.getidToken () // This is the original token
B1:
Firebase token: FireBaseUser currentUser = Mauth.getCurrentUser ();
String token = currentUser.getIdToken(false).getResult().getToken();
A2:
Google officially provides a method to verify the token
B2:
Firebase officially provides the authentication token method
We use code names for the four data points above. If you need to verify the validity of tokens in the background, they must correspond to each other, A1 to A2 and B1 to B2. If you use A2 to validate the B1, it will fail
First of all, do not use Id_Token for authorization. It is only for authentication. Use access token for authorization. Use link below to verify token.
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=${access_token}

Categories

Resources