I am trying to add csrf protection to my react app but I am getting an error Invalid token all the time
import bodyParser from 'body-parser';
import cookieSession from 'cookie-session';
import passport from 'passport';
import csrf from 'csurf'
import config from '../../config'
import AuthRoutes from "./routes/AuthRoutes";
/* Test only */
import cookieParser from 'cookie-parser';
const session = cookieSession({
maxAge:24 * 60 * 60 * 1000,
keys:[config.COOKIE_KEY],
name:'authentication',
});
export default app => {
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.use(session);
app.use(passport.initialize());
app.use(passport.session());
/* Test */
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.use(function (err, req, res, next) {
if (err.code !== 'EBADCSRFTOKEN') return next(err)
// handle CSRF token errors here
res.status(403)
res.send('form tampered with')
})
/*Passport Config*/
require('../../services');
/* Register, Login these are routes i want to protect */
AuthRoutes(app);
}
You need to:
1. Configure csrf
library on the server. This ensures the library will send the first piece of data attached to the server responses.
2. Use csrf
library on the server to generate the second piece of data and attach it to the server response (e.g. HTML form sent to the client). After this step is completed the server response will carry two pieces of CSRF data.
3. On the client take the second piece of data and insert it into the request you are about to send (e.g. the form you are about to submit).
Step 1
So far only the step (1) has been completed. You asked the csrf
library to send the first piece of data as a cookie. You could have used a better configuration:
app.use(csrf({cookie: {
httpOnly: true,
}}));
It ensures the browser won't allow any JS on the client to touch the first piece of data inside the cookie which is good because there is no legit reason for any script to know what is inside this cookie. Later on, in production and when you use HTTPS, you can optionally add secure: true
to the above configuration to make the server refuse to send this cookie over connections that are not secure.
Step 2
To get the second piece of data call csrfToken()
. The csrf
middleware added another property to Request
object for your convenience so it can be called like this: const secondPiece = req.csrfToken()
. You can put the second piece of data into the server responce in any way or manner you like: into another cookie with an arbitrary name (except for the _csrf
name already taken by the step 1 cookie) or into HTTP header named as you like.
For example this code will put it into another cookie:
res.cookie('XSRF-TOKEN', req.csrfToken());
Step 3
On the client write JS to get the second piece of data and put it into one of the predefined places/locations (inside the request to be sent to the server) where csrf
middleware searches for it by default.
Just in case, any solutions doesn't work.
I just had days of debugging caused by incorrect naming of the cookie. Make sure to name your cookie XSRF-COOKIE
(take note it uses -
symbol) because I incorrectly named mine using underscore XSRF_COOKIE
and this bit my ass for days.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With