Hi I have tried all things possible to find out what could be causing above error on my live website built using NEXTJS.
I have noticed that this error happens whenever I reload the website.
I also notice that whenever I try to login using userName and password, I am able to do that without any errors in local host and also using https://codesandbox.io. But on the live site I get a server error "problem with the server configuration.".
when I scroll further on my developer tools I find the following additional information.
Unexpected token < in JSON at position 0 {error: {…}, path: "session", message: "Unexpected token < in JSON at position 0"
I have added the following environment variables in vercel
NEXTAUTH_URL = https://****.vercel.app/
MONGODB_URI = mongodb+srv://****#cluster0.9kc5p.mongodb.net/*****?retryWrites=true&w=majority
my [...nextauth].js file is as below
import NextAuth from "next-auth";
import CredentialsProviders from "next-auth/providers/credentials";
import { verifyPassword } from "../../../lib/hashedPassword";
import clientPromise from "../../../lib/mongodb";
export default NextAuth({
session: {
strategy: "jwt"
} /* check other providers you may add database etc */,
providers: [
CredentialsProviders({
/* authorize will be called when it receives incoming login req */
async authorize(credentials) {
const client = await clientPromise;
const db = client.db();
/* check if we have user or email */
const usersCollection = await db.collection("users");
const user = await usersCollection.findOne({
$or: [
{ email: credentials.email },
{ userName: credentials.userName }
]
});
if (!user) {
throw new Error("No user found");
}
const isvalid = await verifyPassword(
credentials.password,
user.password
);
if (!isvalid) {
throw new Error("password is invalid");
}
return {
email: user.email
};
}
})
]
});
my login page is as below
import Button from "../../UI/Button/Button";
import Input from "../../UI/Input/Input";
import Card from "../../UI/card/Card";
import classes from "./Login.module.css";
import Link from "next/link";
import { useForm } from "react-hook-form";
import { signIn, getSession } from "next-auth/react";
import { useRouter } from "next/router";
const Login = () => {
const route = useRouter();
const {
register,
handleSubmit,
formState: { errors }
} = useForm();
const submittedFormHandler = async (userInputs) => {
const result = await signIn("credentials", {
redirect: false,
email: userInputs.userNameEmail,
userName: userInputs.userNameEmail,
password: userInputs.password
}); /* result will always resolve */
if (!result.error) {
route.replace("/");
}
};
return (
<>
<Card className={classes.login}>
<form onSubmit={handleSubmit(submittedFormHandler)}>
<Input
htmlFor="userNameEmail"
id="userNameEmail"
label="UserName or Email"
input={{
type: "text",
...register("userNameEmail", { required: true})
}}
></Input>
<span className={classes.spanning}>
{errors.userName &&
"Enter userName or Email at least four characters"}
</span>
<Input
htmlFor="password"
id="password"
label="Enter Password"
input={{
type: "password",
...register("password", { required: true, minLength: 8 })
}}
></Input>
<span className={classes.spanning}>
{errors.password && "password should be at least 8 characters"}
</span>
<div className={classes.password}>
<Button type="submit">Submit</Button>
<Link href="/ForgotPassword">Forgot Password ?</Link>
</div>
<Link href="/NewUser" className={classes.link}>
Create Account New User
</Link>
</form>
</Card>
</>
);
};
export async function getServerSideProps(context) {
const session = await getSession({
req: context.req
}); //returns session obj or null
if (session) {
return {
redirect: {
destination: "/",
permanent: false
}
};
}
return {
props: { session }
};
}
export default Login;
what could be the problem? please assist
I faced the same problem, but i was using mysql as database and i didn't use the auth file middleware that suggest next-auth to handle the providers, instead i created a separated file to handle sequelize (in your case will be the orm with the database you're using).
I fixed it adding dialectModule to the propertys of the class Sequelize
const db = new Sequelize(`${process.env.DB_URI}`, {
database: process.env.DB_NAME,
logging: false,
dialect: "mssql",
dialectModule: require("mysql2"),
});
I also have this problem. They said on the doc that make sure you define NEXTAUTH_URL variable correctly. If you use Vercel to host, then the content of the variable should be only the url without quote. For example, https:project.vercel.app.
If not solved, try changing the [...nextauth].ts file to a more simple version. I got this error when I tried doing things with database in the callback (mongodb in my case) like this
async jwt({ token }) {
let role:Role = 'user'
if (!token.email) throw Error('no email provided with token')
let user = await getUser(token.email)
if (user?.isAdmin) role = 'admin'
return {...token, role}
}
After removing this, my problem is solved. In your case, you could try removing anything that deals with the database.
After I got that working, I added this callback function instead
async jwt({ token }) {
let role:Role = 'user'
if (!token.email) throw Error('no email provided with token')
const client = await clientPromise
const collection = client.db().collection('users')
const user = await collection.findOne({email:token.email})
if (user?.isAdmin) role = 'admin'
return {...token, role}
}
The only difference is that the first one use mongoose, and the second one doesn't. The approach of the second one is taken from https://github.com/vercel/next.js/tree/canary/examples/with-mongodb
Disclaimer: I don't know why it worked.
Related
I have followed the code from the official docs. When you scroll down to the full code, it has two things that make problems. First of all, this seems weird:
const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
const auth = getAuth();
They define auth after using it for recaptchaVerifier. But that seems like a typo, so I just switched these two lines.
But I cannot resolve the second issue. Their code is in JavaScript, my code is in TypeScript. They use undefined as an argument in the definition of recaptchaVerifier:
const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undefined, auth);
The second argument of the constructor is undefined. Since TypeScript does not allow that, I tried many things, for example these:
const undef: any = undefined; const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', undef, auth);
const recaptchaVerifier = new RecaptchaVerifier('recaptcha-container-id', { size: 'invisible' }, auth);
But it ALWAYS gives this error in the console:
ERROR FirebaseError: Firebase: Error (auth/argument-error).
at createErrorInternal (index-0bb4da3b.js:474:41)
at _assert (index-0bb4da3b.js:480:15)
at new RecaptchaVerifier (index-0bb4da3b.js:7369:9)
I could not find anything that helped me fix this error in the internet.
Here is my full code:
LogIn(email: string, password: string) {
const auth = getAuth();
const undef: any = undefined;
const recaptchaVerifier = new RecaptchaVerifier(
'recaptcha-container-id',
undef,
auth
);
/* It never reaches this code below here since new RecaptchaVerifier() always throws an error */
return signInWithEmailAndPassword(auth, email, password)
.then((result) => {
this.afAuth.authState.subscribe((user) => {
if (user) {
this.router.navigate(['home']);
}
});
})
.catch((error) => {
if (error.code == 'auth/multi-factor-auth-required') {
// The user is a multi-factor user. Second factor challenge is required.
const auth = getAuth();
let resolver = getMultiFactorResolver(auth, error);
const phoneInfoOptions = {
multiFactorHint: resolver.hints[0],
session: resolver.session
};
// Send SMS verification code.
const phoneAuthProvider = new PhoneAuthProvider(auth);
phoneAuthProvider.verifyPhoneNumber(phoneInfoOptions, recaptchaVerifier)
.then((verificationId) => {
// verificationId will be needed for sign-in completion.
// Ask user for the SMS verification code via prompt (yeah, very bad UI)
const verificationCode = prompt("Enter the verification code we sent to your number");
if (verificationCode !== null) {
const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
// Complete sign-in.
return resolver.resolveSignIn(multiFactorAssertion);
} else {
this.toast.error("Entered wrong code");
return null;
}
})
.then((userCredential) => {
// User successfully signed in with the second factor phone number.
this.toast.success("Code is correct. Logged in");
this.afAuth.authState.subscribe((user) => {
if (user) {
this.router.navigate(['home']);
}
});
})
.catch((error) => {
console.log(error);
// failed
this.toast.error(error.message);
});
} else if (error.code == 'auth/wrong-password') {
this.toast.error(error.message);
}
});
}
I am using Angular and angularfire. The code above is not called directly from a component, but from a service. That service though is called from my LoginComponent.
Edit. My imports are:
import { Injectable, NgZone } from '#angular/core';
import { AngularFireAuth } from '#angular/fire/compat/auth';
import {
AngularFirestore,
} from '#angular/fire/compat/firestore';
import { Router } from '#angular/router';
import { child, get, getDatabase, ref, set } from "firebase/database";
import { HotToastService } from '#ngneat/hot-toast';
import firebase from "firebase/compat/app";
import { getAuth, getMultiFactorResolver, GoogleAuthProvider, PhoneAuthProvider, PhoneMultiFactorGenerator, RecaptchaVerifier, signInWithEmailAndPassword, signInWithPopup } from 'firebase/auth';
As we talked in the comments for this to work you need an empty div with the passed id, like:
<div id="recaptcha-container-id"></div>
For now, my test site is as follows:
First a user can register an account, after which they will be able to update their profile, since when registering, all they input is their email and password, so their name attribute is still an empty string.
My User model:
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
email: {type: String, required: true},
passwordHash: {type:String, required: true},
firstName: {type:String, default:""},
});
const User = mongoose.model("user", userSchema);
module.exports = User;
Other things like lastName, address, bio, etc will be easily added once I figure this issue out.
After registering, the user will have access to a simple form that allows them to enter a name to create a profile (for now, just the first name).
My CreateProfile.js file that I use as a React component is as follows:
import React, { useContext, useState } from "react";
import axios from "axios";
import AuthContext from "../context/AuthContext";
import { useNavigate } from "react-router-dom";
function CreateProfile() {
const [firstName, setFirstName] = useState("");
const {getLoggedIn} = useContext(AuthContext);
const navigate = useNavigate();
async function createProfile(e) {
e.preventDefault();
try {
const profileData = {
firstName,
};
console.log(profileData)
await axios.post("http://localhost:5000/auth/createProfile", profileData);
await getLoggedIn();
navigate("/");
} catch (error) {
console.error(error);
}
}
return (
<div>
<h1>Create your profile</h1>
<form onSubmit={createProfile}>
<input type="text"
placeholder="First Name"
onChange={(e) => setFirstName(e.target.value)}
value={firstName}
/>
<button type="submit">Create Profile</button>
</form>
</div>
);
}
export default CreateProfile;
When the "Create Profile" button gets clicked, you get automatically rerouted back to the temporary "home page" via navigate("/"), a feature that does work correctly at the moment.
The profileData gets sent into the userRouter, who's relevant code is as follows:
router.post("/createProfile", async (req,res) => {
try {
const updatedUser = await User.findOneAndUpdate(
{ $set: {firstName: req.body.firstName} },
{ new: false }
);
res.json(updatedUser);
} catch (error) {
console.error(error);
res.status(500).send();
}
});
And just to make sure I handled my route setup correctly, here's my server-side index.js:
const express = require("express");
const mongoose = require("mongoose");
const dotenv = require("dotenv");
const cookieParser = require("cookie-parser");
const cors = require("cors");
dotenv.config();
//set up server
const app = express();
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port: ${PORT}`));
app.use(express.json());
app.use(cookieParser());
app.use(cors({
origin: ["http://localhost:3000"],
credentials: true,
}));
//remove the warning
mongoose.set('strictQuery', true)
//connect to mongoDB
mongoose.connect(process.env.MDB_CONNECT, {
useNewUrlParser: true,
useUnifiedTopology: true,
}, (err) => {
if (err) return console.error(err);
console.log("Connected to MongoDB");
});
// set up routes
app.use("/auth", require("./routers/userRouter"));
And one more, here's the front end AuthContext.js to handle checking if the user is logged in:
import React, { createContext, useEffect, useState } from "react";
import axios from "axios";
const AuthContext = createContext();
function AuthContextProvider(props) {
const [loggedIn, setLoggedIn] = useState(undefined);
async function getLoggedIn() {
const loggedInRes = await axios.get("http://localhost:5000/auth/loggedIn");
setLoggedIn(loggedInRes.data);
}
useEffect(() => {
getLoggedIn();
}, []);
return <AuthContext.Provider value={{loggedIn, getLoggedIn}}>
{props.children}
</AuthContext.Provider>;
};
export default AuthContext;
export {AuthContextProvider};
What did I do wrong that is preventing MongoDB from picking up the updated firstName? When I enter "test" in the form and click the submit button, the object does get created but doesn't go to MongoDB. What should I do instead, and is my problem in the userRouter or somewhere else?
I've been looking around online for previously asked questions similar to this and tried a few different things in my userRouter.js file, before I ended up with I currently have (that still doesn't work), to no avail.
Attempt 1:
router.post("/createProfile", async (req,res) => {
var _id = req.body._id;
console.log(_id);
var profile = {
firstName: req.body.firstName,
}
User.findByIdAndUpdate(_id, req.body.firstName, {new:false}, function(
err,
profile
) {
if (err) {
console.log("err", err);
res.status(500).send(err);
} else {
console.log("success");
console.log(profile);
console.log(_id);
console.log(req.body.email);
res.send(profile);
}
});
});
I've also tried instead doing res.send(req.body.firstName) which is pretty silly and obviously didn't work (if you couldn't already tell, I'm new to this).
I've also tried:
try {
const profile = await User.create(req.body);
console.log("success");
res.send(profile);
} catch (error) {
console.log(req.body);
console.log("err", error);
res.status(500).send(error);
}
Which definitely did not work.
And finally,
try {
const {firstName} = req.body;
const test = User.updateOne(firstName);
res.send(test);
} catch (error) {
console.error(error);
res.status(500).send();
}
From other answers, I've seen things where people would do res.json() or res.send() or res.put() but I'm not sure which one I should be doing in my situation, though I doubt that's the root cause of my issue.
I'd appreciate any help, if you need any additional context or code I'll edit my post accordingly. My registration/login/logout stuff is working fine, I just can't for the life of me seem to create the post-registration profile creation functionality correctly.
To update a document previously created you will need to add the _id in the body or in the path params of the route. So you will need to add some logic in the front-end to retrieve the logged user's id.
// ... all imports
router.post('/createProfile', async (req, res) => {
const {_id, firstName} = req.body;
try {
const updatedUser = await User.findByIdAndUpdate(
_id,
{firstName: firstName},
{new: true}, // "new: true" or "returnOriginal: false"
);
res.status(200).json(updatedUser);
} catch (error) {
// ...
}
});
The function findByIdAndUpdate need the id, the new values and optionally a options object (https://mongoosejs.com/docs/api.html#model_Model-findByIdAndUpdate). "You should set the new option to true to return the document after update was applied".
You can also do this operation using findOneAndUpdate:
const updatedUser = await User.findOneAndUpdate(
{_id: new mongoose.Types.ObjectId(req.body._id)},
{$set: {firstName: req.body.firstName}},
{new: true}
);
EDIT1:
In the front-end you will need to retrieve the logged user's id. I think that this data is inside the return of the "getLoggedIn" function which is called You need to use your "loggedIn" data.
// ... all imports
function CreateProfile() {
const navigate = useNavigate();
const [firstName, setFirstName] = useState('');
const {
loggedIn, // Get the "loggedIn" user data
getLoggedIn
} = useContext(AuthContext);
async function createProfile(e) {
e.preventDefault();
try {
/**
* I think that the user is already logged in to fill the "first name",
* the "getLoggedIn" was already called and "loggedIn" has some data inside.
*/
const id = loggedIn.id;
await axios.post('http://localhost:5000/auth/createProfile', {
id,
firstName,
});
await getLoggedIn();
navigate('/');
} catch (error) {
console.error(error);
}
}
return (
<div>
<h1>Create your profile</h1>
<form onSubmit={createProfile}>
<input
type='text'
placeholder='First Name'
onChange={e => setFirstName(e.target.value)}
value={firstName}
/>
<button type='submit'>Create Profile</button>
</form>
</div>
);
}
I have already an application built with Node Js and express , also I have a front end using create-react-app with redux , but now I would like to move it to next Js, but I've got stuck because I do not the right way to authenticate and authorize using my rest Full API, I want to mention that my API already handle this using JWT (saving it within a cookie)
Next-Auth is a go-to in such a case. The following example shows how to get started with password authentication.
Create a file called /pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
const options = {
providers: [
Providers.Credentials({
// The name to display on the sign in form (e.g. 'Sign in with...')
name: 'Email and Password',
credentials: {
username: { label: "Username", type: "text", placeholder: "jsmith" },
password: { label: "Password", type: "password" }
},
authorize: async (credentials) => {
// Add logic here to look up the user from the credentials supplied eg from db or api
const user = { id: 1, name: 'J Smith', email: 'jsmith#example.com' }
if (user) {
// Any object returned will be saved in `user` property of the JWT
return Promise.resolve(user)
} else {
// If you return null or false then the credentials will be rejected
return Promise.resolve(null)
// You can also Reject this callback with an Error or with a URL:
// return Promise.reject(new Error('error message')) // Redirect to error page
// return Promise.reject('/path/to/redirect') // Redirect to a URL
}
}
}),
],
}
export default (req, res) => NextAuth(req, res, options)
Then on your component code:
import React from 'react'
import {
signIn,
signOut,
useSession
} from 'next-auth/client'
export default function myComponent() {
const [ session, loading ] = useSession()
return <>
{!session && <>
Not signed in <br/>
<button onClick={signIn}>Sign in</button>
</>}
{session && <>
Signed in as {session.user.email} <br/>
<button onClick={signOut}>Sign out</button>
</>}
</>
}
This would easily get you started, if you shared more of the auth routes you have I can advise more.
I use next-auth.js library to create authentication system for Next.js my application.
Everything works fine on my local computer, but after deploying website to Vercel, my sign in function doesn't work.
The sign in page (/pages/signin) loads fine, i submit credentials and i get an error like this:
https://projectname-git-branch.username.now.sh/api/auth/error?error=Error%3A%20connect%20ECONNREFUSED%20127.0.0.1%3A80.
I found many articles that error is about to wrong baseURL configured for axios, but i have many axios requests everywhere in code (for example i get data from firebase after app loads) and it works without any problem.
I console logged environment variable where i set baseURL property for axios and it's fine.
// _app.js
import axios from 'axios';
axios.defaults.baseURL = process.env.FIREBASE_APIURL;
console.log('axios-baseurl', axios.defaults.baseURL) // it prints right url
More informations about this problem i wrote there: #846
I really don't know what to do. I don't get any information about where this error happens, which file and line number, what request in my code.
Below i put my whole [...nextauth.js] file and sign in form. Maybe there is something what i should to configure better there.
// [...nextauth.js]
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import axios from 'axios';
axios.defaults.baseURL = process.env.FIREBASE_APIURL;
console.log('axios-baseurl', axios.defaults.baseURL)
const sha1 = require('crypto-js/sha1');
const md5 = require('crypto-js/md5');
export default (req, res) =>
NextAuth(req, res, {
providers: [
Providers.Credentials({
name: 'Credentials',
credentials: {
phone: { label: "Numer telefonu", type: "text" },
password: { label: "Haslo", type: "password" }
},
authorize: async (loginData) => {
const { csrfToken, phone, password } = loginData;
// checking if there is account with these credentials
let res = await login({
phone,
password: sha1(md5(password)).toString()
})
// 200 = OK
if(res.status == 200){
// check if account was activated
if(res.data.info.activated == "true"){
// collect account data
const user = {
phone,
...res.data.info
}
return Promise.resolve(user);
}
else{
// account is not activated by sms code
return Promise.reject(new Error("AccountNotActivated"));
}
}
else {
// wrong credentials
return Promise.reject(new Error("WrongCredentials"));
}
}
})
],
callbacks: {
jwt: async (token, user, account, profile, isNewUser) => {
user && (token.user = user);
return Promise.resolve(token)
},
session: async (session, user, sessionToken) => {
session.user = user.user;
return Promise.resolve(session)
}
},
pages: {
signIn: '/signin'
},
site: process.env.NEXTAUTH_URL || "localhost:3000",
debug: true
})
// function which sends login request to rest api and returns result
// https://stackoverflow.com/q/64244115
const login = async data => await axios.post('/checkCredentials', data);
<!-- /pages/signin.js -->
<form action="/api/auth/callback/credentials" method="post">
<input name='csrfToken' type='hidden' defaultValue={csrfToken} />
<label>
Numer telefonu: <br />
<input name='phone' type='text' required autoComplete='off' />
</label>
<br />
<label>
Hasło: <br />
<input name='password' type='password' required autoComplete='off' />
</label>
<br />
<br />
<input type="submit" value="Zaloguj" />
</form>
You'll need to set environment variables for production use:
NEXTAUTH_URL (e.g. NEXTAUTH_URL='https://example.com'
https://next-auth.js.org/configuration/options#nextauth_url
and
NEXTAUTH_SECRET (e.g. NEXTAUTH_SECRET='MYGENERATEDOPENSSLSECRET')
documentation # https://next-auth.js.org/configuration/options#nextauth_secret
You can generate a secret on the command line (only one possible way)
openssl rand -base64 32
Add both variables to your vercel deployment:
https://vercel.com/docs/concepts/projects/environment-variables
I am trying to make a login for a website. I am trying to send a http request with axios, to save the inputted data by the user, name, password etc and save it to a database.
When I make the request with postman it works but when I try to make the request on the client side it won't.
I am getting this error from the console:
Error:
("Network Error"
createError createError.js:17
handleError xhr.js:80
index.js:1375
e index.js:1375
onSubmit Register.js:39)
I have tried various things and tried to scrape the web for the answer, but I cant find anything.If you could give me any help that would be great.
I have tried using local host 3000 and now the local.
Thanks!!!!!
Heres the code
import React, { useState } from 'react';
import '../../App';
import axios from 'axios';
const Register = () => {
const [formData, gatherData] = useState({
name: '',
email: '',
password: '',
paswrd: ''
});
const { name, email, password, paswrd } = formData;
//on submit checks if passwords are valid, if they are valid create a new instance of user
const onSubmit = async element => {
element.preventDefault();
if (password !== paswrd) {
console.log("Passwords are not matching ")
}
else {
const newUser = {
name,
email,
password
}
try {
const configuration = {
headers: {
'Content-Type': 'application/json'
}
}
const body = JSON.stringify(newUser);
const res = await axios.post('http://localhost:5000/apis/user', body, configuration);
console.log(res.data);
} catch (err) {
console.error(err)
}
}
}
export default Register;
Try to add cors on your server
const cors = require('cors');
app.use(cors());