Next-Auth CredentialsProvider really slow - mongodb

The following works fine, but I have noticed that it is really slow login in a client. How can I make it faster?
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import { ObjectId } from 'mongodb'
import { MongoDBAdapter } from "#next-auth/mongodb-adapter"
import clientPromise from "../../../lib/mongodb";
import { v4 as uuidv4 } from 'uuid';
var CryptoJS = require("crypto-js");
const sFinder = async (task, token) => {
try{
const client = await clientPromise;
const database = client.db('DRN1');
const ses = await database.collection('sessions');
switch (task) {
case 1:
const result = await ses.find({
"userId": ObjectId(token.uuid)
}).sort({"_id":-1}).limit(1).toArray();
if (!result) {
return 202;
}
else{
return result[0].sessionToken
}
break;
case 2:
const insertResult = await ses.insertOne({"userId":token.uuid, "sessionToken":token.accessToken});
if (!insertResult) {
return 203;
}
else{
return insertResult
}
break;
case 3:
var expdate = new Date(token.exp * 1000);
const UpdateResult = await ses.updateOne({"userId":ObjectId(token.uuid), "sessionToken":token.accessToken},
{ $set: {"expires": expdate}}, { upsert: true });
if (!UpdateResult) {
return 203;
}
else{
return UpdateResult
}
break;
default:
break;
}
} catch(e){
console.error(e);
}
}
export default NextAuth({
adapter: MongoDBAdapter(clientPromise),
session: {
strategy: 'jwt',
jwt: true,
},
providers: [
CredentialsProvider({
name: 'DRN1',
credentials: {
username: { label: "Username", type: "text"},
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
try{
const client = await clientPromise;
const database = client.db('DRN1');
const users = await database.collection('users');
const result = await users.findOne({
username: credentials.username,
});
if (!result) {
throw new Error('No user found with the username');
}
var bytes = CryptoJS.AES.decrypt(result.password, process.env.PASS_ENC);
var decryptedData = bytes.toString(CryptoJS.enc.Utf8);
//Check hased password with DB password
if(decryptedData != credentials.password){
throw new Error('Password doesnt match');
}
return {uuid:result._id, username: result.username, email: result.email, type:result.type, "sessionID":uuidv4()};
} catch(e){
console.error(e)
}
}
})
],
callbacks: {
signIn: async ({ user, account, profile, email, credentials }) => {
account.accessToken = user.sessionID
account.uuid = user.uuid
const test = await sFinder(2,account)
return true
},
jwt: async ({ token, account }) => {
if (account) {
token.uuid = account.uuid
token.accessToken = account.accessToken
}
const lastUsedToken = await sFinder(1,token)
const updateTokenExpire = await sFinder(3,token)
if(lastUsedToken != token.accessToken){
// console.log("I have made it an error")
token.error = 555;
}
return token
},
session: async ({ session, token, user }) => {
session.uuid = token.uuid
if(!token.accessToken){
//OAUTH Accounts
session.accessToken = uuidv4()
}else{
session.accessToken = token.accessToken
}
if(token.error == 555){
session.error = 555
}
return session
}
},
pages:{
error: 'signin'
},
theme: {
colorScheme: "dark", // "auto" | "dark" | "light"
brandColor: "", // Hex color code
logo: "https://storage.googleapis.com/radiomedia-images/station_logos/v2/DRN1_small.png" // Absolute URL to image
}
});
I believe what is slowing it down is the following
callbacks: {
signIn: async ({ user, account, profile, email, credentials }) => {
account.accessToken = user.sessionID
account.uuid = user.uuid
const test = await sFinder(2,account)
return true
},
jwt: async ({ token, account }) => {
if (account) {
token.uuid = account.uuid
token.accessToken = account.accessToken
}
const lastUsedToken = await sFinder(1,token)
const updateTokenExpire = await sFinder(3,token)
if(lastUsedToken != token.accessToken){
// console.log("I have made it an error")
token.error = 555;
}
return token
},
session: async ({ session, token, user }) => {
session.uuid = token.uuid
if(!token.accessToken){
//OAUTH Accounts
session.accessToken = uuidv4()
}else{
session.accessToken = token.accessToken
}
if(token.error == 555){
session.error = 555
}
return session
}
},
Mainly all the awaits, but the await functions are to make sure the user is not login on another device. As we log the old devices out automatically.

Related

unstable_getServerSession to secure apis (nextAuth)

