ExpressGraphql JWT authentication, not working - node.js

For the past 2 weeks, I'm working on this solution but no success. Can anyone suggest to me where I'm going wrong? For authentication, I'm using express-graphql, express-jwt for authentication [backend-[node, express-graphql, express-jwt, graphql-tools], frontend-[React-hooks,graphql-hooks]]. Following I'm using for authentication
const authMiddleware = jwt({
secret: app.get("getsecretval"),
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring(req) {
if (
req.headers.authorization &&
req.headers.authorization.split(" ")[0] === "Bearer"
) {
return req.headers.authorization.split(" ")[1];
} else if (req.query && req.query.token) {
return req.query.token;
}
return null;
}
});
app.use(authMiddleware);
app.use(
"/graphqlAPIRoute",
bodyParser.json(),
authMiddleware,
ExpressGraphQLHTTP(req => ({
schema: Schema,
rootValue: global,
context: {
user: req.user
}
}))
);
// Schema - place above authMiddleware
This even works when authorization headers not present,i.e., in case if the app idle in logged-in state as the token is stored in local storage, and not passed in headers yet the server code executes and fetches the data. Which must not be the case and must throw authentication error. If I add jwt verify we are not able to log in as there are no headers.
I suppose the auth middleware is not working and where do I place the jwt-verify function as to verify the token. For Jwt verify token I'm using
const jwtverify = require('jsonwebtoken');
Coz in express-jwt I've found no such functionality
Can anyone please lemme know where I'm going wrong? Any help would be appreciated.
Would this not work?

index.js - code sequence matters
const authMiddleware = jwt({
secret: "place secret here either pass as env",
credentialsRequired: false,
)}
app.use(authMiddleware);
const context = async (req) => {
const { authorization: token } = req.headers;
return { token };
};
app.use(
"/graphqlAPIRoute",
bodyParser.json(),
authMiddleware,
ExpressGraphQLHTTP(req => ({
schema: Schema,
rootValue: global,
}))
context: () => context(req),
);

Related

UnauthorizedError: invalid algorithm - Node.js express-jwt

I', trying to authenticate a user, I can create a user and get the bearer but after that I added this code to authenticate and it keeps showing the following error once every few seconds:
UnauthorizedError: invalid algorithm
at /mnt/c/Projects/myProject/node_modules/express-jwt/lib/index.js:105:22
at /mnt/c/Projects/myProject/node_modules/jsonwebtoken/verify.js:121:14
at getSecret (/mnt/c/Projects/myProject/node_modules/jsonwebtoken/verify.js:90:14)
at Object.module.exports [as verify] (/mnt/c/Projects/myProject/node_modules/jsonwebtoken/verify.js:94:10)
at verifyToken (/mnt/c/Projects/myProject/node_modules/express-jwt/lib/index.js:103:13)
at fn (/mnt/c/Projects/myProject/node_modules/async/lib/async.js:746:34)
at /mnt/c/Projects/myProject/node_modules/async/lib/async.js:1213:16
at /mnt/c/Projects/myProject/node_modules/async/lib/async.js:166:37
at /mnt/c/Projects/myProject/node_modules/async/lib/async.js:706:43
at /mnt/c/Projects/myProject/node_modules/async/lib/async.js:167:37
The code:
const express = require("express");
const { ApolloServer } = require("apollo-server-express");
const jwt = require("express-jwt");
const typeDefs = require("./settings/schema");
const resolvers = require("./settings/resolvers");
const JWT_SECRET = require("./settings/constants");
const app = express();
const auth = jwt({
secret: JWT_SECRET,
credentialsRequired: false,
algorithms: ['RS256'],
});
app.use(auth);
const server = new ApolloServer({
typeDefs,
resolvers,
playground: {
endpoint: "/graphql",
},
context: ({ req }) => {
const user = req.headers.user
? JSON.parse(req.headers.user)
: req.user
? req.user
: null;
return { user };
},
});
server.applyMiddleware({ app });
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log("The server started on port " + PORT);
});
Can't figure out why 'RS256' is not a valid algorithm, should I need to require something else? Do I need different algorithms for different tasks?
constants.js contains the following:
const JWT_SECRET = "sdlkfoish23##$dfdsknj23SD";
module.exports = JWT_SECRET;
Thanks
EDIT:
I'm not using Auth0, OAuth or any other service, I want to authenticate users by my own here
I'm registering a key when a new user is added to the DB (postgres) through the GraphQL API:
mutation {
register(login: "john", password: "doe")
}
answers with:
{
"data": {
"register": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NiwibG9naW4iOiJqb2VsIiwiaWF0IjoxNjE0NDM0NzMwLCJleHAiOjE2MTQ0MzQ5MTB9.ALltmClvlzxDJJ2FgZcFzstDUP5CY1xRzs8yQwheEn8"
}
}
then I use this bearer like that:
// Headers
{
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NiwibG9naW4iOiJqb2VsIiwiaWF0IjoxNjE0NDM0NzMwLCJleHAiOjE2MTQ0MzQ5MTB9.ALltmClvlzxDJJ2FgZcFzstDUP5CY1xRzs8yQwheEn8"
}
// Query
query {
current {
id,
login
}
}
I'm receiving this answer (also don't know why):
{
"error": "Unexpected token < in JSON at position 0"
}
And the error at the top of this post on the terminal
For a bearer token with JWT_SECRET, use the HS256 algorithm. The RSA256 algorithm requires a public key and private key pair.
The following code snippet works:
const auth = jwt({
secret: JWT_SECRET,
credentialsRequired: false,
algorithms: ['HS256']
});

