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:
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user