Axios: How to attach X-XSRF-TOKEN header manually? - node.js

I'm developing an SSR app with Vue. The API is made using Laravel framework and i'm using axios to send requests to the API.
I have to make a POST request on server-side to fetch it's response and render it on initial page load. To fetch the CSRF token for the POST request, the Laravel API provides an endpoint that sends the CSRF response as JSON (instead of a cookie because cookies cannot be used on server-side).
I'm attaching the csrf token in the following way:
this._vm.$api.post('/api/ssr-csrf-token').then(response => {
console.log("Token: ", response.data.token); // This shows that csrf is fetched correctly
let config = {
headers: {
'X-XSRF-TOKEN': response.data.token
}
};
this._vm.$api.post('/api/product/categories', { some: "thing"}, config).then(response => {
console.log("Categories: ", response.data.data);
}).catch(err => {
console.log(err);
})
}).catch(err => {
console.log(err);
})
But sending the request this way still gives the "CSRF Token Mismatch" error. Even though the token is attached to the request.
When I use Postman to fetch the results on categories endpoint, everything works fine and I get the response as should. But making a request from my SSR project I'm not receiving the response.
What do I need to do to get the response? Any help will be appreciated.
Thanks

You can attach the token to axios.defaults.headers.common.
next time, when you make a call, it auto sets your token to the header.
this._vm.$api.post('/api/ssr-csrf-token').then(response => {
console.log("Token: ", response.data.token); // This shows that csrf is fetched correctly
// set token to your header
axios.defaults.headers.common['Authorization'] =
'Bearer ' + response.data.response.access_token;
this._vm.$api.post('/api/product/categories', { some: "thing"}).then(response => {
console.log("Categories: ", response.data.data);
}).catch(err => {
console.log(err);
})
}).catch(err => {
console.log(err);
})
Now you don't have to include the config variable in your request. it auto sets to the header on your all calls.

Related

Why doesn't fetch reach the specified url?

...
const userIcon = document.querySelector('.user-icon');
userIcon.addEventListener("click", () => {
if (token) {
fetch('/privatearea', {
headers: {
'authorization': `Bearer ${token}`
}
}).catch(err => console.log(err));
} else {
window.location.assign('/login');
}
...
What i'm trying to do is:
When the element "userIcon" is clicked if "token" exists, i want to fetch the url "http://localhost:3000/privatearea".
If token doesn't exist the url "http://localhost:3000/login" is reached.
...
router.get('/privatearea', authenticateToken, (req, res) => {
res.render("private-area");
});
...
The backend has been done with node js and express.
So, if i click the element the url "http://localhost:3000/privatearea" should be reached and the page "private-area" should be renderized.
But it doesn't happen. I don't need a response, but i would reach the url by setting some headers.
fetch() by itself does not render anything in the browser. It just returns content back to your Javascript. So, if you had a .then() handler on the fetch() call, you would just get back the rendered data from res.render("private-area"); on your server.
userIcon.addEventListener("click", () => {
if (token) {
fetch('/privatearea', {
headers: {
'authorization': `Bearer ${token}`
}
}).then(data => {
// here you will see the result of res.render("private-area");
// on your server
console.log(data);
}).catch(err => console.log(err));
} else {
window.location.assign('/login');
}
...
}
So, the way you have it isn't the correct design for using fetch(). Since you don't show the larger context of this feature here, it's not entirely clear what the best architecture/solution would be here. Here are some options:
You could take the data from the .then() handler I show above and insert it into the current page using your own Javascript.
You could do window.location = '/privatearea'; and find some other way to communicate the Bearer token to your server (perhaps a previous call to your server sets a session cookie) so your server can use the session cookie to verify that the browser is allowed to enter /privatearea.
You could switch your auth model to something that is more friendly to browser URLs (like a session/cookie) since you can't use a Bearer Token from a URL entered into the browser bar as they can only be used from Javascript. The user logs in, gets assigned a session cookie and URL handlers on your server for URLs from the browser check that.

How is correctly way to send firebase auth tokenId between front-end and node.js backend

I am during create a chat-app. I want to find a good way to safely connect front and backend using a TokenId from firebase auth.
Do I have to in every request from my front to backend send a TokenId?
It is possible to do it in session or something like that I can send token once and verified it once time too?
My current road.
My code front end where I send a token:
fetch('http://localhost:5500/check', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ token: token }),
})
.then(data => data.json())
.then(e => console.log(e))
.catch(err => console.log(err));
});
I get token on front end like that:
firebase.auth().currentUser.getIdToken(true)
My code backend verify token:
router.post('/check', urlencodedParser, (req, res) => {
const {token} = req.body;
admin
.auth()
.verifyIdToken(token)
.then(function (decodedToken) {
// let uid = decodedToken.uid;
res.setHeader('Content-Type', 'application/json');
res.send({status: 'Ok'});
})
.catch(function (error) {
console.log(error);
res.setHeader('Content-Type', 'application/json');
res.send({status: 'err'});
});
});
Do I have to in every request from my front to backend send a TokenId?
That is what the Firebase clients and back-end services do: they send (and reverify) the ID token with every request. The server caches is the public key that is needed to decode the token, but aside from that are pretty much stateless with regards to the user/ID token.
It is possible to do it in session or something like that I can send token once and verified it once time too?
You can definitely keep the token in a session, but that means that you'll need to send the session ID instead of the token itself, and have to implement state management for your sessions.

