FIrebase Google Auth Logout Error Connection refused - reactjs

I am trying to implment google auth into my firebase project. I am succesully able to login into the app but the problem happens at logout.
I receive the error
GET http://localhost:4000/auth/logout net::ERR_CONNECTION_REFUSED
This seems to be an issue with my logout route which is defined in the code as this
export default function Dashboard() {
const [error, setError] = useState("");
const history = useHistory();
async function handleLogout() {
setError("");
axios({
method: "GET",
withCredentials: true,
url: "/auth/logout",
})
.then((res) => {
Auth.deauthenticateUser();
history.push("/login");
})
.catch((err) => {
console.log(err.response.data.message);
});
}
return (
<div>
{error && <p>{error}</p>}
<h2>Home</h2>
<p>Signed In</p>
<button variant="link" onClick={handleLogout}>
Log Out
</button>
</div>
);
}
I think there is an issue with my logout route, any help would be appreciated.
Login Code
function onGoogleSubmit(e) {
e.preventDefault();
var provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
prompt: 'select_account'
})
firebase
.auth()
.signInWithPopup(provider)
.then((result) => {
setError("");
console.log(result.user);
Auth.authenticateUser();
history.push("/");
})
.catch((err) => {
setError(err.message);
});
}
return (
<div className="login-container">
<div className="login-shadow-box">
<button onClick={onGoogleSubmit}>Google</button>
</div>
</div>
</div>
);

axios({
method: "GET",
withCredentials: true,
url: "/auth/logout",
})
Axios will make a GET request at https://domain.tld/auth/logout. I'm not sure if that is server. But you can simply use signOut method to logout.
async function handleLogout() {
setError("");
firebase.auth().signOut()
.then((res) => {
Auth.deauthenticateUser();
history.push("/login");
})
.catch((err) => {
console.log(err.response.data.message);
});
}

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;
});

Stop react redirecting before API call has finsished

Im writing an application using react and django rest. I am trying to update a post and then redirect back to the home screen, but sometimes the redirect happens before the put request.
As there is a Get request on the home page, that then gets called first and i do not see the updated values unless i refresh the page? Any suggestions?
Here is the page with the put request (updateNote())
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { ReactComponent as ArrowLeft } from "../assets/arrow-left.svg";
const NotePage = ({ match, history }) => {
let noteId = match.params.id;
let [note, setNote] = useState(null);
useEffect(() => {
getNote();
}, [noteId]);
let getNote = async () => {
let response = await fetch(`/api/get-note/${noteId}/`);
let data = await response.json();
setNote(data);
};
let updateNote = async () => {
fetch(`/api/get-note/${noteId}/update/`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(note),
});
};
let deleteNote = async () => {
fetch(`/api/get-note/${noteId}/delete/`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
history.push("/");
};
let handleSubmit = () => {
updateNote().then(history.push("/"));
};
let handleChange = (value) => {
setNote((note) => ({ ...note, body: value }));
console.log("Handle Change:", note);
};
return (
<div className="note">
<div className="note-header">
<h3>
<ArrowLeft onClick={handleSubmit} />
</h3>
<button onClick={deleteNote}>Delete</button>
</div>
<textarea
onChange={(e) => {
handleChange(e.target.value);
}}
value={note?.body}
></textarea>
</div>
);
};
export default NotePage;
Then here is the page it redirects to
import React, { useState, useEffect } from "react";
import ListItem from "../components/ListItem";
const NotesListPage = () => {
let [notes, setNotes] = useState([]);
useEffect(() => {
getNotes();
}, []);
let getNotes = async () => {
let response = await fetch("/api/get-notes/");
let data = await response.json();
setNotes(data);
};
return (
<div className="notes">
<div className="notes-header">
<h2 className="notes-title">☶ Notes</h2>
<p className="notes-count">{notes.length}</p>
</div>
<div className="notes-list">
{notes.map((note, index) => (
<ListItem key={index} note={note} />
))}
</div>
</div>
);
};
export default NotesListPage;
I want to make sure that history.push("/") doesnt get executed unitll the fetch request has returned a response
I suggest using the promise method and using '.then' or await just like that :
let updateNote = async () => {
let temp =await fetch(`/api/get-note/${noteId}/update/`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(note),
});
if(temp)
history.push("/")
};
If you want to navigate after the fetch request has resolved then the code needs to wait for them to settle. Don't forget to catch and/or handle any errors and rejected Promises appropriately.
Example:
const updateNote = async () => {
// return Promise to chain from
return fetch(`/api/get-note/${noteId}/update/`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(note),
});
};
const deleteNote = async () => {
try {
// wait for Promise to resolve
await fetch(`/api/get-note/${noteId}/delete/`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
history.push("/");
} catch(error) {
// log error, etc...
}
};
const handleSubmit = () => {
// pass a callback in .then
updateNote()
.then(() => history.push("/"))
.catch(error => {
// log error, etc...
});
};

Refresh token implementation using axios interceptor

I want to get the new access token from the refresh token by making a post request for every request after the expiration of the access token. I have implemented it using axios interceptors but getting 401 Unauthorized errors after every post request at api/notes/create/ endpoint.
What changes do I need to make for a successful post request at api/notes/create/?
export const Main = () => {
const [showAddNote, setShowAddNote] = useState(false)
const [notes, setNotes] = useState([])
useEffect(()=>{
const getNotes = () => {
axios.get(`http://127.0.0.1:8000/api/notes/`)
.then((res)=>{ setNotes(res.data)})
.catch(err=>console.log(err))
}
getNotes()
},[])
axios.interceptors.response.use(undefined,
function axiosRetryInspector(err) {
const refreshToken = localStorage.getItem('refresh')
if (err.response.status === 401 && err.response.data.detail === 'Authentication credentials were not provided.') {
console.log("ooo")
axios.post(`http://localhost:8000/api/accounts/token/refresh/`, {
refresh: refreshToken
})
.then((res) => res.data)
.then((res) => {
console.log(err.config)
err.config.headers['Authorization'] = 'Bearer ' + res.access;
localStorage.setItem('access', res.access)
})
}
return Promise.reject(err)
}
);
const addNote = (note) => {
console.log("fffffff")
const headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('access')
}
console.log(headers)
axios.post(`http://127.0.0.1:8000/api/notes/create/`, note, { headers } )
.then(res=> res.json())
.catch(err=>console.log(err))
setNotes([...notes, note])
}
return (
<div>
<Header />
<CreateArea onAdd={addNote} />
<Notes notes={notes} />
<Footer />
</div>
)
}

