I'm getting the error Hapi unknown authentication strategy jwt but I'm not sure why. I'm certain I've probably set something up wrong but here's my server index.js:
Should i be using a different auth strategy? Also stackoverflow won't let me submit my question because it's mostly code but I'm not sure what else to submit. All of the details are here and I don't know what else to add to provide any more information that i can. I'm just using using auth with config.auth.strategy and jwt in an array.
const Hapi = require('#hapi/hapi');
const objection = require('objection');
const knex = require('./knex');
const authService = require('./auth/auth-service');
const JWTAuth = require('hapi-auth-jwt2');
const init = async () => {
const server = Hapi.server({
port: 9000,
host: 'localhost',
routes: { cors: {
origin: ['*'],
headers: ['Authorization'],
exposedHeaders: ['Accept'],
additionalExposedHeaders: ['Accept'],
maxAge: 60,
credentials: true
}}
});
objection.Model.knex(require('./knex'));
await server.register([
{plugin: JWTAuth},
{
plugin: require('./movies/movie-routes'),
routes: {prefix: '/movies'}
}, {
plugin: require('./user/user-routes'),
}
])
server.auth.strategy('jwt', 'jwt',{
key: authService.jwtKey,
validate: authService.validateJWT,
verifyOptions: {algorithms: ['HS256']},
errorFunc: (err)=> {return err},
cookieKey: 'id_token'
})
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
Seems like there is a problem with authService.validateJWT I have added an example of HapiJs jwt authentication please it once for your reference
const Hapi = require('#hapi/hapi');
const JWTAuth = require('hapi-auth-jwt2');
const init = async () => {
const server = Hapi.server({
port: 9000,
host: 'localhost',
routes: {
cors: {
origin: ['*'],
headers: ['Authorization'],
exposedHeaders: ['Accept'],
additionalExposedHeaders: ['Accept'],
maxAge: 60,
credentials: true
}
}
});
await server.register([
{ plugin: JWTAuth }
])
server.auth.strategy('jwt', 'jwt', {
key: 'your-key',
validate: async function (decoded, request) {
if (!decoded) {
return { isValid: false };
} else {
request.auth.credentials = {
'user': decoded,
'token': request.headers['authorization']
};
return { isValid: true };
}
},
verifyOptions: { algorithms: ['HS256'] },
cookieKey: 'id_token'
});
server.auth.default('jwt');
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Hello World!';
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
Related
I'm following a tutorial for Ben Awad and i'm trying to set a userId in an express session after the user logs in to save the cookies in the browser. But when i check the req.session.userId its always undefined.
this is my index.ts
`
const RedisStore = connectRedis(session);
const redis = new Redis({
port: 6379,
host: "127.0.0.1",
});
redis.on("connect", () => console.log("Redis redisClient Connected"));
redis.on("error", (err) => console.log("Redis Client Connection Error", err));
app.use(
session({
name: "SessionID",
store: new RedisStore({
client: redis,
disableTouch: true,
}),
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365 * 10, //10 years
httpOnly: true,
sameSite: "lax",
domain: _prod_ ? ".codeponder.com" : undefined,
secure: _prod_, // cookie only works in https
},
secret: "adsadafasdasdasfasfdasda",
resave: false,
saveUninitialized: true,
})
);
const apolloServer = new ApolloServer({
schema: await buildSchema({
resolvers: [PostResolver, UserResolver],
validate: false,
}),
context: ({ req, res }): MyContext => ({ em: orm.em, res, req, redis }),
});
await apolloServer.start();
apolloServer.applyMiddleware({
app,
cors: {
credentials: true,
origin: ["http://localhost:3000", "https://studio.apollographql.com"],
},
});
app.listen(4000, () => {
console.log("server started on localhost:4000");
});
};
`
and when i try to save the userId in the login. Here when i log the req.session after assigning it to the user.id i can see it working
`
#Mutation(() => UserResponse)
async login(
#Arg("options") options: UsernamePasswordInput,
#Ctx() { em, req }: MyContext
): Promise<UserResponse> {
const user = await em.findOne(User, { username: options.username });
if (!user) {
return {
errors: [
{
field: "username",
message: "that username doesnt exist",
},
],
};
}
const valid = await argon2.verify(user.password, options.password);
if (!valid) {
return {
errors: [
{
field: "password",
message: "incorrect password",
},
],
};
}
req.session.userId = user.id;
console.log("req.>>", req.session);
req.session.save(() => {
console.log("saved");
});
return { user };
}
}
`
But when i check if the user is logged in or not from this function. req.session.userId is always undefined and not saved in the session
`
#Query(() => User, { nullable: true })
async me(#Ctx() { req, em }: MyContext) {
console.log("req", req.session);
if (!req.session.userId) {
return null;
}
const user = await em.findOne(User, { id: req.session.userId });
return user;
}
`
i tried upgrading/downgrading the packages. Checked my redis connection. Nothing seem to work
When signing in with postman everything works fine. But when i am doing an axios request i get 404 error and directly after 204 error. When i render my vue.js page i get "cannot get api/auth/signin. Also I get a message somewhere that says user not found.
What i have tried:
Frontend: I tried with adding headers to my axios request. I console logged the data and it seems perfectly fine.
Backend: Changed deprecated body parsers.
Frontend Code:
Auth store
import axios from "axios";
const state = {
token: "",
users: [],
};
const getters = {};
const actions = {
async signIn(_, payload) {
const response = await axios.post(
"http://localhost:3000/api/auth/signin",
{ payload },
{
headers: {
"Content-Type": "application/json",
},
}
);
console.log(response.data);
console.log(response.headers);
console.log(response.status);
},
};
const mutations = {};
export default {
state,
getters,
actions,
mutations,
};
This is my backend:
Controller
//signin
exports.signin = (req, res) => {
User.findOne({
username: req.body.username,
})
.populate("roles", "-__v")
.exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (!user) {
return res.status(404).send({ message: "User Not found." });
}
var passwordIsValid = bcrypt.compareSync(
req.body.password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({
accessToken: null,
message: "Invalid Password!",
});
}
var token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400, // 24 hours
});
var authorities = [];
for (let i = 0; i < user.roles.length; i++) {
authorities.push("ROLE_" + user.roles[i].name.toUpperCase());
}
res.status(200).send({
id: user._id,
username: user.username,
email: user.email,
roles: authorities,
accessToken: token,
});
});
};
Route
module.exports = function (app) {
app.use(function (req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});
app.post(
"/api/auth/signup",
[
verifySignUp.checkDuplicateUsernameOrEmail,
verifySignUp.checkRolesExisted,
],
controller.signup
);
app.post("/api/auth/signin", controller.signin);
And my server
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");
const Quote = require("./models/Quote");
const quoteRoute = require("./routes/quoteRoute");
const quoteController = require("../Maxico/controllers/quoteController");
const config = require("./config/config");
const verifySignup = require("./middlewares/verifySignUp");
const Role = require("./models/Role");
const app = express();
//Import routes
//const authRoute = require("./routes/auth");
var corsOptions = {
origin: "http://localhost:8080/?#/",
};
app.use(cors(corsOptions));
app.use(express.urlencoded({ extended: true }));
app.use(express.json()); //
const db = require("./models/Quote");
mongoose
.connect(
"url",
{
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
}
)
.then(() => {
console.log("Connected to the database!");
})
.catch((err) => {
console.log("Cannot connect to the database!", err);
process.exit();
});
app.use(express.json());
app.get("/", (req, res) => {
res.send("Welcome to homepage");
});
app.use("/quote", quoteRoute);
require("./routes/authRoute")(app);
//require("./routes/userRoute")(app);
// initial roles
Role.estimatedDocumentCount((err, count) => {
if (!err && count === 0) {
new Role({
name: "user",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'user' to roles collection");
});
new Role({
name: "moderator",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'moderator' to roles collection");
});
new Role({
name: "admin",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'admin' to roles collection");
});
new Role({
name: "superadmin",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'superadmin' to roles collection");
});
}
});
// set port, listen for requests
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
In my network tab the request pay load got sent like this:
{payload: {username: "jon", password: "password"}}
payload: {username: "jon", password: "password"}
But my postman only accepts this:
{username: "jon", password: "password"}
So in my action i sent like this:
const actions = {
async signIn(_, payload) {
console.log(payload);
const response = await axios.post(
"http://localhost:3000/api/auth/signin",
payload,
{
headers: {
"Content-Type": "application/json",
},
}
);
console.log(payload);
console.log(response.data);
console.log(response.headers);
console.log(response.status);
},
};
My nodejs API runs fine for few hours then API does not return data to client. Once I debug I found that TypeOrm Unable to query data from remote postgres server even though postgres server is Connected. Query in Nodejs application die with no error.
I can query data from remote postgres DB from pgAdmin.
The app.ts code
const conn = await createConnection({
host: process.env.TYPEORM_HOST,
password: process.env.TYPEORM_PASSWORD,
type: 'postgres',
username: process.env.TYPEORM_USERNAME,
database: process.env.TYPEORM_DATABASE,
port: Number(TYPEORM_PORT),
logging: TYPEORM_LOGGING === 'true' ? true : false,
synchronize: TYPEORM_SYNCHRONIZE === 'true' ? true : false,
entities: [UserEntity],
});
console.log(conn.options.database);
////#### Middleware Section ####/////
const apolloServer = new ApolloServer({
schema: await CreateSchema(),
subscriptions: {
path: `${ENDPOINT_PATH}`,
},
uploads: false,
//tracing: true,
context: ({ req, res }: any) => ({
req,
res,
...dataLoaderInject,
}),
});
const app = Express();
const whitelist: any = process.env.ENDPOINT_CORS?.split(',').map((x) =>
x.trim()
);
var corsOptions = {
origin: function (origin: any, callback: any) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(null, false);
}
},
credentials: true,
};
app.use(cors(corsOptions));
app.use(graphqlUploadExpress({ maxFileSize: 500000000, maxFiles: 50 }));
app.use(cookieParser());
apolloServer.applyMiddleware({ app, cors: false, path: ENDPOINT_PATH });
const httpServer = http.createServer(app);
apolloServer.installSubscriptionHandlers(httpServer);
httpServer.listen(ENDPOINT_PORT, () => {
//Register jobScheduler here
JobSchedulerService.sendReminder();
console.log(
`Server started at ${ENDPOINT}:${ENDPOINT_PORT}${ENDPOINT_PATH}`
);
});
POST request where TypeOrm fails to return results from remote postgres server.
app.post('/user', async (req, res) => {
let token = req.cookies.UserCookie;
if (!token) {
console.log('token is not valid ' + token);
return res.send({ ok: false, accessToken: '' });
}
let payload: any;
try {
payload = verify(token, process.env.REFRESH_TOKEN_SECRET!);
} catch (err) {
console.log(err);
return res.send({ ok: false, accessToken: '' });
}
const user = await
getRepository(UserEntity)
.createQueryBuilder()
.where('"ID"=:ID', { ID: payload.userID })
.getOne() // request die here , no error , does not respond anything
.catch( err => {
console.log(err, "User Query error")
})
if (!user) {
console.log('User not found');
return res.send({ ok: false, accessToken: '' });
}
Stack version
Postgres 12
Nodejs : v12.16.3
npm : 6.14.4
"pg": "^8.3.3"
"typeorm": "^0.2.26",
"express": "^4.17.1",
"node-cron": "^2.0.3",
Your response will be much appreciated Thank you.
I am trying to use the login API I made using node, however, whenever I call the API using Axios, it gives me a request failed in the console.
This is how I use axios to call my method:
axios
.post(
"http://localhost:8080/staffMember/login",
{
email: "Fred#gmail.com",
password: "Flintstone",
},
{
headers: {
"Content-Type": "application/json",
},
}
)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error.message);
});
};
And this is my login page and console :
This is my backend configuration:
require("dotenv").config();
const mongoose = require("mongoose");
const express = require("express");
const app = express();
const staffMember = require("./routers/staffMember.router.js");
const hrMember = require("./routers/hrMember.router.js");
const academicMember = require("./routers/academic members/academicMember.router");
const headOfDepartment = require("./routers/academic members/headOfDepartment.router");
const courseInstructor = require("./routers/academic members/courseInstructor.router");
const courseCoordinator = require("./routers/academic members/courseCoordinator.router");
var cors = require("cors");
app.use(cors());
mongoose
.connect(process.env.DATABASE_CONN_STRING, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true,
})
.then(() => {
console.log("DB connected");
})
.catch(() => {
console.log("DB connection failed");
});
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use("/staffMember", staffMember);
app.use("/hrMember", hrMember);
app.use("/academicMember", academicMember);
app.use("/courseInstructor", courseInstructor);
app.use("/courseCoordinator", courseCoordinator);
app.use("/headOfDepartment", headOfDepartment);
module.exports = app;
please try this
axios.post('YOUR_FULL_URL', {
username: 'api',
password: 'MY_PASSWORD',
grant_type: 'MY_GRANT_TYPE'
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}}).then(response => {console.log(response)})
.catch(error => {
console.log(error.response)
});
well, I do not know how is the backend configured, but you could start by stringyfing yow body
axios
.post(
"http://localhost:8080/staffMember/login",
JSON.strinfify({
email: "Fred#gmail.com",
password: "Flintstone",
}),
{
headers: {
"Content-Type": "application/json",
},
}
)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error.message);
});
};
If that does not do any change, then change the header instead
axios
.post(
"http://localhost:8080/staffMember/login",
{
email: "Fred#gmail.com",
password: "Flintstone",
},
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}
)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error.message);
});
};
lil commercial
If you want to b'cum a master with axios checkout https://medium.com/#enetoOlveda/how-to-use-axios-typescript-like-a-pro-7c882f71e34a
or npm i -S axios-es6-class
In my hapijs app I have few routes which require a session, uses hapi-auth-cookie plugin for auth strategy. I want to add few tests (via Lab ) for these routes.
I couldn't find any documentation on how I can setup a test (maybe via before ?) for this scenario. Any help is appreciated. Thanks in advance.
If you only need an authenticated user, just assign the user to the credentials property in tests:
var user = { ... };
server.inject({ method: 'GET', url: '/', credentials: user }, function (res) {
console.log(res.result);
});
Here is an example that demonstrates it:
var Bcrypt = require('bcrypt');
var Hapi = require('hapi');
var HapiAuthCookie = require('hapi-auth-cookie');
var server = new Hapi.Server();
server.connection({ port: 3000 });
var users = {
john: {
username: 'john',
password: '$2a$10$iqJSHD.BGr0E2IxQwYgJmeP3NvhPrXAeLSaGCj6IR/XU5QtjVu5Tm',
name: 'John Doe',
id: '2133d32a'
}
};
var validate = function (request, username, password, callback) {
var user = users[username];
if (!user) {
return callback(null, false);
}
Bcrypt.compare(password, user.password, function (err, isValid) {
callback(err, isValid, { id: user.id, name: user.name });
});
};
server.register(HapiAuthCookie, function (err) {
server.auth.strategy('session', 'cookie', {
password: 'secret',
validateFunc: validate
});
server.route({
method: 'GET',
path: '/',
config: {
auth: 'session',
handler: function (request, reply) {
reply('hello, ' + request.auth.credentials.name);
}
}
});
server.inject({ method: 'GET', url: '/', credentials: users.john }, function (res) {
console.log(res.result);
});
});
Large part of the example was taken from the Authentication Tutorial.
For my need for session during testing, I created hapi-session-inject. Usage is as follows
const server = new Hapi.Server();
const session = new Session(server);
// Callback interface
session.inject('/', (res) => {
...
});
// Promise interface
return session.inject('/').then((res) => {
...
});
Hope it helps.