Express response methods don't work after verifying ID token from Firebase through a middleware

I am trying to build a web application using Firebase and NodeJS with ejs templates at the client. I want to make sure the user is authenticated before accessing a particular route through a middleware. In this middleware, I verify ID tokens sent through the client and then render the page if they are verified. But when the tokens are verified the app doesn't show the new page i.e. "user-profile.ejs".
<button onclick="sendRequest('/profile')">Profile</button>
//The client function which sends ID token
function sendRequest(url){
if(firebase.auth().currentUser){
firebase.auth().currentUser.getIdToken(true)
.then(idToken => {
client({
method: 'get',
url: url,
headers: {
'authtoken': idToken
}
})
.then(res => {
console.log("Auth token sent.")
})
.catch(err => {
console.log(err)
})
})
.catch(err => {
console.log(err)
})
}else{
client({
method: 'get',
url: url,
}).then(res => {
console.log("Request sent without header.")
})
.catch(err => {
console.log(err)
})
}
}
//The NodeJS server which contains the routes and checkAuth middleware
function checkAuth(req,res,next){
if(req.headers.authtoken){
console.log('Auth token with headers received. User has logged in.')
admin.auth().verifyIdToken(req.headers.authtoken)
.then(verifiedUser => {
console.log('Auth token verified')
return next()
})
.catch(err => {
console.log(err)
res.redirect('back')
})
}else{
console.log('Without header received.')
res.redirect('back')
}
}
app.get('/',(req,res)=>{
res.render('home')
})
app.get('/profile',checkAuth,(req,res)=>{
console.log("Reached user profile")
res.send("user-profile")
})
Remember that when you redirect, the client get the redirected URL and issues an entirely new http request. That starts a completely new request cycle on your server that will go through all your middleware again that matches that new URL.
Automatic client redirects do NOT include custom headers from the original response. If that's what you were expecting, that will not work. If you're following a redirect manually from an Ajax call, you can manually get the token from the redirect response header and manually add it to a new Ajax call to the redirected location.
If you're expecting the browser or some automatic redirect to handle the redirect, they won't take the token with them from the headers. So, your server will have to either send the response to the original URL without redirecting or you will have to put the token in something that will survive the redirect such as a query parameter on the redirect (usually a bad idea for a credential) or in a cookie or in a server-side session object that is tied to a cookie.

Maintaining express sessions when moving between static and dynamic pages on client side

