this is my first issue I am posting here so please bear with me 😀
I am creating a social app, where currently I am implementing fallback when my database is off. In my Login.jsx component I am fetching my data do the backend like this:
fetch(getApiUrl('/auth/login'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(values),
credentials: 'include',
})
.then(res => {
const jsn = res.json();
if (res.ok) {
handleAuthLogin(navigate, revalidate)(jsn);
} else if (jsn.message) {
toast.error(<div className='text-lg'>{jsn.message}</div>);
}
})
.catch((err) => {
console.error(err);
if (err.message === 'Failed to fetch') {
navigate('/service-unavailable');
} else if (err.message === 'Invalid token' || err.message === 'Unauthorized') {
navigate('/login');
} else {
toast.error(<div className='text-lg'>Failed to connect to the server. Please check your connection.</div>);
}
});
Then in my backend, I am using JWT to hash the password. Here is just a code snippet from my index.js file:
/**
* Middleware for handling authentication routes.
* All routes starting with '/auth' will be handled by the authRouter.
*/
app.use(checkToken);
app.use('/auth', authRouter);
app.use(userRouter);
// Set up the /users endpoint
app.get('/users', async (req, res) => {
const { name } = req.query.name;
// Query the database using Prisma
const users = await prisma.user.findMany({
where: {
name: {
contains: name,
},
},
});
// Send the result back to the client
res.json(users);
});
and the checkToken component:
const checkToken = (req, res, next) => {
const token = req.headers['authorization'];
console.log('token: ', token);
if (!token) {
return res.status(403).send({ message: 'No token provided.' });
}
try {
const decoded = decodeJwt(token);
req.userId = decoded.id;
next();
} catch (error) {
return res.status(401).send({ message: 'Unauthorized! Session expired.' });
}
};
as well as the form verification:
const formVerification = (req, res, action) => {
// Extract the data from the request body
const data = req.body;
console.log(data);
// Validate the data against the schema
schema
.validate(data)
.catch(err => {
// If validation fails, send a 422 status code and the validation error messages
res.status(422).json({ status: 422, message: err.errors });
})
.then(valid => {
if (valid) {
const cred = {
usr: data.username,
pwd: data.password
};
if (action === "login") {
// Login handle
handleLogin(cred, req, res);
} else if (action === "signup") {
// Signup handle
handleSignup(cred, req, res);
} else {
throw new Error("Invalid action");
}
} else {
res.status(400).json({ status: 400, message: "Invalid request" });
}
});
}
handleLogin component with encoding jwt:
const hashPwd = (pwd) => {
return createHash("md5").update(pwd).digest("hex");
};
const encodeJwt = ({ id, name }) => {
return sign({ id, name }, secret, { expiresIn: "1h" });
};
const decodeJwt = (jwt) => {
try {
return verify(jwt, secret);
} catch (e) {
throw new Error('Failed to decode JWT: ' + e.message);
}
};
const handleLogin = ({ usr, pwd }, req, res) => {
console.log('usr: ', usr, 'pwd: ', pwd);
getUser(usr, (err, user) => {
if (err) {
console.error(err);
return res.status(500).json({ status: 500, message: "Database is offline" });
}
console.log('user: ', user);
if (!user) {
return res.status(404).json({ status: 404, message: "User not found" });
} else if (user.credentials.hash !== hashPwd(pwd)) {
return res.status(401).json({ status: 401, message: "Invalid password" });
} else {
return res.status(200).json({ status: 200, jwt: encodeJwt(user), user: { id: user.id, name: user.name } });
}
}, true);
};
You may realize now that this is hard to pinpoint where exactly is the error and the code is a little large, so I’ll just link my repo for the whole code: https://github.com/Patri22k/social-app.
Finally, what is the error? On the mount I have an error user.js:29 GET http://localhost:5000/user/@me 401 (Unauthorized). Oh, btw this is user.js file:
// No token stored in local store
const NO_TOKEN_ERR = "no_token";
const UserContext = createContext(null);
const UserProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [fetching, setFetching] = useState(false);
const [error, setError] = useState(null);
// Obtain new data
const revalidate = () => {
// Obtain local JWT val
const jwt = localStorage.getItem("jwt");
if (!jwt) {
setError(NO_TOKEN_ERR);
return;
}
const init = {
method: "GET",
headers: {
'Authorization': `Bearer ${jwt}`
}
}
// Fetch that thing
fetch(getApiUrl("/user/@me"), init) // line 29
.then(res => res.json())
.then(({ user }) => setUser(user))
.catch((e) => setError(e.message))
.finally(() => setFetching(false))
}
// rest of the code
}
and after I pass any value to the form, the token is undefined and I got an error POST http://localhost:5000/auth/login 403 (Forbidden). But on the mount there is a hashed token.
What I want to achieve?
I’d like to have a fallback when my database is off, it will redirect to the /service-unavailable url. That’s it. When it’s on, it should work as any social app with form validation.