cant retrieve token from headers node - node.js

I'm having trouble getting the token once the user has logged in. I am supposed to get the token that is generated when I login through req.header so that when I ask for a route in the api (thunder api client) it should be able to give me access
My auth middleware:
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
const token = req.headers['auth-token']
console.log(token)
res.json({
token: token
})
}
My routes:
const { Router } = require('express');
const router = Router();
const {
getAllUsers,
signupUser,
signinUser
} = require('../controllers/user.controller');
const auth = require('../middleware/auth');
router.get('/users', auth, getAllUsers);
router.post('/users/signup', signupUser);
router.post('/users/signin', signinUser);
module.exports = router;
when I user users/signin the response header:

Well well well, Apparently my problem was the following:
When I consumed by (thunder client) the route /users/signin -> it returned the object with the token. What I don't realize is that when I request the route that I want to be restricted to logged in users (only with the token) I must pass the same token that I get back from the signin in the headers of the new request (XD !) as seen in the photo.

Related

Authentication using Node.js OauthClient "auth-code" flow

I'm building a SaaS application which require read-access to a user's google calendar. After the user gives consent to access their calendar during the first sign-in, I want the application to be able to authorize itself to access any of the user's calendars at any time without having to prompt the user for authorization again.
Currently I'm trying to create an authentication flow following the '#react-oauth/google' node library (specifically the "authorization code flow" here: https://react-oauth.vercel.app/). In my frontend, I get a code from the user, which is sent and successfully received by my backend. The backend is then supposed to use the code to get an access token and a refresh token for that user, but the request to exchange the code for the access token (oAuth2Client.getToken(req.body.code);) is failing with error 401 (unauthorized client.)
My ultimate goal is to store the access token and refresh token in a database somewhere so that I can access that user's calendar later at any time.
If I treat the backend as an Oath Client on google cloud and pass in the credentials for that, I get error 401 - unauthorized client, but I've given it access to the calendar api on google console, as you can see in the image:
How can I resolve the issue that I'm facing?
I started reading about service accounts that can do this for you but I'm unsure how to proceed. I saw that they can do domain wide delegation but my users will be signing in from their personal gmail accounts so that option is not applicable for me.
Frontend Code:
import { useGoogleLogin } from '#react-oauth/google';
import axios from 'axios';
export const LoginModal = () => {
const googleLogin = useGoogleLogin({
flow: "auth-code",
onSuccess: async codeResponse => {
console.log(codeResponse);
const tokens = await axios.post("http://localhost:3001/auth/google/", {
code: codeResponse.code
});
console.log(tokens);
}
})
return (<>
...some html code
<button onClick={() => { googleLogin() }}>
..some more html code
</>)
}
Backend Code:
require('dotenv').config();
const express = require('express');
const {
OAuth2Client,
} = require('google-auth-library');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
const CLIENT_ID = "xxx";
const CLIENT_SECRET = "xxx";
// initialize oathclient
const oAuth2Client = new OAuth2Client(
CLIENT_ID,
CLIENT_SECRET,
'postmessage',
);
// get token from code given from frontend
app.post('/auth/google', async (req, res) => {
console.log(req.body.code)
const { tokens } = await oAuth2Client.getToken(req.body.code); // exchange code for tokens
res.json(tokens);
});
app.post('/auth/google/refresh-token', async (req, res) => {
const user = new UserRefreshClient(
CLIENT_ID,
CLIENT_SECRET,
req.body.refreshToken,
);
const { credentials } = await user.refreshAccessToken(); // obtain new tokens
res.json(credentials);
})
app.listen(3001, () => {
console.log(`server is running`)
});
I figured it out. Basically, I was putting in the wrong clientid/client secret in my google console because I thought the frontend and backend needed different oauth client IDs. I used the same oauth client secret/id for frontend and backend and made sure to follow the answers here:
https://github.com/MomenSherif/react-oauth/issues/12
MAKE SURE TO PUT "postmessage" AS YOUR REDIRECT_URI! It will not work without that.
Working code:
frontend is the same
backend:
require('dotenv').config();
const express = require('express');
const {
OAuth2Client,
} = require('google-auth-library');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
const CLIENT_ID = XXX
const CLIENT_SECRET = XXX
// initialize oathclient
const oAuth2Client = new OAuth2Client(
CLIENT_ID,
CLIENT_SECRET,
'postmessage',
);
// get token from code given from frontend
app.post('/auth/google', async (req, res) => {
console.log("got request!")
console.log(req.body.code)
const { tokens } = await oAuth2Client.getToken(req.body.code); // exchange code for token
res.json(tokens);
});
app.listen(3001, () => {
console.log(`server is running`)
});

