I am trying to implement NextAuth with Credentials (email and password).
I have set up my mongodb for this. I also set up /api/proile/ route to post login credentials and tested it out with Postman, returns user correctly.
But the problem starts with after logging in. when I log in, credentials return in the vscode terminal (I console log in /api/auth/[...nextauth].js with console.log(credentials) but in the browser it authorizes the user, i can access and view protected routes and stuff, but when I log the session in front-end, it displays as null for user information as you can see in the picture below;
here is my /api/auth/[...nextauth].js code;
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
import { connectToDatabase } from '../../../util/mongodb';
const options = {
providers: [
Providers.Credentials({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials, req) {
console.log(credentials);
const res = await fetch('http://localhost:3000/api/profile/', {
method: 'POST',
body: JSON.stringify(credentials),
headers: {
'Content-Type': 'application/json',
},
});
const user = await res.json();
// const user = { id: '1', name: 'Suat Bayrak', email: 'test#test.com2' };
if (user) {
return user;
} else {
return null;
}
},
}),
],
pages: {
signIn: '/signin',
},
session: {
jwt: true,
maxAge: 30 * 24 * 60 * 60,
updateAge: 24 * 60 * 60,
},
database: `mongodb+srv://${process.env.MONGO_DB_USERNAME}:${process.env.MONGO_DB_PASSWORD}#nextjs-academia- sb.ki5vd.mongodb.net/test`,
};
export default (req, res) => NextAuth(req, res, options);
by the way, when I use static user credentials like i commented in the code above, it works perfectly fine and returns session info correctly...
Also here is my /pages/signin.js
import { signIn } from 'next-auth/client';
import { useState } from 'react';
import { useRouter } from 'next/router';
export default function SignIn() {
const router = useRouter();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loginError, setLoginError] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
console.log('submitted!');
signIn('credentials', {
email: email,
password: password,
// email: 'test#test.com',
// password: '1234',
callbackUrl: 'http://localhost:3000/about',
redirect: false,
}).then(function (result) {
console.log(result);
if (result.error !== null) {
if (result.status === 401) {
setLoginError(
'Your username/password combination was incorrect. Please try again'
);
} else {
setLoginError(result.error);
}
} else {
console.log(result);
router.push(result.url);
}
});
};
return (
<form onSubmit={handleSubmit}>
<label>
Email
<input
name='email'
type='text'
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<label>
Password
<input
name='password'
type='password'
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</label>
<button type='submit'>Sign in</button>
</form>
);
}
also here is my github repo for this whole code;
GitHub Repo
UPDATE: At first login, user info displays on the terminal but after I refresh the page, it says undefined
import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';
const options = {
providers: [
Providers.Credentials({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials, req) {
console.log(credentials);
let user;
const res = await fetch('http://localhost:3000/api/profile/', {
method: 'POST',
body: JSON.stringify(credentials),
headers: {
'Content-Type': 'application/json',
},
});
const arrayToJson = await res.json();
user = arrayToJson[0];
if (user) {
return user;
} else {
return null;
}
},
}),
],
pages: {
signIn: '/signin',
},
session: {
jwt: true,
maxAge: 30 * 24 * 60 * 60,
updateAge: 24 * 60 * 60,
},
callbacks: {
async signIn(user) {
return user.userId && user.isActive === '1';
},
async session(session, token) {
session.user = token.user;
return session;
},
async jwt(token, user) {
if (user) token.user = user;
return token;
},
},
database: `mongodb+srv://${process.env.MONGO_DB_USERNAME}:${process.env.MONGO_DB_PASSWORD}#nextjs-academia-sb.ki5vd.mongodb.net/test`,
};
export default (req, res) => NextAuth(req, res, options);
so the problem was at callbacks section of the code. Now its working fine
Related
i'm pretty new in MEAN STACK.
I make my API REST under Nodejs Express and mongoodb.
I use Angular 14 as client.
I made a little project where user can register / login / display user informations.
Now i'd like to add an update functionality. A simple form where user can update his name or his role. I'm stuck at this point and i'd like to know why my code don't work and how can i fix this. It's been 2 days i'm on it, that's why i'm there to have some informations.
This is my config.json
{
"development": {
"PORT": 3000,
"MONGODB_URI": "mongodb://localhost:27017/PangolinDB",
"JWT_SECRET": "SECRET#123",
"JWT_EXP": "10m"
},
"production": {
"PORT": 80,
"MONGODB_URI": "mongodb://xxxxx/DB_Name",
"JWT_SECRET": "SECRET#123",
"JWT_EXP": "2m"
}
}
user.controller.js
const mongoose = require('mongoose');
const passport = require('passport');
const _ = require('lodash');
const { rest } = require('lodash');
const User = mongoose.model('User');
// updated crud code
ObjectId = require('mongoose').Types.ObjectId; //catch collection Id
// end updated crud code
module.exports.register = (req, res, next) => {
//console.log('inside register function');
var user = new User();
user.fullName = req.body.fullName;
user.email = req.body.email;
user.password = req.body.password;
user.role = req.body.role;
user.followers = req.body.followers;
user.following = req.body.following;
user.save((err, doc) => {
if (!err)
res.send(doc);
else {
if (err.code = 1100)
res.status(422).send(['L\'adresse email existe déja.']);
else
return next(err);
}
});
}
module.exports.authenticate = (req, res, next) => {
//call for passport authentication
passport.authenticate('local', (err, user, info) => {
//error from passport middleware
if (err) return res.status(404).json(err);
//registered user
else if(user) return res.status(200).json({ "token" : user.generateJwt() });
//unknow user or wrong password
else return res.status(401).json(info);
})(req,res);
}
module.exports.userProfile = (req, res, next) => {
User.findOne({ _id: req._id },
(err, user) => {
if (!user)
return res.status(404).json({ status: false, message: 'User record not found'});
else
return res.status(200).json({ status: true, user: _.pick(user,['fullName',
'email', 'role', 'friends','followers','following']) });
})
}
module.exports.getAllUser = async (req, res) => {
const users = await User.find().select('-password');
res.status(200).json(users);
};
module.exports.userInfo = (req, res) => {
if (!ObjectId.isValid(req.params.id))
return res.status(400).send('ID unknown :' + req.params.id)
User.findById(req.params.id, (err,docs) => {
if(!err) res.send(docs);
else console.log('ID unknown ; ' . err);
}) .select('-password');
};
module.exports.updateUser = async (req, res) => {
if (!ObjectId.isValid(req.params.id))
return res.status(400).send('ID unknown :' + req.params.id)
try {
await User.findOneAndUpdate(
{_id: req.params.id},
{
$set: {
role : req.body.role
},
},
{ new: true, upsert: true, setDefaultsOnInsert: true},
)
.then((docs) => res.send(docs))
.catch((err) => res.status(500).send({ message: err }));
} catch (err){
return res.status(500).json({ message: err});
}
}
module.exports.deleteUser = async (req, res) => {
if (!ObjectId.isValid(req.params.id))
return res.status(400).send('ID unknown :' + req.params.id)
try {
await User.remove({_id: req.params.id}).exec();
res.status(200).json({ message: "Successfully deleted. "});
} catch (err) {
return res.status(500).json({ message: err });
}
}
module.exports.follow = async (req, res) => {
/* if (!ObjectId.isValid(req.params.id) || !ObjectId.isValid(req.params.idToFollow))
return res.status(400).send('ID unknown :' + req.params.id) */
try {
// add to the follower list
await User.findByIdAndUpdate(
req.params.id, // id wanted to being followed
{ $addToSet: { following: req.body.idToFollow } }, // id being followed
{ new : true, upsert: true },
);
// add to following list
await User.findByIdAndUpdate(
req.body.idToFollow,
{ $addToSet: { followers: req.params.id}},
{ new: true, upsert: true },
)
return res.status(201).json(docs);
}catch (err) {
return res.status(500).json({ message: err });
}
};
module.exports.unfollow = async (req, res) => {
/* if (!ObjectId.isValid(req.params.id) || !ObjectId.isValid(req.params.idToFollow))
return res.status(400).send('ID unknown :' + req.params.id) */
try {
// add to the follower list
await User.findByIdAndUpdate(
req.params.id, // id wanted to being followed
{ $pull: { following: req.body.idToUnFollow } }, // id being followed
{ new : true, upsert: true },
);
// add to following list
await User.findByIdAndUpdate(
req.body.idToUnFollow,
{ $pull: { followers: req.params.id}},
{ new: true, upsert: true },
)
return res.status(201).json(docs);
}catch (err) {
return res.status(500).json({ message: err });
}
};
user.model.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
var userSchema = new mongoose.Schema({
fullName: {
type: String,
required: 'Full name can\'t be empty'
},
email: {
type: String,
required: 'Email can\'t be empty',
unique: true
},
password: {
type: String,
required: 'Password can\'t be empty',
minlength: [4, 'Password must be atleast 4 character long']
},
role: {
type: String,
required: 'Role can\'t be empty',
},
followers: {
type: String,
},
following: {
type: String,
},
saltSecret: String
});
// Custom validation for email
userSchema.path('email').validate((val) => {
emailRegex = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return emailRegex.test(val);
}, 'Invalid e-mail.');
// Events encrypt password
userSchema.pre('save', function (next) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(this.password, salt, (err, hash) => {
this.password = hash;
this.saltSecret = salt;
next();
});
});
});
// Methods verify password
userSchema.methods.verifyPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
userSchema.methods.generateJwt = function () {
return jwt.sign({ _id: this._id},
process.env.JWT_SECRET,
{
expiresIn: process.env.JWT_EXP
});
}
mongoose.model('User', userSchema);
routes.js
const express = require('express');
const router = express.Router();
const ctrlUser = require('../controllers/user.controller');
const jwtHelper = require('../config/jwtHelper');
//auth
router.post('/register', ctrlUser.register,);
router.post('/authenticate',ctrlUser.authenticate);
// user display
router.get('/userProfile',jwtHelper.verifyJwtToken, ctrlUser.userProfile);
router.get('/list', ctrlUser.getAllUser);
router.get('/user/:id', ctrlUser.userInfo);
router.put("/user/:id",ctrlUser.updateUser);
router.delete("/user/:id",ctrlUser.deleteUser);
//follow
router.patch('/user/follow/:id', ctrlUser.follow);
router.patch('/user/unfollow/:id', ctrlUser.unfollow);
module.exports = router;
index.js
require('./config/config');
require('./models/db');
require('./config/passportConfig');
require('./routes/index.router')
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const passport = require('passport');
//updated code
// end updtated code
const rtsIndex = require('./routes/index.router');
var app = express();
// middleware
app.use(bodyParser.json());
app.use(cors());
app.use(passport.initialize());
app.use('/api', rtsIndex);
//
/* app.use('api',routeList); */
//
// error handler
app.use((err, req, res, next) => {
if (err.name === 'ValidationError') {
var valErrors = [];
Object.keys(err.errors).forEach(key => valErrors.push(err.errors[key].message));
res.status(422).send(valErrors)
}
else{
console.log(err);
}
});
// start server
app.listen(process.env.PORT, () => console.log(`Server started at port :
${process.env.PORT}`));
user.service.ts (angular 14)
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse, } from '#angular/common/http';
import { User } from './user.model';
import { Observable, ObservableInput, throwError} from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { environment } from 'src/environments/environment';
#Injectable({
providedIn: 'root'
})
export class UserService {
selectedUser: User = {
id:'',
fullName: '',
email: '',
password: '',
role:'',
friends:'',
followers:'',
following:'',
};
noAuthHeader = { headers: new HttpHeaders({ 'NoAuth': 'True'}) };
errorMgmt: (err: any, caught: Observable<any>) => ObservableInput<any>;
HttpHeaders: HttpHeaders | { [header: string]: string | string[]; };
constructor(private http: HttpClient) { }
//httpMethods
//user form
//register ()
postUser(user: User){
return this.http.post( environment.apiBaseUrl+'/register',user,this.noAuthHeader);
}
//login ()
login(authCredentials: any){
return this.http.post(environment.apiBaseUrl + '/authenticate', authCredentials,
this.noAuthHeader);
}
//user profile
//user infos
getUserProfile(){
return this.http.get(environment.apiBaseUrl + '/userProfile');
} // need jwt in header
getOne(){
const token = this.getToken();
const headers = new HttpHeaders({'Authorization ':'bearer'+ token});
return this.http.get(environment.apiBaseUrl+'/userProfile',{headers});
}
updateUser(_id: any, value: any){
return this.http.put(environment.apiBaseUrl + `/user/${_id}`, value);
}
edit(_id: any, data: any){
return this.http.put(environment.apiBaseUrl + `/user/${_id}`, data);
}
//get all users
getAllUsers(){
return this.http.get(environment.apiBaseUrl + '/list');
}
//Helper Methods
setToken(token: string) {
localStorage.setItem('token', token);
}
getToken() {
return localStorage.getItem('token');
}
deleteToken() {
localStorage.removeItem('token');
}
getUserPayload(){
var token = this.getToken();
if (token) {
var getUserPayload = atob(token.split('.')[1]);
return JSON.parse(getUserPayload);
}
else
return null;
}
isLoggedIn() {
var userPayload = this.getUserPayload();
if (userPayload)
return userPayload.exp > Date.now() / 1000;
else
return false;
}
// Error
handleError(error: HttpErrorResponse) {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Handle client error
errorMessage = error.error.message;
} else {
// Handle server error
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
console.log(errorMessage);
return throwError(() => {
errorMessage;
});
}
}
edit.component.ts
import { UserService } from './../shared/user.service';
import { Component, OnInit, NgZone } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { FormGroup, FormControl, Validator, Validators, SelectControlValueAccessor,
FormBuilder } from "#angular/forms";
#Component({
selector: 'app-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.css']
})
/* export class EditComponent implements OnInit{
editUsr = new FormGroup({
fullName: new FormControl(null, [Validators.required]),
email: new FormControl(null, [Validators.required]),
role: new FormControl(null, [Validators.required])
});
loading: boolean =true;
id: any;
constructor (
private userService : UserService,
private activateRoute : ActivatedRoute,
private route: Router
){}
ngOnInit(): void {
this.activateRoute.params.subscribe((data: any) =>{
this.id = data['id'];
this.userService.getOne(this.id).subscribe((res: any)=>{
if (res.user != undefined){
this.editUsr.get('fullName')?.setValue(res.user.fullName);
this.editUsr.get('email')?.setValue(res.user.email);
this.editUsr.get('role')?.setValue(res.user.role);
}
});
this.loading = false;
});
}
addUser(){
const data: any = {
fullName : this.editUsr.get('fullName')?.value,
email : this.editUsr.get('email')?.value,
role: this.editUsr.get('role')?.value
}
this.userService.edit(this.id, data).subscribe(()=>this.route.navigateByUrl('edit'));
}
}
*/
export class EditComponent implements OnInit {
getId:any;
updateForm: FormGroup;
constructor(
public formBuilder: FormBuilder,
private router: Router,
private ngZone: NgZone,
private activatedRoute: ActivatedRoute,
private UserService: UserService
) {
this.getId = this.activatedRoute.snapshot.paramMap.get('id');
this.UserService.getOne().subscribe(res=>{
this.updateForm.patchValue({
fullName: res['fullName'],
email: res['email'],
role: res['role']
});
});
this.updateForm = this.formBuilder.group({
fullName:[''],
email:[''],
role:[''],
})
}
ngOnInit(): void {}
onUpdate(): any{
this.UserService.updateUser(this.getId, this.updateForm.value).subscribe(()=>{
console.log('mise à jouer effectuée avec succès')
this.ngZone.run(()=> this.router.navigateByUrl('/userprofile'))
},(err)=> {
console.log(err);
});
}
}
edit.component.html
<div class="row justify-content-center mt-5">
<div class="col-md-4">
<form [formGroup]="updateForm" (ngSubmit)="onUpdate()">
<h2>Edit User</h2>
<div class="form-group">
<label for="fullName">FullName</label>
<input class="form-control" type="text" formControlName="fullName" id="fullName" >
</div>
<div class="form-group">
<label for="email">Email</label>
<input class="form-control" type="text" formControlName="email" id="email">
</div>
<div class="form-group">
<label for="role">Role</label>
<input class="form-control" type="text" formControlName="role" id="role">
</div>
<div class="form-group">
<button class="btn btn-primary btn-block" type="submit" >Enregistrer</button>
<button class="btn btn-info btn-block" [routerLink]="['/userprofile']"
type="submit">Retour</button>
</div>
</form>
</div>
</div>
<p>edit works!</p>
environment.ts
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
apiBaseUrl: 'http://localhost:3000/api'
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative
impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
routes.ts
import { EditComponent } from './edit/edit.component';
import { HomeComponent } from './home/home.component';
import { Routes } from "#angular/router";
import { UserComponent } from "./user/user.component";
import { SignUpComponent } from "./sign-up/sign-up.component";
import { SignInComponent } from "./user/sign-in/sign-in.component";
import { UserProfileComponent } from "./user-profile/user-profile.component";
import { AuthGuard } from "./auth/auth.guard";
export const appRoutes: Routes = [
{path:'home', component: HomeComponent},
{path:'edit', component:EditComponent},
{
path: 'signup', component: UserComponent,
children: [{ path:'', component: SignUpComponent}]
},
{
path: 'login', component: UserComponent,
children: [{ path:'', component: SignInComponent}]
},
{
path: 'userprofile', component: UserProfileComponent,canActivate:[AuthGuard]
},
{
path: '', redirectTo: '/login', pathMatch: 'full'
}
];
thanks in advance for the help :)
I'm trying to set the cookie from my express backend to my nextjs front end using the response.setHeader, but it's not working, I get my json response but no cookies are set, I used postman to make some tests and in postman it did set the cookies as it should be.
NextJs version is 13.0.2, express version is 4.18.2 and cookie version is 0.5.0
My express server:
export const loginUser = async (req: Request, res: Response) => {
const { email, password } = req.body;
//limpa os cookies
res.clearCookie("accessToken", { httpOnly: true });
if (email !== "" && password !== "") {
try {
const foundUser: any = await prisma.users.findFirst({
where: {
email,
password,
},
});
try {
const accessToken = jwt.sign(
{ id: foundUser.id, email },
"dspaojdspoadsaodksa",
{
expiresIn: "10s",
}
);
const refreshToken = jwt.sign(
{ id: foundUser.id, email },
"dsaoindsadmnsaosda",
{
expiresIn: "50s",
}
);
const savedRefresh = await prisma.refresh_token.upsert({
where: {
users_id: foundUser.id,
},
update: {
access_token: accessToken,
refresh_tk: refreshToken,
users_id: foundUser.id,
},
create: {
access_expires_in: "",
refresh_expires_in: "",
access_token: accessToken,
refresh_tk: refreshToken,
users_id: foundUser.id,
},
});
res.setHeader(
"Set-Cookie",
cookie.serialize("accessToken", accessToken, {
maxAge: 1000 * 60 * 15, //15 minutes
httpOnly: true, // The cookie only accessible by the web server
})
);
res.json({ accessToken, user: { email }, ok: true });
} catch (error) {
console.log("refresh cant be created");
res.send({ message: "refresh cant be created" });
}
} catch (error) {
res.send({ message: "user not found" });
}
} else {
res.json({ message: "token creation failed" });
}
};
My nextJs front-end:
const handleLogin = async (event: React.SyntheticEvent) => {
event.preventDefault();
const email = (event?.target as any).email.value;
const password = (event?.target as any).password.value;
if (email === "" || password === "") {
setError(true);
return;
}
const data = {
email,
password,
};
const resp = await fetch("http://localhost:5000/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const userData = await resp.json();
console.log(userData);
};
Scenario : When I create/register user1 ,the verification mail is sent to that email id and he(user1) is being verified successfully and I am able to change password for user1. After creating user1 , I am creating/registering user2 ,where the verification email is sent to the account .After clicking the link , it's becomes INVALID Overall , I am only able to create one user
Languages used : MERN stack
Backend => route.js :
const express = require("express");
const router = express.Router();
const User = require("../models/userModel");
const Doctor = require("../models/doctorModel");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const authMiddleware = require("../middlewares/authMiddleware");
const sendEmail = require("../utils/sendMail");
const Token = require("../models/tokenModel");
const Appointment = require("../models/appointmentModel");
const moment = require("moment");
router.post("/register", async (req, res) => {
try {
const userExists = await User.findOne({ email: req.body.email });
if (userExists) {
return res
.status(200)
.send({ message: "User already exists", success: false });
}
const password = req.body.password;
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
req.body.password = hashedPassword;
const newuser = new User(req.body);
const result = await newuser.save();
await sendEmail(result, "verifyemail");
res
.status(200)
.send({ message: "User created successfully", success: true });
} catch (error) {
console.log(error);
res
.status(500)
.send({ message: "Error creating user", success: false, error });
}
});
router.post("/login", async (req, res) => {
try {
const result = await User.findOne({ data: req.body.userId });
console.log(result);
const user = await User.findOne({ email: req.body.email });
if (!user) {
return res
.status(200)
.send({ message: "User does not exist", success: false });
}
if (user.isVerified === false) {
return res
.status(200)
.send({ message: "User not Verified", success: false });
}
const isMatch = await bcrypt.compare(req.body.password, user.password);
if (!isMatch) {
return res
.status(200)
.send({ message: "Password is incorrect", success: false });
} else {
const dataToBeSentToFrontend = {
id: user._id,
email: user.email,
name: user.name,
};
const token = jwt.sign(dataToBeSentToFrontend, process.env.JWT_SECRET, {
expiresIn: "1d",
});
res
.status(200)
.send({ message: "Login successful", success: true, data: token });
}
} catch (error) {
console.log(error);
res
.status(500)
.send({ message: "Error logging in", success: false, error });
}
});
router.post("/get-user-info-by-id", authMiddleware, async (req, res) => {
try {
const user = await User.findOne({ _id: req.body.userId });
user.password = undefined;
if (!user) {
return res
.status(200)
.send({ message: "User does not exist", success: false });
} else {
res.status(200).send({
success: true,
data: user,
});
}
} catch (error) {
res
.status(500)
.send({ message: "Error getting user info", success: false, error });
}
});
router.post("/send-password-reset-link", async (req, res) => {
try {
const result = await User.findOne({ email: req.body.email });
await sendEmail(result, "resetpassword");
res.send({
success: true,
message: "Password reset link sent to your email successfully",
});
} catch (error) {
res.status(500).send(error);
}
});
router.post("/resetpassword", async (req, res) => {
try {
const tokenData = await Token.findOne({ token: req.body.token });
if (tokenData) {
const password = req.body.password;
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
await User.findOneAndUpdate({
_id: tokenData.userid,
password: hashedPassword,
});
await Token.findOneAndDelete({ token: req.body.token });
res.send({ success: true, message: "Password reset successfull" });
} else {
res.send({ success: false, message: "Invalid token" });
}
} catch (error) {
res.status(500).send(error);
}
});
router.post("/verifyemail", async (req, res) => {
try {
const tokenData = await Token.findOne({ token: req.body.token });
if (tokenData) {
await User.findOneAndUpdate({ _id: tokenData.userid, isVerified: true });
await Token.findOneAndDelete({ token: req.body.token });
res.send({ success: true, message: "Email Verified Successlly" });
} else {
res.send({ success: false, message: "Invalid token" });
}
} catch (error) {
res.status(500).send(error);
}
});
Backend => sendEmail.js :
const nodemailer = require("nodemailer");
const bcrypt = require("bcrypt");
const Token = require("../models/tokenModel");
module.exports = async (user, mailType) => {
try {
const transporter = nodemailer.createTransport({
service: "gmail",
host: "smtp.gmail.com",
port: 587,
secure: true,
auth: {
user: "sh***********th#gmail.com",
pass: "e**************l",
},
});
const encryptedToken = bcrypt
.hashSync(user._id.toString(), 10)
.replaceAll("/", "");
const token = new Token({
userid: user._id,
token: encryptedToken,
});
await token.save();
let mailOptions, emailContent;
if (mailType === "verifyemail") {
emailContent = `<div><h1>Please click on the below link to verify your email address</h1> ${encryptedToken} </div>`;
mailOptions = {
from: "sh************th#gmail.com",
to: user.email,
subject: "Verify Email For MERN Auth",
html: emailContent,
};
} else {
emailContent = `<div><h1>Please click on the below link to reset your password</h1> ${encryptedToken} </div>`;
mailOptions = {
from: "shanshangeeth#gmail.com",
to: user.email,
subject: "Reset Password",
html: emailContent,
};
}
await transporter.sendMail(mailOptions);
} catch (error) {
console.log(error);
}
};
Backend => authMiddleware.js :
const jwt = require("jsonwebtoken");
module.exports = async (req, res, next) => {
try {
const token = req.headers["authorization"].split(" ")[1];
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
return res.status(401).send({
message: "Auth failed",
success: false,
});
} else {
req.body.userId = decoded.id;
next();
}
});
} catch (error) {
return res.status(401).send({
message: "Auth failed",
success: false,
});
}
};
Backend => tokenmodel.js :
const mongoose = require("mongoose");
const tokenSchema = new mongoose.Schema(
{
userid: {
type: String,
required: true,
},
token: {
type: String,
required: true,
},
},
{ timestamps: true }
);
const tokenModel = mongoose.model("tokens", tokenSchema);
module.exports = tokenModel;
Backend => userModel.js
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
isVerified: {
type: Boolean,
default: false,
},
isDoctor: {
type: Boolean,
default: false,
},
isDoctor: {
type: Boolean,
default: false,
},
isAdmin: {
type: Boolean,
default: false,
},
seenNotifications: {
type: Array,
default: [],
},
unseenNotifications: {
type: Array,
default: [],
},
},
{
timestamps: true,
}
);
const userModel = mongoose.model("users", userSchema);
module.exports = userModel;
Frontend => VerifyEmail.js
path="/verifyemail/:token" in app.js inside PublicRoute
import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { toast } from "react-hot-toast";
import axios from "axios";
function VerifyEmail() {
const [emailVerified, setEmailVerified] = useState("");
const params = useParams();
const verifyToken = async () => {
try {
toast.loading();
const response = await axios.post("/api/user/verifyemail", {
token: params.token,
});
if (response.data.success) {
setEmailVerified("true");
} else {
setEmailVerified("false");
}
toast.dismiss();
} catch (error) {
toast.dismiss();
setEmailVerified("false");
}
};
useEffect(() => {
verifyToken();
}, []);
console.log(emailVerified);
return (
<div className="flex min-h-screen p-5 justify-center items-center">
{emailVerified == "" && (
<h1 className="text-primary text-4xl">
Please wait we are verifying your email
</h1>
)}
{emailVerified == "true" && (
<h1 className="text-primary text-4xl">
Your email verified successfully
</h1>
)}
{emailVerified == "false" && (
<h1 className="text-primary text-4xl">
Invalid or Expired Token
{console.log(emailVerified)}
</h1>
)}
</div>
);
}
export default VerifyEmail;
Frontend =>ProtectedRoute.js
import React from "react";
import { Navigate, useNavigate } from "react-router-dom";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { setUser } from "../redux/userSlice";
import { showLoading, hideLoading } from "../redux/alertsSlice";
function ProtectedRoute(props) {
const { user } = useSelector((state) => state.user);
const dispatch = useDispatch();
const navigate = useNavigate();
const getUser = async () => {
try {
dispatch(showLoading());
const response = await axios.post(
"/api/user/get-user-info-by-id",
{ token: localStorage.getItem("token") },
{
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
}
);
console.log('TOKEN ("token") : ', localStorage.getItem("token"));
dispatch(hideLoading());
if (response.data.success) {
dispatch(setUser(response.data.data));
console.log(response.data.data);
} else {
localStorage.clear();
navigate("/login");
}
} catch (error) {
dispatch(hideLoading());
localStorage.clear();
navigate("/login");
}
};
useEffect(() => {
if (!user) {
getUser();
}
}, [user]);
if (localStorage.getItem("token")) {
return props.children;
} else {
return <Navigate to="/login" />;
}
}
export default ProtectedRoute;
Frontend=> VerifyEmail.js
import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { toast } from "react-hot-toast";
import axios from "axios";
function VerifyEmail() {
const [emailVerified, setEmailVerified] = useState("");
const params = useParams();
const verifyToken = async () => {
try {
toast.loading();
const response = await axios.post("/api/user/verifyemail", {
token: params.token,
});
if (response.data.success) {
setEmailVerified("true");
} else {
setEmailVerified("false");
}
toast.dismiss();
} catch (error) {
toast.dismiss();
setEmailVerified("false");
}
};
useEffect(() => {
verifyToken();
}, []);
console.log(emailVerified);
return (
<div className="flex min-h-screen p-5 justify-center items-center">
{emailVerified == "" && (
<h1 className="text-primary text-4xl">
Please wait we are verifying your email
</h1>
)}
{emailVerified == "true" && (
<h1 className="text-primary text-4xl">
Your email verified successfully
</h1>
)}
{emailVerified == "false" && (
<h1 className="text-primary text-4xl">
Invalid or Expired Token
{console.log(emailVerified)}
</h1>
)}
</div>
);
}
export default VerifyEmail;
Frontend => userSlice.js
import { createSlice } from "#reduxjs/toolkit";
export const userSlice = createSlice({
name: "user",
initialState: {
user: null
},
reducers: {
setUser: (state , action) => {
state.user = action.payload;
}
},
});
export const { setUser , reloadUserData } = userSlice.actions;
When I create/register user1 , the verification mail is sent to that email id and he(user1) is being verified successfully and I am able to change password for user1. After creating user1 , I am creating/registering user2 ,where the verification email is sent to the account .After clicking the link , it's becomes INVALID Overall , I am only able to create one user who's being verified
I have used next-auth for Authentication in my next.js project and have deployed it on AWS server using DOcker. I am getting "http://localhost:3000/api/auth/error?error=Something%20went%20wrong!%20Please%20try%20again%20later." in responce of network everytime when I try to login or register and on all the pages where I have uset nextauth.
This is my nextauth code:
[...nextauth.js]
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { API_BASE_URL } from '../../../utils/constants';
export default NextAuth({
providers: [
CredentialsProvider({
id: 'credentials',
name: 'Busfabrik',
credentials: {
email_id: {
label: 'email',
type: 'email',
},
password: { label: 'Password', type: 'password' }
},
async authorize(credentials, req) {
const payload = {
email_id: credentials.email_id,
password: credentials.password,
isadmin: !!credentials.isadmin
};
const res = await fetch(API_BASE_URL + '/users/login', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
},
});
const user = await res.json();
// console.log('user', user);
// If error and we don't have user data, return error
if (!res.ok) {
throw new Error(user.exception);
}
// If no error and we have user data, return it
if (res.ok && user.success) {
return user;
} else {
// Return error if user data could not be retrieved
throw new Error(user.msg);
}
},
}),
// ...add more providers here
],
secret: process.env.JWT_SECRET,
callbacks: {
async jwt({ token, user }) {
// console.log('jwt user', user)
if (user && user.success) {
return {
...token,
accessToken: user.payload.token,
userData: user.payload
};
}
return token;
},
async session({ session, token }) {
// console.log('session', session);
// console.log('session token', token);
session.accessToken = token.accessToken;
session.user = token.userData;
return session;
},
},
theme: {
colorScheme: 'auto', // "auto" | "dark" | "light"
brandColor: '', // Hex color code #33FF5D
logo: '/vercel.svg', // Absolute URL to image
},
// Enable debug messages in the console if you are having problems
debug: process.env.NODE_ENV === 'development',
});
Can someone suggest or have aced the same issue? If Yes, then please guide me on this. I an stuck here from a long time and not able to find the solution.
I am just posting user Singup data in my Simple react native app to mongo-atlas.
Through Postman; same code is working fine and POST user data successfully but through app taking data from user gives the above error. both frontend and backend are runing on separate folders. I think i am not handling the frontend part correctly.
Here is the Front End
export default class Formsignup extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
email: '',
password: '',
showPass: true,
press: false,
};
this.showPass = this.showPass.bind(this);
}
showPass() {
this.state.press === false
? this.setState({showPass: false, press: true})
: this.setState({showPass: true, press: false});
}
getInput(text, field){
if(field == 'name')
{
this.setState({ name: text, })
}
else if(field == 'email')
{
this.setState({ email: text, })
}
else if(field == 'password')
{
this.setState({ password: text, })
}
}
submit(){
(async () => {
const rawResponse = await fetch('http://myIpAddress/api/user/register', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: this.state.name,
email: this.state.email,
password: this.state.password
})
});
const content = await rawResponse.json();
console.log(content);
})();
}
render() {
return (
<View style={styles.container}>
<UserInput
source={usernameImg}
placeholder="Username"
autoCapitalize={'none'}
returnKeyType={'done'}
autoCorrect={false}
onChangeText = {(text) => this.getInput(text, 'name')}
/>
<UserInput
source={usernameImg}
placeholder="Email"
keyboardType="email-address"
autoCapitalize={'none'}
returnKeyType={'done'}
autoCorrect={false}
onChangeText= {(text) => this.getInput(text, 'email')}
/>
<UserInput
source={passwordImg}
secureTextEntry={this.state.showPass}
placeholder="Password"
returnKeyType={'done'}
autoCapitalize={'none'}
autoCorrect={false}
onChangeText= {(text) => this.getInput(text, 'password')}
/>
<TouchableOpacity onPress = {()=> this.submit()}><Text>Submit Test</Text> </TouchableOpacity>
</View>
);
}
}
Here is back-end code
router.post('/register', async (req, res) => {
const {error} = registerValidation(req.body);
if(error) return res.status(400).send(res.send(error.details[0].message));
//if user already in db
const emailExist = await User.findOne({email: req.body.email});
if(emailExist)
return res.status(400).send('Email Already Exist');
//hash the password bro
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(req.body.password, salt);
//adding new user
const user = new User({
name: req.body.name,
email: req.body.email,
password: hashedPassword
});
try{
const savedUser = await user.save();
res.send({user: user._id});
}catch(err){
res.status(400).send(err);
}
});
hie; the way I see it this has something to do with your json response coming from your server side; here is a tip try :
res.json instead of res.send for a start
the code you did for the Fetch function is weak so try this
let data = {
method: 'POST',
body: JSON.stringify({
name: this.state.name,
email: this.state.email,
password: this.state.password
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}
fetch('http://10.105.4.135:3000/api/user/register', data).then(result=>result.json())
.then((parsedResponse) => {
console.warn('response',parsedResponse);
}).catch((err) => {
console.warn('error', err);
})