How to access a token in session (Shopify Access Token)

I mostly work on front-end so I'm not super familiar with NodeJS.
I'm working on a Shopify Custom App and purpose of this app is when order placed it will receive webhook request and with that request it will send some data to other API (billing application)
I built shopify app with shopify app cli and my server.js file is like this;
import "#babel/polyfill";
import dotenv from "dotenv";
import "isomorphic-fetch";
import createShopifyAuth, { verifyRequest } from "#shopify/koa-shopify-auth";
import graphQLProxy, { ApiVersion } from "#shopify/koa-shopify-graphql-proxy";
import Koa from "koa";
import next from "next";
import Router from "koa-router";
import session from "koa-session";
const { receiveWebhook } = require("#shopify/koa-shopify-webhooks");
import * as handlers from "./handlers/index";
dotenv.config();
const port = parseInt(process.env.PORT, 10) || 8081;
const dev = process.env.NODE_ENV !== "production";
const app = next({
dev,
});
const handle = app.getRequestHandler();
const { SHOPIFY_API_SECRET, SHOPIFY_API_KEY, SCOPES } = process.env;
app.prepare().then(() => {
const server = new Koa();
const router = new Router();
server.use(
session(
{
sameSite: "none",
secure: true,
},
server
)
);
server.keys = [SHOPIFY_API_SECRET];
server.use(
createShopifyAuth({
apiKey: SHOPIFY_API_KEY,
secret: SHOPIFY_API_SECRET,
scopes: [SCOPES],
async afterAuth(ctx) {
//Auth token and shop available in session
//Redirect to shop upon auth
const { shop, accessToken } = ctx.session;
// This accessToken is what I need on other scope
ctx.cookies.set("shopOrigin", shop, {
httpOnly: false,
secure: true,
sameSite: "none",
});
// Register Webhook
handlers.registerWebhooks(
shop,
accessToken,
"ORDERS_PAID",
"/webhooks/orders/paid",
ApiVersion.October20
);
console.log(accessToken);
ctx.redirect("/");
},
})
);
const webhook = receiveWebhook({ secret: SHOPIFY_API_SECRET });
router.post("/webhooks/orders/paid", webhook, (ctx) => {
let user_id = ctx.state.webhook.payload.customer.id;
console.log("received webhook, user_id: ", user_id);
//console.log("ctx", ctx);
// I need that accessToken here to get some more info from Admin API with GraphQL
let accessToken = "???"
handlers
.graphqlRequest(
accessToken,
"https://my-store.myshopify.com/admin/api/2020-10/graphql.json",
`{
customer(id: "gid://shopify/Customer/${user_id}") {
email
metafields(first: 5) {
edges {
node {
key
value
}
}
}
}
}`
)
.then((res) => {
console.log("res => ", res);
})
.catch((err) => {
console.log("err => ", err);
});
});
server.use(
graphQLProxy({
version: ApiVersion.October20,
})
);
router.get("(.*)", verifyRequest(), async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
});
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
createShopifyAuth() method gets an accessToken with my app secret and app api key and I can use it in afterAuth() method but I also need that token in router.post() method to get more info from Shopify Admin API.
According to the Shopify docs(or what I understand), that key is available in session but how can I access that session data? or what can I use that token in router.push()?
The session is created only for the user who logged in the app from the admin panel. This is true for the online method for creating an access token.
If you are requesting the access token from a webhook request (a.k.a a request that doesn't require you to relogin in the app) you will not be able to access the session and you will not be able to get the access token. In addition the session expires at some point.
In order to use the Access Token in a webhook request you need to create an offline access token which is valid indefinitely. createShopifyAuth has an option for creating an offline access token, you just need to add accessMode: 'offline' to your request (more on this here)
Example:
createShopifyAuth({
apiKey: SHOPIFY_API_KEY,
secret: SHOPIFY_API_SECRET_KEY,
accessMode: 'offline',
scopes: ['read_products', 'read_orders'],
After you create the Offline Access Token you need to save that in a database (or some other way) and request it from the webhook route in order make your graphql request.
That's pretty much the just of it.

Unable to get decoded payload of JWT from auth0/express-jwt library

I'm trying to obtain the decoded JWT payload using the express-jwt from auth0 as a middleware in my expressjs application. For the token to be used for authentication, I have to use a custom async function to process the token first before providing it to express-jwt. I understand that I can write a custom function under the getToken parameter as documented in express-jwt as shown below, but it does not work with async function.
const jwt = require("express-jwt");
const checkJwt = jwt({
secret: 'hello world !',
algorithms: ['HS256'],
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring (req) {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
return req.headers.authorization.split(' ')[1];
} else if (req.query && req.query.token) {
return req.query.token;
}
return null;
}
});
module.exports = checkJwt;
To overcome this limitation, I included the jwt() as part of a larger middleware function that first process the token, and then providing it to jwt(). The unprocessed token is attached to my headers. My code structure is as follows:
const myMiddleware = async (req, res, next) => {
// Process Token First
const processedToken = await functionToProcessToken(req.headers.tokenLocation);
// Jwt Authentication
jwt({
secret: 'hello world !',
algorithms: ['HS256'],
credentialsRequired: false,
getToken: processedToken
});
next();
});
module.exports = myMiddleware;
While I'm able to authenticate my routes without any issue, I need to get the decoded JWT payload for further uses. By default, as written in the documentation, the decoded payload should have been attached to req.user, but when I try to console.log it, it shows undefined. I have also tried adding requestProperty but it does not work as well. The documentation for resultProperty is not very clear, so I could not get that to work too.
I very much appreciate if someone can point me towards right direction to help me achieve the expected outcome for my middleware. Thank you!

Google APi + Passport + React : Authentication flow

I'm using passport to authenticate using Google API, I'm sending a token by URL to the client (React app) which saves it in the localStorage.
I want to use that token : With every API call (get, post, put) I want to send that token to the server , but I didn't know how to verify that token on the server side.
Passport Startegy :
app.use(passport.initialize()); // Used to initialize passport
app.use(passport.session()); // Used to persist login sessions
passport.use(new GoogleStrategy({
clientID: 'IDxxxxx',
clientSecret: 'SecreXXX',
callbackURL: 'http://localhost:3000/callback'
},
(accessToken, refreshToken, profile, done) => {
// Directory API here
var userData = {
name: profile.displayName,
token: accessToken
};
done(null, userData);
Authentication :
app.get('/auth/google', passport.authenticate('google', {
scope: ['profile'] // Used to specify the required data
}));
// The middleware receives the data from Google and runs the function on Strategy config
app.get('/callback', passport.authenticate('google'), (req, res) => {
var token = req.user.token;
res.redirect("http://localhost:8000?token=" + token);
});
API in express (which contains CRUD methods) :
app.use('/api', movieRouter)
In react side : Getting the token
componentWillMount() {
var query = queryString.parse(this.props.location.search);
if (query.token) {
window.localStorage.setItem("jwt", query.token);
// appel a directory api (avec token) puis sauvergarder dans redux puis redirection vers liste demandes
this.props.history.push("/");
}
}
Doing API calls :
import axios from 'axios'
const api = axios.create({
baseURL: 'http://localhost:3000/api',
})
export const insertMovie = payload => api.post(`/movie`, payload)
I just need to send the token in every call and check it in the server side.
Thanks
You want to set the token in a header most likely, try changing your axios client to something like
const api = axios.create({
baseURL: 'http://localhost:3000/api',
headers: {
Authorization: `Bearer ${your_token_here}`
}
})
I'm not 100% sure if this is the correct header form that passport will be expecting, but it's the general idea you need to do.
If the token is correctly set in the header, session, or cookie by the client as noted by Bill Metcalf, then express is able to authenticate a route/endpoint by adding the passport.authenticate middleware function to the route, like so
app.use('/api', passport.authenticate('google', {failureRedirect:'/login'}), movieRouter)
Refer to http://www.passportjs.org/docs/google/ for more information
For every API that you want to verify the token, you can pass a verify token function (which I call 'isCorrectToken') before taking action like this:
router.get("/api", isCorrectToken, (req, res) => {
// your api content })
And then, this is our isCorrectToken function:
const isCorrectToken = (req, res, next) => {
const token = req.headers.authorization;
if(token){
const onlyToken = token.slice(7, token.length);
jwt.verify(onlyToken, accessToken, (err, decode) => {
if(err) return res.status(401).send({ msg: 'Invalid Token' });
req.user = decode;
next();
return;
});
}
else return res.status(401).send({ msg: 'Token is not supplied'});}
The number 7 is the length of 'Bearer ' (from Bill Metcalf's answer above).

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