How to save current user data using react-redux?

I create simple MERN app. I use passport in authentication. I have home, login, register secret and submit page have. only loginned users can see secret , home and submit pages and only not loginned users can see login and register page. I send data using axios backend. User login,register was succesfully but after loginned users get user data many time after. I dont know how to fix this. My backend doesnt have error. It work well.
-------------- Secret page --------------
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Redirect } from "react-router";
import { user_ } from "../actions/register_action";
const Secret = ()=>{
const dispatch = useDispatch();
const user = useSelector( state => state.user );
useEffect( ()=>{
dispatch( user_ );
},[] );
console.log(user);
if(user === null) return <Redirect to = "/login" />
else
return (
<div className="jumbotron text-center">
<div className="container">
<i className="fas fa-key fa-6x"></i>
<h1 className="display-3">You've Discovered My Secret!</h1>
<p className="secret-text">Jack Bauer is my hero.</p>
<hr />
<a className="btn btn-light btn-lg" href="/logout" role="button">Log Out</a>
<a className="btn btn-dark btn-lg" href="/submit" role="button">Submit a Secret</a>
</div>
</div>
);
}
export default Secret;
-------------- actions --------------
import { fail, user, USER_LOGIN_FAIL, USER_LOGIN_REQUEST, USER_LOGIN_SUCCESS,
USER_REGISTER_FAIL, USER_REGISTER_REQUEST, USER_REGISTER_SUCCESS
} from "../constants/all";
import axios from "axios";
const user_Register = (email, password) => async (dispatch) =>{
dispatch({ type: USER_REGISTER_REQUEST });
try {
const {data} = await axios( {
method: "POST",
data: {
"username": email,
"password": password
},
withCredentials: true,
url: "http://localhost:3001/register"
});
dispatch({ type: USER_REGISTER_SUCCESS, payload: data });
} catch (error) {
dispatch({type: USER_REGISTER_FAIL, error: error.message});
}
}
const user_Login = (email, password) => async (dispatch) =>{
dispatch({ type: USER_LOGIN_REQUEST });
try {
const {data} = await axios( {
method: "POST",
data: {
username: email,
password: password
},
withCredentials: true,
url: "http://localhost:3001/login"
});
dispatch({ type: USER_LOGIN_SUCCESS, payload: data });
} catch (error) {
dispatch({type: USER_LOGIN_FAIL, error: error.message});
}
}
const user_ = async (dispatch) => {
try{
const {data} = await axios({
method: "GET",
withCredentials: true,
url: 'http://localhost:3001'
});
if(data.isAuth){
dispatch({type:user, payload:data});
}
else{
dispatch({type: fail});
}
}
catch (error){
}
}
export {user_Register, user_Login, user_};
Who have better idea? Thanks a lot!
Can you try re-writing your user_ action creator to be a function that returns a function:
const user_ = () => async (dispatch) => {
try {
const { data } = await axios({
method: "GET",
withCredentials: true,
url: "http://localhost:3001",
});
if (data.isAuth) {
dispatch({ type: user, payload: data });
} else {
dispatch({ type: fail });
}
} catch (error) {}
};
And then in your Secret component's useEffect, call the user_ action creator to create the action:
useEffect(() => {
dispatch(user_());
}, []);
I'm not positive this is the issue, but it does seem like something that should be done either way.

