Skip to content

Commit 4b3363d

Browse files
authored
Merge pull request #86 from docusign/csrf-server-side-sessions
Add CSRF protection and server-side sessions to login
2 parents fa77730 + 50d0b88 commit 4b3363d

9 files changed

Lines changed: 130 additions & 42 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
private.key
66

77
# dependencies
8-
/node_modules
8+
*/node_modules
99
client/node_modules
1010
/.pnp
1111
.pnp.js

client/package-lock.json

Lines changed: 37 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/src/containers/Login/Login.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,22 @@ class Login extends Component {
6868
*/
6969
login = async (role) => {
7070
try {
71+
const apiUrl = process.env.REACT_APP_API_URL || 'http://localhost:5000/api';
72+
// Fetch a CSRF token bound to this session before triggering the
73+
// JWT-grant flow. The token must be echoed back in the request
74+
// header so the server can verify the call originates from this SPA.
75+
const csrfRes = await axios.get(`${apiUrl}/auth/csrf-token`, { withCredentials: true });
76+
const csrfToken = csrfRes.data.csrfToken;
77+
7178
//get the jwt stored in session cookie
72-
const apiUrl = process.env.REACT_APP_API_URL;
73-
let loginReq = await axios.get(`${apiUrl}/auth/login`);
79+
let loginReq = await axios.post(
80+
`${apiUrl}/auth/login`,
81+
{},
82+
{
83+
withCredentials: true,
84+
headers: { 'X-CSRF-Token': csrfToken },
85+
}
86+
);
7487
//if status is 210, redirect the user to the constent page
7588
if (loginReq.status === 210) {
7689
window.location = loginReq.data;

server/controllers/authController.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ const addNewOfficeToSessionIfMissing = async (req) => {
155155
//continue if it is a name already exists error
156156
let sameNameErrorMessage =
157157
'The office \'' + newOffice.name + '\' already exists. Please choose a different name.';
158-
if(error.response.body.message !== sameNameErrorMessage) {
158+
if(error?.response?.body?.message !== sameNameErrorMessage && error?.body?.message !== sameNameErrorMessage) {
159159
//send the error if you are out of tries
160160
if(tries === 5) {
161161
throw error;
@@ -173,6 +173,14 @@ const addNewOfficeToSessionIfMissing = async (req) => {
173173

174174

175175

176+
/**
177+
* Issues a fresh CSRF token for the session.
178+
* The client must call this before POSTing to /login.
179+
*/
180+
module.exports.getCsrfToken = (req, res) => {
181+
res.json({ csrfToken: req.csrfToken() });
182+
};
183+
176184
/**
177185
* Login user
178186
* request body:

0 commit comments

Comments
 (0)