mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #3892 from overleaf/sk-reroll-csrf
Regenerate CSRF token on login GitOrigin-RevId: 501582b34794a822f4c9fe3af2575b5756511e06
This commit is contained in:
parent
16d02c9d8b
commit
04fa863f9f
7 changed files with 80 additions and 30 deletions
|
@ -489,7 +489,7 @@ function _afterLoginSessionSetup(req, user, callback) {
|
||||||
// transferred to the new session below.
|
// transferred to the new session below.
|
||||||
for (let key in oldSession) {
|
for (let key in oldSession) {
|
||||||
const value = oldSession[key]
|
const value = oldSession[key]
|
||||||
if (key !== '__tmp') {
|
if (key !== '__tmp' && key !== 'csrfSecret') {
|
||||||
req.session[key] = value
|
req.session[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,43 @@ describe('Authentication', function() {
|
||||||
user = new User()
|
user = new User()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('CSRF regeneration on login', function() {
|
||||||
|
it('should prevent use of csrf token from before login', function(done) {
|
||||||
|
user.logout(err => {
|
||||||
|
if (err) {
|
||||||
|
return done(err)
|
||||||
|
}
|
||||||
|
user.getCsrfToken(err => {
|
||||||
|
if (err) {
|
||||||
|
return done(err)
|
||||||
|
}
|
||||||
|
const oldToken = user.csrfToken
|
||||||
|
user.login(err => {
|
||||||
|
if (err) {
|
||||||
|
return done(err)
|
||||||
|
}
|
||||||
|
expect(oldToken === user.csrfToken).to.equal(false)
|
||||||
|
user.request.post(
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'x-csrf-token': oldToken
|
||||||
|
},
|
||||||
|
url: '/project/new',
|
||||||
|
json: { projectName: 'test' }
|
||||||
|
},
|
||||||
|
(err, response, body) => {
|
||||||
|
expect(err).to.not.exist
|
||||||
|
expect(response.statusCode).to.equal(403)
|
||||||
|
expect(body).to.equal('Forbidden')
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('login', function() {
|
describe('login', function() {
|
||||||
beforeEach('doLogin', async function() {
|
beforeEach('doLogin', async function() {
|
||||||
await user.login()
|
await user.login()
|
||||||
|
|
|
@ -89,15 +89,20 @@ const tryFollowInviteLink = (user, link, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const tryAcceptInvite = (user, invite, callback) => {
|
const tryAcceptInvite = (user, invite, callback) => {
|
||||||
user.request.post(
|
user.getCsrfToken(err => {
|
||||||
{
|
if (err) {
|
||||||
uri: `/project/${invite.projectId}/invite/token/${invite.token}/accept`,
|
return callback(err)
|
||||||
json: {
|
}
|
||||||
token: invite.token
|
user.request.post(
|
||||||
}
|
{
|
||||||
},
|
uri: `/project/${invite.projectId}/invite/token/${invite.token}/accept`,
|
||||||
callback
|
json: {
|
||||||
)
|
token: invite.token
|
||||||
|
}
|
||||||
|
},
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const tryRegisterUser = (user, email, callback) => {
|
const tryRegisterUser = (user, email, callback) => {
|
||||||
|
|
|
@ -160,22 +160,10 @@ describe('UserHelper', function() {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('getCsrfToken', function() {
|
describe('getCsrfToken', function() {
|
||||||
describe('when the csrfToken is not cached', function() {
|
it('should fetch csrfToken', async function() {
|
||||||
it('should fetch csrfToken', async function() {
|
const userHelper = new UserHelper()
|
||||||
const userHelper = new UserHelper()
|
await userHelper.getCsrfToken()
|
||||||
await userHelper.getCsrfToken()
|
expect(userHelper.csrfToken).to.be.a.string
|
||||||
expect(userHelper.csrfToken).to.be.a.string
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('when the csrfToken is cached', function() {
|
|
||||||
it('should return cached csrfToken', async function() {
|
|
||||||
let userHelper = new UserHelper()
|
|
||||||
await userHelper.getCsrfToken()
|
|
||||||
const csrfToken = userHelper._csrfToken
|
|
||||||
await userHelper.getCsrfToken()
|
|
||||||
expect(csrfToken).to.equal(userHelper._csrfToken)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,18 @@ class User {
|
||||||
url: settings.enableLegacyLogin ? '/login/legacy' : '/login',
|
url: settings.enableLegacyLogin ? '/login/legacy' : '/login',
|
||||||
json: { email, password: this.password }
|
json: { email, password: this.password }
|
||||||
},
|
},
|
||||||
callback
|
(error, response, body) => {
|
||||||
|
if (error != null) {
|
||||||
|
return callback(error)
|
||||||
|
}
|
||||||
|
// get new csrf token, then return result of login
|
||||||
|
this.getCsrfToken(err => {
|
||||||
|
if (err) {
|
||||||
|
return callback(err)
|
||||||
|
}
|
||||||
|
callback(null, response, body)
|
||||||
|
})
|
||||||
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -123,9 +123,6 @@ class UserHelper {
|
||||||
* Requests csrf token unless already cached in internal state
|
* Requests csrf token unless already cached in internal state
|
||||||
*/
|
*/
|
||||||
async getCsrfToken() {
|
async getCsrfToken() {
|
||||||
if (this._csrfToken) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// get csrf token from api and store
|
// get csrf token from api and store
|
||||||
const response = await this.request.get('/dev/csrf')
|
const response = await this.request.get('/dev/csrf')
|
||||||
this._csrfToken = response.body
|
this._csrfToken = response.body
|
||||||
|
@ -255,6 +252,7 @@ class UserHelper {
|
||||||
if (!userHelper.user) {
|
if (!userHelper.user) {
|
||||||
throw new Error(`user not found for email: ${userData.email}`)
|
throw new Error(`user not found for email: ${userData.email}`)
|
||||||
}
|
}
|
||||||
|
await userHelper.getCsrfToken()
|
||||||
|
|
||||||
return userHelper
|
return userHelper
|
||||||
}
|
}
|
||||||
|
@ -298,6 +296,7 @@ class UserHelper {
|
||||||
if (!userHelper.user) {
|
if (!userHelper.user) {
|
||||||
throw new Error(`user not found for email: ${userData.email}`)
|
throw new Error(`user not found for email: ${userData.email}`)
|
||||||
}
|
}
|
||||||
|
await userHelper.getCsrfToken()
|
||||||
|
|
||||||
return userHelper
|
return userHelper
|
||||||
}
|
}
|
||||||
|
|
|
@ -1236,6 +1236,16 @@ describe('AuthenticationController', function() {
|
||||||
this.req.login.callCount.should.equal(1)
|
this.req.login.callCount.should.equal(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should erase the CSRF secret', function() {
|
||||||
|
this.AuthenticationController.finishLogin(
|
||||||
|
this.user,
|
||||||
|
this.req,
|
||||||
|
this.res,
|
||||||
|
this.next
|
||||||
|
)
|
||||||
|
expect(this.req.session.csrfSecret).to.not.exist
|
||||||
|
})
|
||||||
|
|
||||||
it('should call req.session.save', function() {
|
it('should call req.session.save', function() {
|
||||||
this.AuthenticationController.finishLogin(
|
this.AuthenticationController.finishLogin(
|
||||||
this.user,
|
this.user,
|
||||||
|
|
Loading…
Reference in a new issue