In my react project, I have been using axios to help get data from MongoDB.
My axios get requests return a 401 error when I add my auth middleware as a param. This middleware requires the user to have a valid JWT token before any data is returned.
When making the request with Insomnia/Postman, as long as long as the user token is added to the request headers, I always get the desired data returned. However, when I try to get the data within the react application, I get a "Failed to load resource: the server responded with a status of 401 (Unauthorized)" error log, along with my custom json error response, "No Auth token, authorisation denied!"
I built a simple boolean function that returns true if the current user has a valid JWT token. Despite returning true, I am still receiving the 401 error as a response, which leads me to suspect there is a syntax or formatting error somewhere inside my react code.
Backend (with express):
router.get("/allauth", auth, async (req, res) => {
const players = await Player.find();
res.json(players);
});
Auth:
const jwt = require("jsonwebtoken");
const auth = (req, res, next) => {
try {
const token = req.header("x-auth-token");
if (!token)
return res.status(401)
.json({ msg: "No Auth token, authorisation denied!" });
//match token against .env password
const verified = jwt.verify(token, process.env.JWT_SECRET);
if (!verified)
return res.status(401)
.json({ msg: "Token verification failed, authorisation denied!" });
req.user = verified.id;
next();
} catch (err) {
res.status(500).json({ error: err.message });
}
};
React frontend:
export default function DisplayTeam() {
const [players, setPlayers] = useState([]);
const {userData} = useContext(UserContext);
useEffect(() => {
let token = localStorage.getItem("auth-token");
const url = "http://localhost:5000/players";
console.log(token);
axios.get(`${url}/allauth`, {
headers: {
"Authorization": `x-auth-token ${token}`
}
})
.then( response => setPlayers(response.data))
.catch( error => console.log(error));
}, []);
return (//display from DB);
Based on your server config, you need to pass X-Auth-Token header not Authorization,
it should be:
headers: {
"X-Auth-Token": token
}
Also, Take into consideration that headers are Case Sensitive!
Related
Here is the situation:
I have a database which contains a user and password registered.
My assignment, for now, is to create a login form, and login with a registered uname and pw.
Uname and pw are registered in the server/database already.
ps: I did not create the server nor database.
Node server code
import express from 'express';
import cors from 'cors';
import http from 'http';
import { Sequelize } from 'sequelize';
import { Data } from './database';
import { router } from './routes/Router';
import { initialData } from './database/someData';
const closeServer = async (
server: http.Server,
sequelize: Sequelize,
signal?: string
) => {
server.close();
await sequelize.close();
process.exit();
};
const runServer = async (): Promise<void> => {
const PORT = process.env.PORT || 8082;
const app = express();
const sequelize = Data.init();
app.use(
cors({
credentials: true,
origin: 'http://localhost:3000',
})
);
app.use('/api', router);
const server = app.listen(PORT, () => {
console.log(`Starting server at ${PORT}`);
});
try {
await sequelize.authenticate();
await sequelize.sync({
force: process.env.SERVER === 'reset',
});
if (process.env.SERVER === 'reset') await initialData();
} catch (e) {
closeServer(server, sequelize);
throw e;
}
};
runServer()
.then(() => {
console.log('Run successfully');
})
.catch((ex: Error) => {
console.log('Unable to run:', ex);
});
I need help on what is that I have to do.
When I input username and pw, on the form, what are the methods to use for sending the info?
And then, when the info reaches the server, i think the username and pw need to be validated with jwt, and then check if the user and pw exists. how do i do that?
What i have understood so far is that i gotta use axios to send info to server, but thats it.
Do i need to use jwt for the login?
What is the normal flow for this kind of mechanism?
I am using react as a framework.
So there are quite few steps here.
First you have to create endpoint on your backend server for issuing jwt tokens. Jwt tokens can be used as a pass for user to login. So in your router you would add something like this:
router.post('/login', (req, res)=> {
const username = req.body.username
const password = req.body.password
// Then you make db call to verify username and password are correct.
if Credentials are valid, you would issue jwt token
jwt.sign({
// here you can save extra information of user. Also remember this information must be public since anyone can see it. Do not put user password here
email: 'email',
userId: 'id',
}, "secret")
})
After this, you need some kind of middleware on backend, so that on each user request, you check and verify this jwt token which is sent from react application. For example you could write isAuth middleware:
const jwt =require("jsonwebtoken");
export const isAuth= (req, res, next) => {
try {
// here we attach request in auth header, with Bearer "jwt token" format. So we extract jwt token and verify it
const authHeader = req.get("Authorization");
if (!authHeader) {
return res.status(401).json({ message: "no token" });
}
const token = authHeader.split(" ")[1];
let decodedToken;
decodedToken = jwt.verify(token, "secret");
if (!decodedToken) {
return res.status(401).json({ message: "Wrong token" });
}
req.userId = decodedToken.userId;
next();
} catch (err) {
console.error(err);
return res.status(401).json({ message: err });
}
};
Now you would be able to have backend endpoints like this:
// This is how you would require login on some routes
router.post("/getMyPrivateInfo", isAuth, QueryPrivatInfo)
Now on React side, you would make request for login like this:
axios.post("/login", {
username: '1',
password: "2"
})
This would return jwt token, now you would save this token in local storage.
After its saved in local storage and you make request with axios for private info you would do following
axios.post("/getMyPrivateInfo", {any request body info neeeded}, {
headers: {
Authorization: "Bearer jwtTokenFromLocalStorage"
}
})
This is how whole flow will work, hope it makes sense
I am currently designing a simple website, where users can log in as a normal user or as an admin. Right now I am coding out the portion for when the user wants to go to an admin only web page, and the server will retrieve the jwt token stored in the local storage on the web browser to validate it.
This is what the local storage looks like
Here is the code for retrieving the jwt token
var verifyFn = {
verifyToken: function (req, res, next) {
const authHeader = localStorage.getItem("jwt_token");
console.log("THIS IS THE HEADER")
console.log(authHeader)
if (authHeader === null || authHeader === undefined ){
return res.status(401).send({ message: 'not authenticated BEARER TOKEN ISSUE' });
}
const token = authHeader
console.log("NEW TOKEN")
console.log(token)
jwt.verify(token, config.jwt.secret, { algorithms: ['HS256'] }, (err, decoded) => {
if (err) return res.status(401).send({ message: 'not authenticated' });
req.decodedToken = decoded;
console.log("DECODED TOKEN: " + req.decodedToken)
next();
});
}
However, whenever I try to run the server and browse to the admin page, there will be an error saying 'localstorage is not defined'. As such, I am not sure about how I can retrieve the jwt_token from the web browser to the server back end.
A server has no access to the browser's localStorage object, as it is accessible from the client only, and does not exist in the server context.
What is usually done is sending the token in an Authorization header. It looks like you are using Node, so consider the following example request using the fetch API on the client:
const jwtToken = localStorage.getItem('jwt_token')
fetch('your-api-url', {
method: 'request method here',
headers: {
Authorization: `Bearer ${jwtToken}`
},
body: JSON.stringify(your request body here)
}).then(response => ...)
In the server, you can then get the JWT token by looking at the request headers, something like this:
var verifyFn = {
verifyToken: function (req, res, next) {
let authHeader = req.headers['Authorization']
// the auth header will have Bearer prepended, so remove it
authHeader = authHeader.replace('Bearer ', '')
console.log("THIS IS THE HEADER")
console.log(authHeader)
if (authHeader === null || authHeader === undefined ){
return res.status(401).send({ message: 'not authenticated BEARER TOKEN ISSUE' });
}
const token = authHeader
console.log("NEW TOKEN")
console.log(token)
jwt.verify(token, config.jwt.secret, { algorithms: ['HS256'] }, (err, decoded) => {
if (err) return res.status(401).send({ message: 'not authenticated' });
req.decodedToken = decoded;
console.log("DECODED TOKEN: " + req.decodedToken)
next();
});
}
I am trying to implement Passport.js Google Login to my MERN.
But, I have this middleware:
const jwt = require("jsonwebtoken");
const config = require("config");
module.exports = async function(req, res, next) {
// Get token from header
const token = req.header("x-auth-token");
// Check if not token
if (!token) {
return res.status(401).json({ msg: "No token, authorization denied" });
}
// Verify token
try {
await jwt.verify(token, config.get("jwtSecret"), (error, decoded) => {
if (error) {
res.status(401).json({ msg: "Token is not valid" });
} else {
req.user = decoded.user;
next();
}
});
} catch (err) {
console.error("something wrong with auth middleware");
res.status(500).json({ msg: "Server Error" });
}
};
As you can see, JWT is asking for token because I made JWT auth before.
What should I write on my Google Login callback? Should I write JWT on Callback? Does this make sense?
router.get(
"/google/redirect",
passport.authenticate("google"),
async (req, res) => {
//here
}
);
I had the same problem.
I found the suggestion: redirect to the expected page with a cookie which holds the JWT.
Link here: Facebook-passport with JWT
However, if we store the jwt token in a cookie, then we cannot use the api for the mobile app.
As far as I know, getIdToken() should by default send an unexpired token to the server, handling the token refreshing internally. However, I am getting many many errors from my server regarding expired tokens.
On my frontend, I created axios instance as follows:
const base = axios.create({
baseURL: apiUrl,
});
base.interceptors.request.use(async (config) => {
const token = await getAccessToken();
config.headers.Authorization = `Bearer ${token}`;
return config;
});
const getAccessToken = async () => {
const user = firebase.auth().currentUser;
if (user) {
const token = await user.getIdToken();
return token;
}
return null;
};
Then on the receiving server, I verify the token by doing:
const admin = require("firebase-admin");
const auths = req.headers.authorization.split(" ");
admin.auth().verifyIdToken(auths[1]).then(async token => {
...
});
The error I receive from the server is:
Error: Firebase ID token has expired. Get a fresh ID token from your client app and try again (auth/id-token-expired). See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
If I change getIdToken() to getIdToken(true), the issue goes away.
I verified that the getAccessToken method is called for every request made. I'm a bit lost why my server continues to complain about expired tokens, any help would be appreciated.
I'm implementing React Authentication with ApolloGraphQL.
Context:
In signin.js, I'm generating the token when user clicks submit button and set it to the Localstorage.
Then, I'm retrieving the token in App.js to I can pass it to GraphQL, so that, it can be retrieved in in server.js.
Problem:
After user clicks the submit button, I can see the newly generation token in: Developer Tools > Application > Local Storage.
But it returning 'null' for 'client side token' in App.js
When I do the signin again, I'm seeing the previously generated token as the value of 'client side token', which means its not getting the updated token from the LocalStorage.
Obviously, because of this, 'server side token' is null for the first time and returning the previously generated token for the second time signin.
app/client/src/components/signin.js:
handleSubmit = (event, SignIn) => {
event.preventDefault();
SignIn().then(({ data }) => {
localStorage.setItem('token', data.SignIn.token);
this.clearState();
})
}
app/client/src/app.js:
//initiating the ApolloClient
const client = new ApolloClient({
uri: 'http://localhost:4000/graphql',
fetchOptions: {
credentials: 'include'
},
//adding token to the localstorage
request: operation => {
const token = localStorage.getItem('token');
operation.setContext({
headers:{
authorization: token
}
})
console.log(`client side token ${token}`);
},
//catching the most common network error, if any
onError: ({ networkError }) => {
if(networkError){
console.log('Network Error', networkError);
}
}
});
server.js:
const app = express();
app.use(async (req, res, next) => {
const token = req.headers['authorization'];
console.log(`server side token: ${token}`);
next();
});
you need somekind of auth middleware as request in apollo client is only getting called on construction i believe which is why the localStorage has the previous token when you reload the page.
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { ApolloLink, concat } from 'apollo-link';
const httpLink = new HttpLink({ uri: '/graphql' });
const authMiddleware = new ApolloLink((operation, forward) => {
// add the authorization to the headers
operation.setContext({
headers: {
authorization: localStorage.getItem('token') || null,
}
});
return forward(operation);
})
const client = new ApolloClient({
link: concat(authMiddleware, httpLink),
});
see https://www.apollographql.com/docs/react/advanced/network-layer/#middleware for more details