Spotify API Authorization redirects too many times

I'm trying to use the Spotify API and following their instructions on authorization found here: https://github.com/spotify/web-api-auth-examples/blob/master/authorization_code/app.js.
Their version of the Authorization code directly uses routes in the server code, but I wanted to separate the Authorization into its own route. Here is my version of the code:
const authRouter = require("express").Router();
const config = require("../utils/config");
const request = require("request");
const querystring = require("querystring");
// Spotify client configurations
const client_id = config.CLIENT_ID;
const client_secret = config.CLIENT_SECRET;
const redirect_uri = config.REDIRECT_URI;
const stateKey = "spotify_auth_state";
//...
// #route GET /
// #desc Prompt User to Login into Spotify
authRouter.get("/", async (req, res) => {
try {
var state = generateRandomString(16);
res.cookie(stateKey, state);
// Request for user full name, profile image, and email address.
var scope = "user-read-private user-read-email";
// 1. Get the user's authorization to access data.
res.redirect(
"https://accounts.spotify.com/authorize?" +
querystring.stringify({
response_type: "code",
client_id: client_id,
scope: scope,
redirect_uri: redirect_uri,
state: state,
})
);
} catch {
console.log("error.");
}
});
// #route GET /callback
// #desc Spotify callback to request access and refresh tokens
authRouter.get("/callback", async (req, res) => {
try {
var code = req.query.code || null; // The authorization code returned by the first call.
var state = req.query.state || null;
var storedState = req.cookies ? req.cookies[stateKey] : null;
// Check the state parameter
if (state === null || state !== storedState) {
res.redirect(
"/#" +
querystring.stringify({
error: "state_mismatch",
})
);
} else {
res.clearCookie(stateKey);
const authOptions = getAuthOptions(
code,
redirect_uri,
client_id,
client_secret
);
// 2. Request an access token and refresh token
request.post(authOptions, function (error, response, body) {
if (!error && response.statusCode === 200) {
// Authorize successful. Access and Refresh Tokens granted.
var access_token = body.access_token,
refresh_token = body.refresh_token;
// Send the tokens to the client so the client can use them in requests to the Spotify API.
res.redirect(
"/#" +
querystring.stringify({
access_token: access_token,
refresh_token: refresh_token,
})
);
} else {
res.redirect(
"/#" +
querystring.stringify({
error: "invalid_token",
})
);
}
});
}
} catch {
console.log("error.");
}
});
module.exports = authRouter;
And in my app.js:
const express = require("express");
const authRouter = require("./controllers/auth");
const cors = require("cors");
var cookieParser = require("cookie-parser");
// initialize app with Express to create client to communicate with Spotify
const app = express();
app.use(cors());
app.use(cookieParser());
app.use("/", authRouter);
module.exports = app;
Now when I start my server, my browser returns: "accounts.spotify.com redirected you too many times.". When I tried starting my server in incognito mode, the Spotify login prompt appears. After I enter my credentials, it returns: "accounts.spotify.com redirected you too many times."
I've tried clearing my cookies and caches but that does not work.
In addition, I've confirmed my redirect URI for my server is the same as my redirect URI in my Spotify application's settings.
What can be the reasons the auth seems to be stuck in an infinite loop?
What's causing the infinite loop is where the code sends the access and refresh tokens back to the client:
// Send the tokens to the client so the client can use them in requests to the Spotify API.
res.redirect(
"/#" +
querystring.stringify({
access_token: access_token,
refresh_token: refresh_token,
})
);
Since I have defined the following route:
authRouter.get("/", async (req, res) => {
The access and refresh tokens are redirected to the login page, which will then lead to the callback which redirects to the login again, creating an infinite loop.
How I solved this was to redirect the access and refresh tokens to a different component, not just to "/#" + query.string... as coded in Spotify's example code.
Spotify's example code does not lead to an infinite loop since they defined a /login route for the login page, but I opted my website's root to be the login page since in my case, authenticating should be the first step.

Node JS API Authentication

I'm new to NodeJS and developing an API using it,
I want the API to be authenticated with an API token method (only people with a token stored in DB, created through a specific encryption should be able to access the API resource.)
I'm using an SQL server, NodeJS, and the Express framework.
Please guide me in what I should use to authenticate the API request.
Thanks in advance.
You could use passport.js with JwtStrategy. This is the idea:
mypassport.js
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const opts = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'yourSecret'
};
passport.use(new JwtStrategy(opts, (payload, done) => {
const user = findUserById(payload.id);
if (!user) {
return done('user not exists', null);
}
return done(null, user);
}));
server.js (using express)
require('./mypassport'); // <- initialize passport strategies
//you could also use passport with local strategy for this
app.post('login', (req, res) => {
const username = req.query.username;
const password = req.query.password;
if (validLogin(username, password)) {
const user = findUserByUsername(username);
const jwt = createTokenWithSecret(user, 'yourSecret'); // You can use jwt-simple for this
res.json({ token: jwt });
} else {
//send unauthorized
}
});
const requireLogin = passport.authenticate('jwt');
app.get('/something', requireLogin, (req, res) => {
//here, user is authenticated and available in 'req.user'
});
First, you must login with POST /login { username: 'john', password: '1234' }. That will return a JSON with the jwt token like this:
{ token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' }
In subsequent requests, you must send a header Authorization with value: Bearer {token} so passportjs and JwtStrategy can authorize the request.
Hope it helps!
NOTE: I have not tested code above, it just shows the approach.
For API Authenication use Passport JS
You can use json web token (jwt) for API authorization. There are node modules available which provides authentication functionality and you can simply use.
Please have a look at this article: https://medium.freecodecamp.org/securing-node-js-restful-apis-with-json-web-tokens-9f811a92bb52?gi=89f3f4d89dfd

Express & Firebase - Failing to set header before redirect

I am trying to make Firebase authentication work on the server.
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser')();
const cors = require('cors')({origin: true});
//const expressSanitizer = require('express-sanitizer');
const app = express();
// Express middleware that validates Firebase ID Tokens passed in the Authorization HTTP header.
// The Firebase ID token needs to be passed as a Bearer token in the Authorization HTTP header like this:
// `Authorization: Bearer <Firebase ID Token>`.
// when decoded successfully, the ID Token content will be added as `req.user`.
const validateFirebaseIdToken = (req, res, next) => {
console.log('Check if request is authorized with Firebase ID token');
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!(req.cookies && req.cookies.__session)) {
console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.',
'Make sure you authorize your request by providing the following HTTP header:',
'Authorization: Bearer <Firebase ID Token>',
'or by passing a "__session" cookie.');
res.redirect("/login");
return;
}
let idToken;
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1];
} else if(req.cookies) {
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
idToken = req.cookies.__session;
} else {
// No cookie
res.redirect("/login");
return;
}
admin.auth().verifyIdToken(idToken).then((decodedIdToken) => {
console.log('ID Token correctly decoded', decodedIdToken);
req.user = decodedIdToken;
return next();
}).catch((error) => {
console.error('Error while verifying Firebase ID token:', error);
res.redirect("/login");
});
};
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("/public"));
app.use(cors);
app.use(cookieParser);
//app.use(expressSanitizer());
//app.use(validateFirebaseIdToken);=
app.set("view engine", "ejs");
// This HTTPS endpoint can only be accessed by your Firebase Users.
// Requests need to be authorized by providing an `Authorization` HTTP header
// with value `Bearer <Firebase ID Token>`.
exports.app = functions.https.onRequest(app);
app.post("/login", (request, response) => {
var idToken = request.body.token;
console.log("REQUEST BODY = " + idToken);
response.header("Authorization" , "Bearer " + idToken);
return response.redirect("dashboard");
});
app.get("/dashboard", validateFirebaseIdToken, (request, response) => {
response.redirect("/dashboard/new");
});
In the /login POST route, I am receiving the idToken as expected (and showed in the logs). It seems though, that the response is unable to preserve/maintain the header property Authentication: Bearer <Firebase ID token> set beforehand.
In fact, I sent a GET request in Postman to /dashboard by getting the idToken printed by the logs and setting it in the header of the request like Authorization: Bearer <idToken> and it worked perfectly.
Here it says that redirects are in fact new HTTPS requests and therefore don't preserve the header set in the response. What should I do in this case?
You have to send the Authorization header with every request. HTTPS functions are stateless. They don't remember anything from a prior request. So, you shouldn't depend on redirect behavior to retain state. Instead, the client needs to figure out where to go next and make the next request itself.

