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']
});
Related
I am trying to send a request to DetectIntent API from webhook - Dialog Flow CX . I downloaded service-key in GCP and stored in serviceaccount.json file .I created .env file and stored all the values from serviceaccount file to .env file.I get error when I run "node index.js"
Error:"IAM permission 'dialogflow.sessions.detectIntent' on 'projects/dialogflowcx-340818,/agent' denied.",
I went through related stackoverflow posts where they suggested to set GOOGLE_APPLICATION_CREDENTIALS .I tried that as well.Still I get the same error.
I tried :
process.env.GOOGLE_APPLICATION_CREDENTIALS="/Users/suchitranagarajan/Desktop/nodeproject/detectintent/service-account.json"
Also,I tried to set
export GOOGLE_APPLICATION_CREDENTIALS="/Users/suchitranagarajan/Desktop/nodeproject/detectintent/service-account.json"
Please find the code below:
const dialogflow = require('#google-cloud/dialogflow');
test=require('dotenv').config();
const express = require('express');
const path = require("path");
process.env.GOOGLE_APPLICATION_CREDENTIALS="/Users/suchitranagarajan/Desktop/nodeproject/detectintent/service-account.json"
const PROJECTID = process.env.project_id;
const location=process.env.location
const agentid=process.env.agentid
const CONFIGURATION = {
credentials: {
private_key: process.env.private_key,
client_email: process.env.client_email
}
}
const sessionClient = new dialogflow.SessionsClient();
const detectIntent = async (languageCode, queryText, sessionId) => {
let sessionPath = sessionClient.projectLocationAgentSessionPath(PROJECTID,location,agentid,sessionId);
// The text query request.
let request = {
session: sessionPath,
queryInput: {
text: {
// The query to send to the dialogflow agent
text: queryText,
// The language used by the client (en-US)
languageCode: languageCode,
},
},
};
// Send request and log result
const responses = await sessionClient.detectIntent(request);
console.log(responses);
const result = responses[0].queryResult;
console.log(result);
return {
response: result.fulfillmentText
};
}
detectIntent("en", "hello", "abcd1234");
// Start the webapp
const webApp = express();
// Webapp settings
webApp.use(express.urlencoded({
extended: true
}));
webApp.use(express.json());
// Server Port
const PORT = process.env.PORT || 5000;
// Home route
webApp.get('/', (req, res) => {
res.send(`Hello World.!`);
});
// Dialogflow route
webApp.post('/dialogflow', async (req, res) => {
let languageCode = req.body.languageCode;
let queryText = req.body.queryText;
let sessionId = req.body.sessionId;
let responseData = await detectIntent(languageCode, queryText, sessionId);
res.send(responseData.response);
});
// Start the server
webApp.listen(PORT, () => {
console.log(`Server is up and running at ${PORT}`);
});
Please find .env file below:
type= service_account,
project_id=xxx,
private_key_id=xxx,
private_key=xxx","client_email": "xxx","client_id": "xxx","auth_uri": "xxx,
token_uri=xxx,
auth_provider_x509_cert_url= xxx,
client_x509_cert_url=xxx
location=xxx
agentid=xxx
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.
We have a front end react app that has MSAL login that uses aws api gateway api endpoints for backend operations. After the login process I would like to send the bearer Token to the api and get it authenticated. From what I have read online you do this by using node.js and passport.authenticate(). Problem is trying to make this work in aws api gateway. So I came up with a scheme to do the authentication through a Authorizer lambda. 1) Use aws-serverless-express to run express as api proxy in lambda 2) Use passport-azure-ad module to set bearerstrategy. 3) run passport.authenticate() to authenticate token. 4) upon valid token return allow policy from lambda 5) The api request will continue on
I just placed the following files and required node modules into a node.js lambda. So this essentially is a middlewear between client and api. If token is valid will allow request to go through.
During my tests I am getting this error when it applies the bearerstrategy:
{
"name": "AzureAD: Bearer Strategy",
"hostname": "169.254.43.173",
"pid": 8,
"level": 30,
"msg": "authentication failed due to: token is not found",
"time": "2020-08-24T23:48:35.497Z",
"v": 0
}
Would be great if there is a simpler way to authenticate a Bearer token.
Lambda index.js file:
const awsServerlessExpress = require('aws-serverless-express')
const app = require('./app')
const server = awsServerlessExpress.createServer(app)
exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context)
config.js
'use strict';
const config = {
identityMetadata: "https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration",
clientID: "xxxxxxxxxxxxxxxxxxxx",
validateIssuer: true,
loggingLevel: 'info',
passReqToCallback: false,
ignoreExpiration: true
};
module.exports = config
app.js file
'use strict'
const express = require('express')
const awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')
const passport = require("passport");
const config = require('./config');
const BearerStrategy = require('passport-azure-ad').BearerStrategy;
const bearerStrategy = new BearerStrategy(config, (token, done) => {
// Send user info using the second argument
done(null, {}, token);
}
);
const app = express();
app.use(passport.initialize());
const router = express.Router()
passport.use(bearerStrategy);
router.use(awsServerlessExpressMiddleware.eventContext())
var generatePolicy = function(effect, resource) {
var authResponse = {};
authResponse.principalId = 'user';
if (effect && resource) {
var policyDocument = {};
policyDocument.Version = '2012-10-17'; // default version
policyDocument.Statement = [];
var statementOne = {};
statementOne.Action = 'execute-api:Invoke'; // default action
statementOne.Effect = effect;
statementOne.Resource = "*";
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
return authResponse;
}
var generateAllow = function(resource) {
return generatePolicy('Allow', resource);
}
router.get('/', passport.authenticate('oauth-bearer', {session: false}),
(req, res) => {
res.send(generateAllow(req.apiGateway.event.methodArn))
}
);
// The aws-serverless-express library creates a server and listens on a Unix
// Domain Socket for you, so you can remove the usual call to app.listen.
// app.listen(3000)
app.use('/', router)
// Export your express server so you can import it in the lambda function.
module.exports = app
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),
);
I am building a single page application with a Vuejs frontend and a Nodejs backend. Been reading tons about single sign on and oidc, and managed to implement authentication using Oidc for the frontend, where I get a token from my identity provider.
Not sure however, now, how to also implement this for the backend and where/when/how.
So currently, when a user accesses the page, in my router.js file, this happens:
router.beforeEach(vuexOidcCreateRouterMiddleware(store));
In the store then, I do this:
Vue.use(vuex);
const store = new Vuex.Store({
state: {
// holds current list of products
products: [],
},
getters,
mutations,
actions,
modules: {
// initialize PING-OIDC module
oidcStore: vuexOidcCreateStoreModule(
oidcSettings,
{ namespaced: false },
{
userLoaded: (oidcUser) => {
axios.defaults.headers.common.Authorization = `${oidcUser.token_type} ${oidcUser.access_token}`;
},
},
),
},
});
export default store;
So I set the authorization header, but now I am not sure where and how to proceed in the backend to also add + validate authentication there.
Very new to all this and there seem to be so many different way to proceed, so appreciate all the hints.
Currently, in server.js, I just do the following:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json())
const cors = require('cors')
const corsOptions = {
origin: 'http://localhost:5002',
optionsSuccessStatus: 200
}
app.use(cors(corsOptions))
//get mysql db here
const data = require('./app/config/db.config.js');
const db = data.MySQL_DB;
// show all products
app.get('/api/productlist',(req, res) => {
const sql = "SELECT ID FROM Product_Table";
const query = db.query(sql, (err, results) => {
if(err) throw err;
console.log("productIds ", results);
res.send(JSON.stringify({"status": 200, "error": null, "response": results}));
});
});
// Create a Server
var server = app.listen(8080, function () {
var host = server.address().address
var port = server.address().port
console.log("App listening at http://%s:%s", host, port)
})
So should I create a post request in there as well? Just not sure on how to validate the request there.. Thanks a lot for the help!
You need to clarify which data/routes/pages you want to protect.
You log in from your client -> send to the server (ex : api/login) -> respond to your client with credential -> store the user and credential
see passport.js for express
see accesscontrol for your data server side
Note that routes in client can be easily hack