Express server "failed to load resource: the server responded with a status of 500" after fetch request to server. How do I fix this error?

When a user enters their email and password then click login a fetch request is made to my server:
const RegistrationForm = (props) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleLoginSuccess = (props) => {
return props.history.push("./homepage");
};
const handleSubmit = (e) => {
e.preventDefault();
authApiService
.userLogin({ email, password })
.then((res) => {
password.value = "";
TokenService.saveAuthToken(res.authToken);
handleLoginSuccess();
})
.catch((res) => {
console.log(res.error);
});
};
return (
<form onSubmit={handleSubmit}>
<fieldset>
<div className="form-group">
<div>
<label htmlFor="registration-email">Email</label>
</div>
<EmailInput
value={email}
handleChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="form-group">
<div>
<label htmlFor="registration-password">Password</label>
</div>
<PasswordInput
value={password}
handleChange={(e) => setPassword(e.target.value)}
/>
</div>
</fieldset>
<Button type="submit" theme="registration-button">
Log in
</Button>
<ul>
<li>
<Link to="/register-account">Create account</Link>
</li>
</ul>
</form>
);
};
Fetch request is made here:
userLogin({ email, password }) {
return fetch(`${config.API_ENDPOINT}/auth/login`, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({ email, password }),
})
.then((res) => {
!res.ok ? res.json().then((e) => Promise.reject(e)) : res.json();
})
.then((res) => {
TokenService.saveAuthToken(res.authToken);
IdleService.registerIdleTimerResets();
TokenService.queueCallbackBeforeExpiry(() => {
authApiService.postRefreshToken();
});
return res;
});
},
postRefreshToken() {
return fetch(`${config.API_ENDPOINT}/auth/refresh`, {
method: "POST",
headers: {
authorization: `Bearer ${TokenService.getAuthToken()}`,
},
})
.then((res) =>
!res.ok ? res.json().then((e) => Promise.reject(e)) : res.json()
)
.then((res) => {
TokenService.saveAuthToken(res.authToken);
TokenService.queueCallbackBeforeExpiry(() => {
authApiService.postRefreshToken();
});
return res;
})
.catch((err) => {
console.log("refresh token req error");
console.log(err);
});
},
Then on the server this is the route for this request:
authRouter.post("/login", jsonBodyParser, (req, res, next) => {
const { email, password } = req.body;
const userLoggingIn = { email, password };
for (const [key, value] of Object.entries(userLoggingIn))
if (value == null)
return res
.status(400)
.json({ error: `Missing '${key}' in request body` });
authService
.confirmUserNameExists(req.app.get("db"), userLoggingIn.email)
.then((userInDb) => {
if (!userInDb)
return res
.status(400)
.json({ error: "Incorrect email or password" });
});
return authService
.comparePasswords(userLoggingIn.password, userInDb.password)
.then((matchedPw) => {
if (!matchedPw)
return res
.status(400)
.json({ error: "Incorrect email or password" });
const subject = userInDb.email;
const payload = { userId: userInDb.id };
res.send({ authToken: authService.createJwt(subject, payload) });
})
.catch(next);
});
authRouter.post("/refresh", requiresAuthorization, (req, res) => {
const subject = req.user.email;
const payload = { userId: req.user.id };
res.send({
authToken: authService.createJwt(subject, payload),
});
});
Im getting the error in the title (500 error, failed to load resource and its pointing to the fetch request. I've tried googling and looking on stack overflow for hours and can't figure it out. Any help would be greatly appreciated. The goal is just to get the user logged in, redirect the user to the home page, create a JWT and store it.

Resources