API login React Not Connecting - reactjs

enter image description hereI'm trying to login/register a new user, however, it won't let me and I have no idea how to get it fixed. What should I do about this? I tried to find out on how to do this but it is kind of getting complicated for me and I have no idea. It's like the code doesn't want to work.
enter code here
import React, { useEffect, useState } from "react";
import LoginForm from "../LoginForm";
import Auth from "../../utils/Auth";
import { useLocation, useHistory } from "react-router";
//Uses the Auth methods to actually login with the LoginForm Component.
function Login() {
let location = useLocation();
let history = useHistory();
const [redirectToReferrer, setRedirectToReferrer] = useState(false);
useEffect(() => {
const { from } = location.state || { from: { pathname: "/protected" } };
if (redirectToReferrer) {
history.push(from);
}
}, [redirectToReferrer, history, location.state]);
/* We need to POST to the API the users info,
This will get passed down as a prop to the LoginForm */
const login = (data) => {
console.log("Logging in " + JSON.stringify(data));
//fetch('api/users/login', { is the error
fetch("api/users/login", {
method: "POST",
body: JSON.stringify(data),
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.status === 200) {
//All good
Auth.authenticate(() => {
//Update the boolean and take off the cuffs
setRedirectToReferrer(true);
console.log(`Response in login ${JSON.stringify(response)}`);
});
}
})
.catch((err) => {
// No beuno, kick them
console.log("Error logging in.", err);
});
};
return (
<div>
<LoginForm onLogin={login} />
</div>
);
}
export default Login;

Related

How do I persist my next-auth user session? so i could use the ID provided to fetch data in other routes