i need to secure the API so that only authorized user can access them. I followed the documentation in this link https://next-auth.js.org/tutorials/securing-pages-and-api-routes#securing-api-routes but apparently I am not retrieving the session.
I am able to console.log the authOptions but if I try to console log the session (and I am logged in), it logs "null"
This is the code
pages/api/profile.js
import prisma from "../../../lib/prisma";
import { unstable_getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]";
export default async function handler(req, res) {
const session = await unstable_getServerSession(req, res, authOptions);
console.log("SESSION", session); // this logs "null"
if (!session) {
return res.status(401).json("Not authorized");
}
try {
const user = await prisma.user.findUnique({
where: { email: session.user.email },
});
return res.status(200).json(user);
} catch (error) {
console.error(error);
return res
.status(503)
.json(
"Our server is not able to process the request at the moment, please try again later!"
);
}
pages/api/auth/[...nextauth].js
import NextAuth from "next-auth";
import CognitoProvider from "next-auth/providers/cognito";
import prisma from "../../../lib/prisma";
export const authOptions = {
providers: [
CognitoProvider({
clientId: process.env.CLIENTID_NEXTAUTH,
issuer: process.env.COGNITO_ISSUER,
clientSecret: process.env.CLIENTSECRET_NEXTAUTH,
}),
],
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60,
updateAge: 24 * 60 * 60,
},
callbacks: {
async jwt({ token, account }) {
if (account) {
token.accessToken = account.access_token;
}
return token;
},
async session({ session, token }) {
const user = await prisma.user.findUnique({
where: { email: session?.user?.email },
});
if (!user) throw new Error("User not found in the database.");
const mySession = {
...session,
accessToken: token.accessToken,
email: user.email,
};
return mySession;
},
},
};
export default NextAuth(authOptions);
pages/dashboard/index.js
import axios from "axios";
import React, { useState } from "react";
import { getSession, useSession } from "next-auth/react";
const Dashboard = (props) => {
let { data: session, status } = useSession();
if (status === "loading") {
return <p>Loading...</p>;
}
if (status === "unauthenticated") {
window.location.reload();
}
return (
<p>
{props.userInfo.name}
</p>
);
};
export default Dashboard;
export async function getServerSideProps(context) {
const session = await getSession(context);
if (!session) {
return {
redirect: {
destination: "/",
permanent: false,
},
};
}
console.log("SESSION IN INDEX", session); // this logs the session
const userInfo = await axios.get(
`${process.env.BASE_URL}/api/profile?email=${session.email}`
);
return {
props: {
session,
userInfo: userInfo.data ? userInfo.data : null,
},
};
}
so when I login, I can see the SESSION in INDEX but when I hit the api/profile, the session from unstable_getServerSession is null, so I canno see nothing in the dashboard
resolved:
when calling the api you need to pass the headers, for example in the dashboard/index.js
const userInfo = await axios.get(
`${process.env.BASE_URL}/api/profiles/profile?email=${session.email}`,
{
withCredentials: true,
headers: {
Cookie: context.req.headers.cookie,
},
}
);
while in the API endpoint
import { getServerSession, getSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]";
export default async function handler(req, res) {
const session = await getServerSession(req, res, authOptions);
console.log("SESSION", session);
//your code
}

Req. query doesn't pass