How to set authorization headers with nodejs and express

I am setting up a site with nodejs, express, mongoose and swig template following this tutorial :
Authenticate a Node.js API with JSON Web Tokens
In this tutorial the author uses Postman to set the token in the header.
I have googled for days to find out how I can set the jwt token in the header of my site, but it is not working for me.
If you want the client to include the token in it's request headers, you can use a cookie parser with express. (HTML5 Web Storage is another option). About Cookies:
Express can set the response headers to tell the client "add the token to a cookie".
Once the client sets the cookie with the token, the token will be in the client's request headers for each request. Let's get to baking with a little
npm install cookie-parser
Sprinkle on some
var cookieParser = require('cookie-parser')
app.use(cookieParser())
Access and set a cookie:
app.use(function (req, res, next) {
var cookie = req.cookies.jwtToken;
if (!cookie) {
res.cookie('jwtToken', theJwtTokenValue, { maxAge: 900000, httpOnly: true });
} else {
console.log('let's check that this is a valid cookie');
// send cookie along to the validation functions...
}
next();
});
You will probably want to do these things with the cookies (or whatever method you go with in the end):
set the cookie to hold the token when a user is authenticated.
check the cookie header value before allowing access to protected
routes.
send back an unauthorized status if a user doesn't have their token
when they are trying to access api routes that require a token.
May help someone in future...
Storing token in cookie with httpOnly:true flag is pretty secure from XSS attack but it can be vulnerable to CSRF attack.
Adding custom request headers for all routes in express using a middleware might be a feasible solution like that:
var token;
//asign 'accessToken' to 'token' in app.post('/login')
token=accessToken;
app.all('*' , (req, res, next) => {
if (!token) {
console.log('token: undefined');
} else {
req.headers.authorization = 'Bearer ' + token;
}
next();
});
this will add authorization=Bearer <token> header in each and every get request coming from browser. Now verify token in each secure route by adding this middleware:
let in app.get('/dashboard')
const authenticateToken=(req, res, next)=>{
var authHeader=req.headers['authorization'];
var token=authHeader && authHeader.split(' ')[1];
if(token==null){
return res.sendStatus(401);
}
jwt.verify(token, process.env.JWT_ACCESS_TOKEN, (err, user)=>{
if(err){
return res.sendStatus(403);
}
req.user=user;
next();
})
}
//in app.get(...)
app.get('/dashboard', authenticateToken ,()=>{
//stuff for authorized user
})
In case if you defined app.post('/login') in another file then,
export addHeader middleware as under:
//var to access token outside app.post('/login') route
var token;
app.post('/login' , (req , res)=>{
//authenticate the user
//create token
const accessToken=jwt.sign(user, secretKey);
//assign 'accessToken' to 'token' var
token=accessToken
//redirect to secure route
res.redirect('dashboard');
}
//middleware to add in your 'index.js' or 'app.js' file.
//export it only if you define app.post('/login') in another file
exports.addHeader = (req, res, next) => {
if (!token) {
console.log('token: undefined');
} else {
req.headers.authorization = 'Bearer ' + token;
}
next();
}
In index.js or app.js
//import file in which app.post('/login') is defined. let it is defined in controller/auth
const authController=require('./controller/auth');
//to add custom header in all routes
app.all('*', authController.addHeader);

Resources