mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
44 lines
2.3 KiB
JavaScript
44 lines
2.3 KiB
JavaScript
import { createHash, randomBytes } from 'crypto'
|
|
import * as cookie from './cookie'
|
|
|
|
/**
|
|
* Ensure CSRF Token cookie is set for any subsequent requests.
|
|
* Used as part of the strategy for mitigation for CSRF tokens.
|
|
*
|
|
* Creates a cookie like 'next-auth.csrf-token' with the value 'token|hash',
|
|
* where 'token' is the CSRF token and 'hash' is a hash made of the token and
|
|
* the secret, and the two values are joined by a pipe '|'. By storing the
|
|
* value and the hash of the value (with the secret used as a salt) we can
|
|
* verify the cookie was set by the server and not by a malicous attacker.
|
|
*
|
|
* For more details, see the following OWASP links:
|
|
* https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie
|
|
* https://owasp.org/www-chapter-london/assets/slides/David_Johansson-Double_Defeat_of_Double-Submit_Cookie.pdf
|
|
* @param {import("..").NextAuthRequest} req
|
|
* @param {import("..").NextAuthResponse} res
|
|
*/
|
|
export default function csrfTokenHandler (req, res) {
|
|
const { cookies, secret } = req.options
|
|
if (cookies.csrfToken.name in req.cookies) {
|
|
const [csrfToken, csrfTokenHash] = req.cookies[cookies.csrfToken.name].split('|')
|
|
const expectedCsrfTokenHash = createHash('sha256').update(`${csrfToken}${secret}`).digest('hex')
|
|
if (csrfTokenHash === expectedCsrfTokenHash) {
|
|
// If hash matches then we trust the CSRF token value
|
|
// If this is a POST request and the CSRF Token in the POST request matches
|
|
// the cookie we have already verified is the one we have set, then the token is verified!
|
|
const csrfTokenVerified = req.method === 'POST' && csrfToken === req.body.csrfToken
|
|
req.options.csrfToken = csrfToken
|
|
req.options.csrfTokenVerified = csrfTokenVerified
|
|
return
|
|
}
|
|
}
|
|
// If no csrfToken from cookie - because it's not been set yet,
|
|
// or because the hash doesn't match (e.g. because it's been modifed or because the secret has changed)
|
|
// create a new token.
|
|
const csrfToken = randomBytes(32).toString('hex')
|
|
const csrfTokenHash = createHash('sha256').update(`${csrfToken}${secret}`).digest('hex')
|
|
const csrfTokenCookie = `${csrfToken}|${csrfTokenHash}`
|
|
cookie.set(res, cookies.csrfToken.name, csrfTokenCookie, cookies.csrfToken.options)
|
|
req.options.csrfToken = csrfToken
|
|
}
|