I'm trying to pull data from a document containing a logged-in user from the MongoDB database. However, req. the query doesn't seem to pass.
case "GET": {
const { userName } = req.query;
const users = await db
.collection("USERS")
.find({ user: { $in: [userName] } })
.toArray();
res.json(users);
break;
}
export async function getStaticProps() {
const res = await fetch(`http://localhost:3000/api/usersAPI`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
let users = await res.json();
return {
props: { users },
};
}

Next-authjs issue with Mongodb

Below is my [...nextauth].js file and for some reason when I try and use it to login by going to http://localhost:3000/api/auth/signin it presents the username and password box but then when I submit it I get an error.
http://localhost:3000/api/auth/error?error=Illegal%20arguments%3A%20undefined%2C%20undefined
But it is not telling me what the illegal argument is, is there any way to find out?
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import clientPromise from "../../../lib/mongodb";
import jwt from "next-auth/jwt";
import { compare } from 'bcryptjs';
export default NextAuth({
session: {
jwt: true,
},
providers: [
CredentialsProvider({
// The name to display on the sign in form (e.g. 'Sign in with...')
name: 'DRN1',
credentials: {
username: { label: "Username", type: "text"},
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
const client = await clientPromise
const { fieldvalue } = req.query
console.log("RUNNING THIS QUERY "+req.query)
const database = client.db('DRN1');
const users = await database.collection('users');
const result = await users.findOne({
username: credentials.username,
});
if (!result) {
client.close();
throw new Error('No user found with the username');
}
//Check hased password with DB password
const checkPassword = await compare(credentials.passowrd, result.passowrd);
//Incorrect password - send response
if (!checkPassword) {
client.close();
throw new Error('Password doesnt match');
}
//Else send success response
client.close();
return { username: result.username };
}
})
],
theme: {
colorScheme: "dark", // "auto" | "dark" | "light"
brandColor: "", // Hex color code
logo: "https://storage.googleapis.com/radiomedia-images/station_logos/v2/DRN1_small.png" // Absolute URL to image
}
});

Cannot read properties of undefined "collectionName" - Mongoose.models

I'm trying to data in the Note collection from a local database of MongoDB using Mongoose with Nextjs and getting an error "Cannot read properties of undefined (reading 'note')"
Any ideas on what I am doing wrong?
import mongoose from 'mongoose';
const { Schema } = mongoose;
const NoteSchema = new Schema(
{
title: {
type: String,
maxlength: 60,
},
description: {
type: String,
maxlength: 200,
},
},
{ timestamps: true }
);
export default mongoose.models.note || mongoose.model('note', NoteSchema)
import dbConnect from "../../../lib/dbConnect"
import Note from "../../../models/Note"
export default async function handler(req, res) {
const { method } = req
await dbConnect()
if (method === 'GET') {
try {
const note = await Note.find({})
res.status(200).json({ success: true, data: note})
} catch (error) {
res.status(400).json({ success: false })
}
}
if (method === 'POST') {
try {
const note = await Note.create(req.body)
res.status(200).json({ success: true, data: note})
} catch (error) {
res.status(500).json(error)
}
}
}
import mongoose from 'mongoose'
const {MONGODB_URI} = process.env
let cached = global.mongoose
if (!cached) {
cached = global.mongoose = { conn: null, promise: null }
}
async function dbConnect() {
if (cached.conn) {
return cached.conn
}
if (!cached.promise) {
const opts = {
useNewUrlParser: true,
useUnifiedTopology:true,
bufferCommands: false,
}
cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
return mongoose
})
}
cached.conn = await cached.promise
return cached.conn
}
export default dbConnect
I get the following
Unhandled Runtime Error
TypeError: Cannot read properties of undefined (reading 'note')
I was able to fix it by changing the export module
global.PizzaSchema = global.NoteSchema || mongoose.model('Note', NoteSchema);
export default global.NoteSchema;

How do I retrieve what I return? (GraphQL query newbie)

I wrote 2 resolvers (signup and login). They return a JWToken. In playground, I'm testing the functions and it won't let me make a mutation without specifying some subfields to display. They all are null and sometimes I get a "Cannot return null for non-nullable field users._id". I'm using express and mongodb.
const { UserTC, UserSchema } = require("../models/user");
const bcrypt = require('bcrypt')
const jsonwebtoken = require('jsonwebtoken')
const resolver = function () {};
resolver.me = UserTC.addResolver({
name: "me",
type: UserTC,
args: { record: UserTC.getInputType() },
resolve: async ({ source, args }) => {
if (!args.record) {
throw new Error('You are not authenticated!')
}
return await UserSchema.findById(args.record._id)
}
});
resolver.signup = UserTC.addResolver({
name: "signup",
type: UserTC,
args: { record: UserTC.getInputType() },
resolve: async ({ source, args }) => {
const user = await UserSchema.create({
firstName: args.record.firstName,
lastName: args.record.lastName,
email: args.record.email,
password: await bcrypt.hash(args.record.password, 10)
});
return jsonwebtoken.sign(
{ id: user._id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '1y' }
)
},
});
resolver.login = UserTC.addResolver({
name: "login",
type: UserTC,
args: { record: UserTC.getInputType() },
resolve: async ({ source, args }) => {
const user = await UserSchema.findOne({email: args.record.email });
if (!user) {
throw new Error('Incorrect email')
}
const valid = await bcrypt.compare(args.record.password, user.password);
if (!valid) {
throw new Error('Incorrect password')
}
return jsonwebtoken.sign(
{ id: user._id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '1y' }
)
},
});
module.exports = resolver;
Here are the queries:
mutation {
signup(
record: {
firstName: "Ben2"
lastName: "Dormant"
email: "Ben2.Dormant#gmail.com"
password: "qwerty"
}
)
}
mutation {
login(record: {
email: "nicolas.sursock#gmail.com",
password: "azerty" })
}
I founded a bug, try like this because you can not use await in reutrn
resolver.me = UserTC.addResolver({
name: "me",
type: UserTC,
args: { record: UserTC.getInputType() },
resolve: async ({ source, args }) => {
if (!args.record) {
throw new Error('You are not authenticated!')
}
let result = await UserSchema.findById(args.record._id)
return result
}
});