1
0
mirror of https://github.com/sasjs/server.git synced 2025-12-11 03:34:35 +00:00

Merge pull request #248 from sasjs/cookies-management

fix(cookie): XSRF cookie is removed and passed token in head section
This commit is contained in:
Allan Bowe
2022-08-04 13:06:30 +01:00
committed by GitHub
7 changed files with 54 additions and 19 deletions

View File

@@ -1,6 +1,6 @@
import path from 'path' import path from 'path'
import express, { ErrorRequestHandler } from 'express' import express, { ErrorRequestHandler } from 'express'
import csrf from 'csurf' import csrf, { CookieOptions } from 'csurf'
import cookieParser from 'cookie-parser' import cookieParser from 'cookie-parser'
import dotenv from 'dotenv' import dotenv from 'dotenv'
@@ -32,9 +32,10 @@ const app = express()
const { PROTOCOL } = process.env const { PROTOCOL } = process.env
export const cookieOptions = { export const cookieOptions: CookieOptions = {
secure: PROTOCOL === ProtocolType.HTTPS, secure: PROTOCOL === ProtocolType.HTTPS,
httpOnly: true, httpOnly: true,
sameSite: PROTOCOL === ProtocolType.HTTPS ? 'none' : undefined,
maxAge: 24 * 60 * 60 * 1000 // 24 hours maxAge: 24 * 60 * 60 * 1000 // 24 hours
} }

View File

@@ -39,12 +39,11 @@ describe('web', () => {
describe('home', () => { describe('home', () => {
it('should respond with CSRF Token', async () => { it('should respond with CSRF Token', async () => {
await request(app) const res = await request(app).get('/').expect(200)
.get('/')
.expect( expect(res.text).toMatch(
'set-cookie', /<script>document.cookie = '(XSRF-TOKEN=.*; Max-Age=86400; SameSite=Strict; Path=\/;)'<\/script>/
/_csrf=.*; Max-Age=86400000; Path=\/; HttpOnly,XSRF-TOKEN=.*; Path=\// )
)
}) })
}) })
@@ -154,10 +153,10 @@ describe('web', () => {
const getCSRF = async (app: Express) => { const getCSRF = async (app: Express) => {
// make request to get CSRF // make request to get CSRF
const { header } = await request(app).get('/') const { header, text } = await request(app).get('/')
const cookies = header['set-cookie'].join() const cookies = header['set-cookie'].join()
const csrfToken = extractCSRF(cookies) const csrfToken = extractCSRF(text)
return { csrfToken, cookies } return { csrfToken, cookies }
} }
@@ -177,7 +176,7 @@ const performLogin = async (
return { cookies: newCookies } return { cookies: newCookies }
} }
const extractCSRF = (cookies: string) => const extractCSRF = (text: string) =>
/_csrf=(.*); Max-Age=86400000; Path=\/; HttpOnly,XSRF-TOKEN=(.*); Path=\//.exec( /<script>document.cookie = 'XSRF-TOKEN=(.*); Max-Age=86400; SameSite=Strict; Path=\/;'<\/script>/.exec(
cookies text
)![2] )![1]

View File

@@ -26,6 +26,7 @@ export const style = `<style>
} }
.app-container .app img{ .app-container .app img{
width: 100%; width: 100%;
height: calc(100% - 30px);
margin-bottom: 10px; margin-bottom: 10px;
border-radius: 10px; border-radius: 10px;
} }

View File

@@ -11,11 +11,15 @@ webRouter.get('/', async (req, res) => {
try { try {
response = await controller.home() response = await controller.home()
} catch (_) { } catch (_) {
response = 'Web Build is not present' response = '<html><head></head><body>Web Build is not present</body></html>'
} finally { } finally {
res.cookie('XSRF-TOKEN', req.csrfToken()) const codeToInject = `<script>document.cookie = 'XSRF-TOKEN=${req.csrfToken()}; Max-Age=86400; SameSite=Strict; Path=/;'</script>`
const injectedContent = response?.replace(
'</head>',
`${codeToInject}</head>`
)
return res.send(response) return res.send(injectedContent)
} }
}) })

View File

@@ -125,8 +125,27 @@ const verifyCORS = (): string[] => {
if (CORS) { if (CORS) {
const corsTypes = Object.values(CorsType) const corsTypes = Object.values(CorsType)
if (!corsTypes.includes(CORS as CorsType)) if (!corsTypes.includes(CORS as CorsType))
errors.push(`- CORS '${CORS}'\n - valid options ${corsTypes}`) errors.push(`- CORS '${CORS}'\n - valid options ${corsTypes}`)
if (CORS === CorsType.ENABLED) {
const { WHITELIST } = process.env
const urls = WHITELIST?.trim()
.split(' ')
.filter((url) => !!url)
if (urls?.length) {
urls.forEach((url) => {
if (!url.startsWith('http://') && !url.startsWith('https://'))
errors.push(
`- CORS '${CORS}'\n - provided WHITELIST ${url} is not valid`
)
})
} else {
errors.push(`- CORS '${CORS}'\n - provide at least one WHITELIST URL`)
}
}
} else { } else {
const { MODE } = process.env const { MODE } = process.env
process.env.CORS = process.env.CORS =

View File

@@ -22,7 +22,7 @@ function App() {
<HashRouter> <HashRouter>
<Header /> <Header />
<Routes> <Routes>
<Route path="/" element={<Login />} /> <Route path="*" element={<Login />} />
</Routes> </Routes>
</HashRouter> </HashRouter>
</ThemeProvider> </ThemeProvider>

View File

@@ -80,7 +80,18 @@ const AppContextProvider = (props: { children: ReactNode }) => {
}) })
.catch(() => { .catch(() => {
setLoggedIn(false) setLoggedIn(false)
axios.get('/') // get CSRF TOKEN // get CSRF TOKEN and set cookie
axios
.get('/')
.then((res) => res.data)
.then((data: string) => {
const result =
/<script>document.cookie = '(XSRF-TOKEN=.*; Max-Age=86400; SameSite=Strict; Path=\/;)'<\/script>/.exec(
data
)?.[1]
if (result) document.cookie = result
})
}) })
axios axios