NextJs creating user document in mongodb after google sign in - reactjs

i want to create a user document after i sign in with google in my nextjs application. I can sign in but it's not creating the document after it. This is my function
const handleSignIn = async () => {
try {
await signIn("google");
await addUser();
} catch (error) {
console.log("Erro");
}
};
The addUser function is
const addUser = async () => {
if (status === "authenticated") {
const user = {
name: session.user.name,
email: session.user.email,
avatar: session.user.image,
};
try {
await fetch("/api/new_user", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(user),
});
} catch (error) {
console.log(error);
}
} else {
console.log("Not logged in");
}
};
This is how i'm creating the new document in my new_user.ts file in the api folder
export default async function handleNewUser(req:NextApiRequest, res:NextApiResponse){
const client = await clientPromise;
const db = client.db("bookdb");
const coll: Collection = db.collection("users");
const user = req.body
console.log(user)
try {
await coll.insertOne(user)
res.status(200).json({response:'Success'})
} catch (error) {
res.status(500).json({error:'Erro'})
To make sure it was working, i triggered manually the addUser function after signing in and it worked.
What am i doing wrong here?

this is my snippet for google auth sign in with mongodb and nextjs using typescript and prisma.
signIn: async ({user, account}) => {
if (account?.provider === 'google') {
const googleAuthData = {
name: user.name,
email: user.email,
image: user.image,
authProvider: 'google',
password: ''
}
const exist = await prisma.user.findFirst({
where: {email: user.email},
});
if (exist) {
const result = await prisma.user.update({
where: {email: user.email},
data: {image: user.image},
});
} else {
const result = await prisma.user.create({
data: googleAuthData,
});
}
}
return true;
},

Related

How to use a value in a use state for API URL?

I am getting trouble with calling the API.
So in the first async await function i got the data of list_id.
Then i put the data inside a useState called listID.
After that i am trying to call an API again with async await function with the value of listID inside the URL. But i am getting an error here. Whats probably the issue?
const [listID, setlistID] = useState();
const formik = useFormik({
initialValues: {
name: "",
description: "",
},
onSubmit: (values) => {
if (localStorage.getItem("sessionID")) {
const createList = async () => {
try {
let response = await axios({
method: "post",
url: `https://api.themoviedb.org/3/list?api_key=${API_KEY}&session_id=${localStorage.getItem(
"sessionID"
)}`,
data: {
name: values.name,
description: values.description,
},
});
setlistID(response.data.list_id);
} catch (error) {
console.log(error);
}
};
createList();
}
const getList = async () => {
try {
const response = await axios.get(
`https://api.themoviedb.org/3/list/${listID}?api_key=${API_KEY}`
);
console.log(response);
} catch (error) {
console.error(error);
}
};
getList();
},
});

Session returns null Get request however in Post returns the session correctly in Next.js in server side (nextAuth)

I am trying to get the session data such as id, name, etc on the server-side but returns null in the terminal.
In the GET Method session is null, however, in POST method returns the session perfectly.
import { getSession } from "next-auth/react";
export default async (req, res) => {
const { method } = req;
switch (method) {
case "GET":
try {
const session = await getSession({ req });
console.log(JSON.stringify(session)); //Returns null
const jobs = await prisma.jobs.findMany();
return res.status(200).json({ success: true, jobs });
} catch (error) {
return res.status(404).json({
success: false,
message: error.message,
});
}
}
case "POST":
try {
const session = await getSession({ req });
console.log(JSON.stringify(session)); // here returns the session
if (!session || session.role != 2) {
return res.status(401).json({
msg: "You are not authorized to perform this action",
});
}
[...nextauth].js
...nextauth.js file applying credentials provider, I am not sure if I have to implement anything else in addition
import NextAuth from "next-auth";
import CredentialProvider from "next-auth/providers/credentials";
import axios from "axios";
export default NextAuth({
providers: [
CredentialProvider({
name: "credentials",
async authorize(credentials) {
try {
const user = await axios.post(
`${process.env.API_URL}/auth/authentication/login`,
{
email: credentials.email,
password: credentials.password,
}
);
if (!user.data.user) {
return null;
}
if (user.data.user) {
return {
id: user.data.user.id,
name: user.data.user.name,
surname: user.data.user.surname,
email: user.data.user.email,
role: user.data.user.role,
};
}
} catch (error) {
console.error(error);
}
},
}),
],
callbacks: {
jwt: ({ token, user }) => {
if (user) {
token.id = user.id;
token.name = user.name;
token.surname = user.surname;
token.email = user.email;
token.role = user.role;
}
return token;
},
session: ({ session, token }) => {
if (token) {
session.id = token.id;
session.name = token.name;
session.surname = token.surname;
session.email = token.email;
session.role = token.role;
}
return session;
},
},
secret: process.env.SECRET_KEY,
jwt: {
secret: process.env.SECRET_KEY,
encryption: true,
maxAge: 5 * 60 * 1000,
},
pages: {
signIn: "/auth/login",
},
});
This is weird. Can you try moving const session = await getSession({ req }) just before the switch statement?
...
const session = await getSession({ req });
console.log(JSON.stringify(session));
switch (method) {
...
}
....

How to return api errors to Login Component in NextAuth.js

How to return API errors to Login Component in NextAuth.js.Actually, I am trying to pass the Errors back to Login Component in NextAuth(Credentials Provider). I am getting this object in console error: "CredentialsSignin" ok: false status: 401 url: null [[Prototype]]: Object Everything is working fine like I am able to log in, but when I am trying to handle errors coming from APIs, I am unable to handle them.
[...nextauth.js] File
export default (req, res) =>
NextAuth(req, res, {
providers: [
CredentialsProvider({
authorize: async (credentials) => {
try {
const data = {
email: credentials.email,
password: credentials.password
}
const user = await login(data);
console.log("401 Error",user.data);
if (user.data.status==200) {
console.log("200 data",user.data);
return Promise.resolve(user.data);
}else if(user.data.status==401){
// Here I wants to Handle Errors and return them back to My login Compenent
}
} catch (error) {
if (error.response) {
console.log(error.response);
Promise.reject(new Error('Invalid Username and Password combination'));
}
}
},
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
],
pages: {
signIn: '/login',
},
callbacks: {
jwt: async ({token,user})=>{
if(user){
token.userid = user.id;
token.name = user.username;
token.token = user.token;
}
return token;
},
session: (session,token)=>{
return session;
}
},
secret:"test",
jwt:{
secret:"test",
encryption:true,
},
site: process.env.NEXTAUTH_URL || "http://localhost:3000",
session: {
jwt: true,
maxAge: 1 * 3 * 60 * 60, // 3 hrs
updateAge: 24 * 60 * 60, // 24 hours
}
});
const login = async data => {
var config = {
headers: {
'Content-Type': "application/json; charset=utf-8",
'corsOrigin': '*',
"Access-Control-Allow-Origin": "*"
}
};
const url = 'api/auth/login';
const result = await axios.post(url,data,config);
return result;
};
Login Components
const LoginSubmit = async (event) => {
event.preventDefault();
const enteredEmail = inputText.email;
const enteredPassword = inputText.password;
// console.log(enteredEmail);
const result = await signIn("credentials", {
redirect: false,
email: enteredEmail,
password: enteredPassword,
});
console.log("Final Result",result);
};
Yes you can do it :
Try :
// component.js
const res = await signIn("credentials", {
email: inputs.email,
password: inputs.password,
callbackUrl: `/`,
redirect: false,
});
if (res?.error) {
setLoading(false);
Swal.fire("Invalid Login", "username or password is incorrect");
}
and in Nextauth file you must throw an error if a user entred invalid credential!
//[...nextauth].js
const providers = [
Providers.Credentials({
name: "credentials",
authorize: async (credentials) => {
//statr try
try {
const user = await
axios.post(`${API_URL}/auth/local`, {
identifier: credentials.email,
password: credentials.password,
});
if (user) {
return { status: "success",
data:user.data };
}
} catch (e) {
throw new Error("Not found ");
}
},
}),
];

Upload input form data and file/image Next js

I am trying to send form data life person name, email and image together using Next js. I used formdata for file upload and using react-hook-form for form input.
The problem is I couldn't receive the image/file in the Next api.
My codes are :
Onchange:
const handleImgChange = (e) => {
if (e.target.files && e.target.files[0]) {
const img = e.target.files[0];
setProfileImg(img);
}
};
to get form data from input.
const handleIChange = (e) => {
const value = e.target.value;
setContents((prevContnet) => {
return {
...prevContnet,
[e.target.name]: value,
};
});
};
On submit
const handleOnsubmlit = (e) => {
e.preventDefault();
if (profileImg.length > 0) {
const formData = { ...contents, profile_picture: profileImg };
updateUserSetting(formData);
} else {
updateUserSetting(contents);
}
};
updateUserSetting
async function updateUserSetting(formdata) {
try {
console.log("form datas", formdata);
dispatch({ type: "UPDATE_USER_SETTING_REQUEST" });
const { data } = await axios(
`${NEXT_URL}/api/updateusersetting`,
{
method: "PUT",
formdata,
"content-type": "multipart/form-data",
}
);
console.log("return data ", data[0]);
dispatch({ type: "UPDATE_USER_SETTING_SUCCESS", payload: data[0] });
} catch (error) {
dispatch({
type: "UPDATE_USER_SETTING_FAIL",
payload: error.response
});
}
}
API
import { IncomingForm } from "formidable";
export const config = {
api: {
bodyParser: false,
},
};
export default async (req, res) => {
if (req.method === "PUT") {
if (!req.headers.cookie) {
res.status(403).json({ message: "Not Authorized" });
return;
}
const { token } = cookie.parse(req.headers.cookie);
console.log("body is", req.body);
const formData = await new Promise((req, res) => {
const form = new IncomingForm();
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
res.writeHead(200, { "content-type": "multipart/form-data" });
res.json({ fields, files });
});
});
};
how can I put data together and send it to the desired API? Thanks in advance.
You can use the FormData interface to send files and other fields as a single JSONified string, or individual strings. Formidable will separate your fields and files in the callback, and you can process them individually.
Here's a working Codesandbox.
Output:

react & redux with hooks: Actions must be plain objects. Use custom middleware for async actions

i tried looking for similar answers to help solve my problem but i couldn't find anything using react redux hooks. This code was from a tutorial and originally written using the Context api. I wanted to trying using it with react-redux-hooks, but i got stuck. Basically i'm trying to register a user with a name, email and password, then pass these three as an object to the express server which will validated it and give me back a jwt token. Then come back to the client side and send the token to the reducer, which adds the token to localstorage and sets the state to isAuthenticated. The error i get is on the dispatch.
Dispatch
const onSubmit = e => {
e.preventDefault();
if (name === "" || email === "" || password === "") {
dispatch(setAlert("Please enter all fields", "danger"));
} else if (password !== password2) {
dispatch(setAlert("Passwords do not match", "danger"));
} else {
dispatch(register({ name, email, password })); // Error is here
}
setTimeout(() => {
dispatch(removeAlert());
}, 5000);
};
Action
export const register = async formData => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
try {
const res = await axios.post("/api/users", formData, config);
return {
type: "REGISTER_SUCCESS",
payload: res.data
};
} catch (err) {
return {
type: "REGISTER_FAIL",
payload: err.response.data.msg
};
}
};
Reducer
const authReducer = (
state = {
token: localStorage.getItem("token"),
isAuthenticated: null,
loading: true,
user: null,
error: null
},
action
) => {
switch (action.type) {
case "REGISTER_SUCCESS":
console.log("register success");
localStorage.setItem("token", action.payload.token);
return {
...state,
...action.payload,
isAuthenticated: true,
loading: false
};
case "REGISTER_FAIL":
console.log("register failed");
localStorage.removeItem("token");
return {
...state,
token: null,
isAuthenticated: false,
loading: false,
user: null,
error: action.payload
};
default:
return state;
}
};
Store
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|| compose;
const store = createStore(
allReducers,
composeEnhancers(applyMiddleware(thunk))
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
Express server
router.post(
"/",
[
check("name", "Please a name")
.not()
.isEmpty(),
check("email", "Please include a valid email").isEmail(),
check(
"password",
"Please enter a password with 6 or more characters"
).isLength({
min: 6
})
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
const { name, email, password } = req.body;
try {
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({
msg: "User already exists"
});
}
user = new User({
name,
email,
password
});
// hash passsword
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
config.get("jwtSecret"),
{
expiresIn: 360000
},
(err, token) => {
if (err) throw err;
res.json({
token
});
}
);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
I believe this question has answers to the issue your experiencing here: how to async/await redux-thunk actions?
Using this example, it may look something like this (wasn't able to test it):
export const register = formData => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
const request = axios.post("/api/users", formData, config);
return dispatch => {
const onSuccess = success => {
dispatch({
type: "REGISTER_SUCCESS",
payload: success.data
});
return success;
};
const onError = error => {
dispatch({
type: "REGISTER_FAIL",
payload: error.response.data.msg
});
return error;
};
request.then(onSuccess, onError);
};
};
export const register = formData => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
return async dispatch => {
const onSuccess = success => {
dispatch({
type: "REGISTER_SUCCESS",
payload: success.data
});
return success;
};
const onError = error => {
dispatch({
type: "REGISTER_FAIL",
payload: error.response.data.msg
});
return error;
};
try {
const success = await axios.post("/api/users", formData, config);
return onSuccess(success);
} catch (error) {
return onError(error);
}
}
};

Resources