What I want to achieve here is, whenever a user logs in, I want to store the data returned because the data holds an ID that I would use to fetch data in other routes.
When a user successfully logs in, he would be redirected to the /home route and the ID gotten from the session would be used to fetch data. Everything works fine initially, but if I refresh the home page, the user becomes null.
This is what my [...nextauth].js looks like.
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import axios from "axios";
export default NextAuth({
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: { label: "Username", type: "text", placeholder: "justin" },
password: {label: "Password",type: "password",placeholder: "******"},
},
async authorize(credentials, req) {
const url = req.body.callbackUrl.split("/auth")[0];
const { username, password } = credentials;
const user = await axios({
url: `${url}/api/user/login`,
method: "POST",
data: {
username: username,
password: password,
},
"content-type": "application/json",
})
.then((res) => {
return res.data;
})
.catch((err) => {
if (err.response.data) {
throw new Error(err.response.data);
} else {
return null;
}
return null;
});
return user;
},
}),
],
callbacks: {
jwt: ({ token, user }) => {
if (user) {
token.user = user;
}
return token;
},
session: ({ session, token }) => {
if (token) {
session.user = token.user;
}
return session;
},
},
pages: {
signIn: "/auth/login",
newUser: "/auth/register",
},
});
and this is what my /home route looks like
import Card from "#/components/card/Card";
import React, { useEffect, useState } from "react";
import styles from "./home.module.css";
import { Ubuntu } from "#next/font/google";
import { useSession } from "next-auth/react";
import { useDispatch, useSelector } from "react-redux";
const ubuntu = Ubuntu({ weight: "500", subsets: ["cyrillic"] });
const getData = async (id) => {
const res = await fetch({
url: "http://localhost:3000/api/note/getall",
method: "POST",
"content-type": "application/json",
data: {
id: id,
},
});
if (!res.ok) {
console.log(id);
throw new Error("Unable to fetch");
} else {
return res.json();
console.log(res);
}
};
function home() {
const colors = ["#E9F5FC", "#FFF5E1", "#FFE9F3", "#F3F5F7"];
const random = Math.floor(Math.random() * 5);
const rc = colors[random];
const [pop, setPop] = useState("none");
const { user } = useSelector((state) => state.user);
const getDataa = async () => {
console.log(user)
const data = await getData(user._id);
console.log(data);
};
useEffect(() => {
if (user) {
alert(user)
}
}, []);
return (
<div className={styles.home}>
<header>
<h3 className={ubuntu.className}>
Hello, <br /> {user?.username}!
</h3>
<input type="text" placeholder="search" />
</header>
<div className={styles.nav}>
<h1 className={ubuntu.className}>Notes</h1>
</div>
<div className={styles.section}>
<div className={styles.inner}>
{/* {data &&
data.map((e) => (
<Card
rawData={e}
color={colors[Math.floor(Math.random() * colors.length)]}
/>
))} */}
</div>
</div>
<div className="new"></div>
</div>
);
}
export default home;
Add this component to your App.js file :
function Auth({ children }) {
const router = useRouter();
const { status } = useSession({
required: true,
onUnauthenticated() {
router.push("/sign-in");
},
});
if (status === "loading") {
return <div>Loading ...</div>;
}
return children;
}
Now in your App function instead of returning <Component {...pageProps} /> you check first if the component has auth property, so you wrapp it with <Auth> to ensure that every component that requires session will only mount when the session finishes loading (that's why the user is null because the session is still loading)
{
Component.auth ? (
<Auth>
<Component {...pageProps} />
</Auth>
) : (
<Component {...pageProps} />
);
}
finally you add .auth = {} to every page in whitch you want the session to be defined (Home in your case)
const Home = () => {
//....
}
Home.auth = {};
This also helps to redirect user to /sign-in page if the session is expired
This code seems like it would create a problem / race-condition since you're mixing two different async promise handling styles:
const user = await axios({
url: `${url}/api/user/login`,
method: "POST",
data: {
username: username,
password: password,
},
"content-type": "application/json",
})
.then((res) => {
return res.data;
})
.catch((err) => {
if (err.response.data) {
throw new Error(err.response.data);
} else {
return null;
}
return null;
});
return user;
It should either be this:
try {
const user = await axios({
url: `${url}/api/user/login`,
method: "POST",
data: {
username: username,
password: password,
},
"content-type": "application/json",
});
return user.data;
} catch (err) {
if (err.response.data) {
throw new Error(err.response.data);
} else {
return null;
}
}
Or this:
axios({
url: `${url}/api/user/login`,
method: "POST",
data: {
username: username,
password: password,
},
"content-type": "application/json",
}).then((res) => {
return res.data;
}).catch((err) => {
if (err.response.data) {
throw new Error(err.response.data);
} else {
return null;
}
return null;
});

Problem sending cookie "connect.sid" with browser

I have an ecommerce created with react (front), meanwhile the back is made with express. My session is handled by passport js, and the problem is that I establish the login or the register without any problem, but when I have to re-check the session, the browser (Chrome) never sends back the cookie, so the server is not able to recognize the session. I checked with postman and it works fine. Does someone know why the front does not send the cookie ("connect.sid")???
I leave here the code of the component in react and the code of my router.
import { useContext, useEffect } from "react"
import ItemList from "./ItemList"
import { myContext } from "../contexto/contexto"
import { useHistory } from "react-router"
const ItemListContainer = () => {
const contexto = useContext(myContext)
const history = useHistory()
const obj = {
email: sessionStorage.getItem("user")
}
useEffect(() => {
fetch(`http://localhost:8080/api/usuarios/check-session`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(obj)
})
.then(res => res.json())
.then(json => {
console.log(json)
//if (json.session == false) history.push("/")
})
.catch(err => console.log(err))
},[obj])
return (
<ItemList items={contexto.productos} />
)
}
export default ItemListContainer
Here is my server:
const { Router } = require("express")
const router = Router()
const upload = require("../../utils/middlewares/multer")
let UsuariosController = require("./controller/usuariosController")
const textUsuarioYaRegistrado = "Email ya registrado!"
const textUsuarioInexistente = "Datos incorrectos!"
module.exports = (app,passport) => {
app.use("/api/usuarios", router)
let isLogin = (req, res, next) => {
try {
if (req.isAuthenticated()) {
next()
} else {
res.json({session: false})
}
} catch (error) {
console.log(error)
}
}
let isNotLogin = (req, res, next) => {
try {
if (!req.isAuthenticated()) {
next()
} else {
res.redirect(`${config.FRONT_URI}/productos`)
}
} catch (error) {
console.log(error)
}
}
//router.post("/nuevo", UsuariosController.createUsuario)
router.post("/guardar-foto", upload.single("foto"), UsuariosController.guardarFoto)
//router.post("/", UsuariosController.getUsuario)
router.post("/nuevo", passport.authenticate('register', {failureRedirect:`/api/usuarios/error/${textUsuarioYaRegistrado}`}), (req,res,next) => {
res.json(req.body)
});
router.post("/", passport.authenticate('login', {failureRedirect:`/api/usuarios/error/${textUsuarioInexistente}`}), (req,res,next) => {
res.json(req.user)
});
router.get("/error/:errorMessage", (req,res,next) => {
res.render("error", {error: req.params.errorMessage})
})
router.post("/check-session", isLogin, UsuariosController.getByEmail)

How to organize/manage context state and helper functions (mainly API calls) in react

I want to organize my react native app such that all state and business logic related to one module remains encapsulated at one place and components can focus mainly on UI. I really liked angular services in this regard as they keep the state and all helper, data fetching methods at one place and components can easily use and share them.
I have seen this
But this way of defining context and then defining custom hook to use that context in fetch API's etc seems unnecessarily complicated. I want a separate service/helper for my fetch api's that is aware of the state. Any suggestions please
For example almost every app has auth module. I have defined auth context:
import React, {
useEffect,
useState,
useContext,
useMemo,
createContext,
} from "react";
import { externalBaseURI2 } from "../config/constants";
import StorageService from "../services/storageService";
const AuthContext = createContext({
token: null,
});
export const AuthProvider = ({ children }) => {
const [token, setToken] = useState(null);
useEffect(() => {
getAuthToken()
.then(token => {
if(token) setToken(token)
})
}, []);
const getAuthToken = () => {
if (token) return Promise.resolve(token);
else return StorageService.getDataFromStorage("authToken");
};
const setAuthToken = (accessToken) => {
setToken(accessToken);
StorageService.setDatatoStorage("authToken", accessToken);
};
const revokeAuthToken = () => {
setToken(null);
StorageService.removeItemFromStorage("authToken");
};
**///////////////////// BELOW ARE MY STATE FUNCTIONS i.e: login, getProfile, logOut ////////////////**
const actions = {
login: (email, pass) => {
console.log(email, pass);
return fetch(`${externalBaseURI2}/auth/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email: email, password: pass }),
})
.then((response) => {
if (!response.ok) {
return response.json().then(error => {
throw new Error(error.message || error.errorMsg)
})
// throw new Error("Something went wrong");
}
return response.json()
.then((jsonResponse) => {
setAuthToken(jsonResponse.token);
return jsonResponse;
})
})
// .catch((err) => console.log(err));
},
getProfile: () => {
return getAuthToken()
.then(token => {
if(token) {
return fetch(`${externalBaseURI2}/api/profile`, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
})
}
else throw new Error('User not logged in')
})
.then((response) => {
if (!response.ok) {
return response.json().then(error => {
throw new Error(error.message || error.errorMsg)
})
}
return response.json();
})
.then((jsonResponse) => {
return jsonResponse;
})
},
logOut: () => {
revokeAuthToken()
}
}
return (
<AuthContext.Provider value={{ token, ...actions }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be inside a Provider with a value");
}
return context;
};
Here I have login and getprofile methods as state functions. Which I don't think should be responsible for fetching api data instead should only be responsible for setting state like logOut method. So where should I put the API fetch methods (in some helper/service file not in component) where I can access the state token too (to send in private api calls).

Gets 401 error while user tries to do Fetch Get request after authentication

I'm trying to get user details after user looged in but user is getting 401 error even user is looged in with 200 ok.
Explanation of process:
i have logged in user using fetch post request.
stored username,role,staffid to async storage
now i want to list all user (with /api/staff endpoint response throws user firstname and last name )with fetch get request but whenever i make GET request it
throws 401 error.
It will be lifesaver to crack this step for me,thank you!
here is my code
import AsyncStorage from "#react-native-community/async-storage";
import React, { useState, useEffect } from "react";
import { SafeAreaView, Text, StyleSheet, Alert } from "react-native";
import AuthService from "../api/auth-service";
import BASE_URL from "../api/baseUrl";
export default function HomeScreen(props) {
const [firstName, setFirstName] = useState({});
const [lastName, setLastName] = useState({});
const [userValue, setUserValue] = useState({});
useEffect(() => {
let mounted = true;
if (mounted) {
getDataFromStorage();
getUserInfo();
}
return () => {
mounted = false;
};
}, []);
const getDataFromStorage = async () => {
let user = await AsyncStorage.getItem("LoggedInUser");
setUserValue(JSON.parse(user));
};
const getUserInfo=async()=>{
return fetch(BASE_URL+"/api/staff")
.then((response) => {
if(response.ok){
console.log(response);
}else{
console.log(response.status);
}
})
.catch((error) => {
console.log(error);
this.setState({ errorMsg: "Error retreiving data" });
});
}
return (
<SafeAreaView>
<Text>
{"Good morning " + userValue.username + " "}
{"you role is " + userValue.role +"your staff id is " + userValue.staffId+" " + "your first name is "+ firstName +"this is your last name"+lastName}
</Text>
</SafeAreaView>
);
}
authservice.js
import AsyncStorage from "#react-native-community/async-storage";
import BASE_URL from "./baseUrl";
class AuthService {
login(Username, Password, role) {
console.log(Username, role);
return fetch(BASE_URL + "/api/authentication/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
Username,
Password,
}),
}).then((res) => {
if (res.ok) {
console.log("the login response", res);
return res.json();
} else {
alert("Invalid Username or Password");
window.stop();
}
});
}
logout() {
AsyncStorage.getAllKeys().then((keys) => AsyncStorage.multiRemove(keys));
}
}
export default new AuthService();
login.js
const submitData = async () => {
AuthService.login(Username, Password).then(
(data) => {
console.log(JSON.stringify(data));
AsyncStorage.setItem("LoggedInUser", JSON.stringify(data));
if (data.role == "Admin") {
console.log(data.username);
navigation.navigate("adminPage");
} else {
navigation.navigate("staffpage");
}
},
(error) => {
Alert.alert(error);
}
);
};
According to developer.mozilla.org
The HTTP 401 Unauthorized client error status response code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.
It's seems the user doesn't have right to access the API. Make sure that the getUserInfo() API, /api/staff, don't need any authentication token in header of your HTTP request.
I have a feeling that you may need to resolve one more promise in AuthService.login.
res.json() is actually a promise which needs to be resolved as well, so you may need one more then block like so:
return fetch(BASE_URL + "/api/authentication/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
Username,
Password,
}),
}).then((res) => {
if (res.ok) {
console.log("the login response", res);
return res.json();
} else {
alert("Invalid Username or Password");
window.stop();
}
}).then(finalData=>finalData )// <---------- add this
.catch(err=> err)
It happens because getDataFromStorage is an async function so you have to resolve it first. Also in login.js we have to await before navigate to make sure that the data is saved in AsyncStorage. Please update the following part of your code:
login.js
const submitData = async () => {
AuthService.login(Username, Password).then(
async (data) => {
console.log(JSON.stringify(data));
await AsyncStorage.setItem("LoggedInUser", JSON.stringify(data));
if (data.role == "Admin") {
console.log(data.username);
navigation.navigate("adminPage");
} else {
navigation.navigate("staffpage");
}
},
(error) => {
Alert.alert(error);
}
);
};
next, initialize userValue with null
const [userValue, setUserValue] = useState(null);
Now have 2 useEffect,
useEffect(() => {
let mounted = true; //Why this required as it doesn't mean anything
if (!userValue) {
getDataFromStorage();
}
return () => {
mounted = false;
};
}, []);
useEffect(()=>{
if(userValue){
getUserInfo()
}
},[userValue])

React render run before fetch method receives token from server

I have a JS file within my React application, which connects to the server, sends username and password, receives an oauth token from the server and stores the token in the local storage.
However before the token received by react, the react sends the next request before token stored in the local storage. Which leads to 401 unauthorized access.
AuthService.js
login(username, password) {
console.log(username);
return this.fetch(`${this.domain}/api/AuthAPI/getCredentials`, {
headers: {
'Access-Control-Allow-Origin': "*"
}
}).then(res => {
this.fetch(`${this.domain}/Token`, {
method: 'POST',
body: 'grant_type=password&username=' + res[0]
}).then(response => {
var date_token_issue = new Date();
this.setToken(response.access_token,response.expires_in, date_token_issue) // Setting the token in localStorage
return Promise.resolve(response);
})
})
}
setToken(idToken,expires, date_token_issue ) {
localStorage.setItem('id_token', idToken)
localStorage.setItem('expires', expires)
localStorage.setItem('date_token_issue', date_token_issue)
}
SignIn.jsx
import React, { Component } from 'react'
import AuthService from '../comonents/AuthService';
import Orders from '../../app/orders/orders'
import { Redirect, Switch, Route} from "react-router-dom";
export default function SignIn(AuthComponent){
const Auth = new AuthService('http://localhost:53050');
return class AuthWrapped extends Component {
constructor() {
super();
this.state = {
user: null,
loggedIn: false
}
}
async componentDidMount() {
if (!Auth.loggedIn()) {
const promise = await Auth.login('m.dawaina', 'm.dawaina');
console.log(promise)
this.setState({loggedIn: true});
}
else {
try {
this.setState({loggedIn: true})
const profile = Auth.getProfile()
this.setState({
user: profile
})
}
catch(err){
Auth.logout()
//this.props.history.replace('/login')
}
}
}
render() {
if (this.state.loggedIn) {
return (
<div>
<Redirect to='/orders'/>
<Switch>
<Route path="/orders" component={Orders} />
</Switch>
</div>
)
}
else {
return (
<AuthComponent history={this.props.history} user={this.state.user} />
)
}
}
}
}
I need a way to force react wait for the JS receives the token and stores it in the local storage, and prevent react sending the next request until it finds the token stored in the local storage.
login(username, password) {
console.log(username);
return this.fetch(`${this.domain}/api/AuthAPI/getCredentials`, {
headers: {
'Access-Control-Allow-Origin': "*"
}
}).then(res => {
// Add a return here
return this.fetch(`${this.domain}/Token`, {
method: 'POST',
body: 'grant_type=password&username=' + res[0]
}).then(response => {
var date_token_issue = new Date();
this.setToken(response.access_token,response.expires_in, date_token_issue) // Setting the token in localStorage
return Promise.resolve(response);
})
})
You need to add a return to the then function so that await will wait for the inner promise to resolve.

Resources