I am trying to set up express sessions for user authentication.
I have a node.js backend, and angular and static page front end (two front ends).
My node backend accepts username and password for authentication on my route
as a post request http://localhost:3000/users/login
My journey is as follows:
1. User is presented with a static front end login page, (this is designed by injecting vue.js with axios on a static html page), wherein he requests a log in with his credentials. The client frontend is hosted on http://localhost:3011/
2. The application will now send a post request to http://localhost:3000/users/login to verify the credentials.
3. If the credentials are valid, the server will create a new session (express-session) using req.session. In this session, I also store my token that I use to authenticate the user for any subsequent requests (this token is saved in the db and verified for all further requests). The server will then respond with a 200 OKAY status.
4. Once the vue application gets a positive response, the client will redirect the application to http://localhost:3000/ where I have angular application in the dist folder.
If, in POSTMAN, I do a post to http://localhost:3000/users/login and then do a GET on http://localhost:3000/, I can see the token in the logs when the server responds to the GET request.
But, if i send a post request to http://localhost:3000/users/login from my vue application, and then redirect to http://localhost:3000/ on successful authentication, I cannot see the token in the logs.
Code Snippet on the client side (Vue)
submit() {
this.errMsg = "";
axios
.post("http://localhost:3000/users/login", {
user: this.user,
password: this.password
})
.then(response => {
window.location = "http://localhost:3000/";
})
.catch(err => {
console.log(err);
this.errMsg = err.data.statusText;
});
}
Code inside login on server side
router.route("/login").post((req, res) => {
let { user, password } = req.body;
merchant_table
.findOne({ attributes: ["id"], where: { email, password } })
.then(result => {
if (result) {
let token = 12345;
token_table
.create({ m_id: result.id, token })
.then(result => {
req.session.user = result.token;
console.log("session is", req.session); //THIS SHOWS THE TOKEN
res.status(200).json({status:"OKAY!"})
});
} else {
res.status(401).json({status:"Invalid User!"})
}
})
.catch(error => console.log(error));
});
Code inside the request API for http://localhost:3000/
app.use("/", function(req, res) {
console.log("Session is", req.session); //THIS SHOWS THE TOKEN FOR POSTMAN, NOT WHEN WORKING WITH VUE
res.sendFile(__dirname + "/dist/index.html");
});
Since the axios post request and the subsequent redirect is to http://localhost:3000/ I expected the token to be maintained. But it seem to assume this as a new connection. Is this approach wrong? Is there a way to keep track of the token on redirect? I need the token only once, because then I will store it in the angular application and send it for any other requests that I need.
How do I go about with this?

Save jwt to local storage

I'm currently developing a node express postgresql application, and I'm trying to implement Jsonwebtokens as authentication. I've seen multiple tutorials on how to implement it and I get how to do it on the backend part, but the frontend is usually skipped and apparently everyone just tests their code with Postman.
I have also read online that the recommended way to implement jwt authentication is to store the generated token in localstorage, and, when needed, to send it on the header. But I wasn't able to find how this is done...
Thus, my questions are:
How do you store the token on the front-end once it's generated by the backend? (an example would help a lot, because I don't really get how am I supposed to get the token on a front-end javascript program)
How do you send the token on the headers when making an http request that needs it once you have it stored?
On the server side, once you have created the token and logged the user in, you send the token via res.send(), example below, note that you may have different approach to functions findByCredentials ad genereateAuthToken, they are custom:
app.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateAuthToken();
res.send({ token: user.tasks });
} catch (e) {
res.status(400).send();
}
});
On the frontend you can use html5's fetch() to send the token in the header. For example, if you would like to access '/users/me' that needs authentication you follow the steps below (make sure you however you save the token to localstorage first so you can access that via getItem:
localStorage.setItem('userInfo', JSON.stringify(userInfo));
document.getElementById("my-profile").addEventListener("click", getMe);
then:
function getMe(e) {
e.preventDefault();
var token = JSON.parse(localStorage.getItem('token'));
console.log(`Authorization=Bearer ${token}`)
fetch('/users/me', {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token
}
})
.then(res => res.json())
.then(data => {
console.log(data)
// window.location.href = 'http://localhost:3000/dashboard';
})
.catch(err => { console.log(err) })
}
As you said, usually the token is store in localStorage.
localStorage is similar to sessionStorage, except that while data
stored in localStorage has no expiration time, data stored in
sessionStorage gets cleared when the page session ends — that is, when
the page is closed.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
For getting the token in front-end you send to a URL the email & password of the user in order to exchange it with a token (you have to be in https). After that you store it with localStorage.setItem('key', value)
Short example:
$.post("/authenticate", {email: userEmail, password: userPassword}, function(data) {
localStorage.setItem('token', data.token)
});
For get back the token, after a refresh for example, you have to use : localStorage.getItem('key').
And finally, in order to be authenticate with this token, you can send it in bearer headers in Authorization headers property.
Why bearer ? => https://security.stackexchange.com/questions/108662/why-is-bearer-required-before-the-token-in-authorization-header-in-a-http-re
Example:
$.ajax({
type: 'GET',
url: '/account,
headers: {
"Authorization": "Bearer " + token
}
}, function(data) {
// Authenticated data
});
May this can help : https://github.com/auth0-blog/angularjs-jwt-authentication-tutorial/blob/master/frontend/login/login.js

Resources