Skip to content

Commit

Permalink
feat: ✨ option to configure header name
Browse files Browse the repository at this point in the history
Resolves #38
  • Loading branch information
Morgbn committed Jul 17, 2024
1 parent 1c3073b commit 2e093cc
Show file tree
Hide file tree
Showing 7 changed files with 19 additions and 10 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ export default defineNuxtConfig({
methodsToProtect: ['POST', 'PUT', 'PATCH'], // the request methods we want CSRF protection for
encryptSecret: /** a 32 bits secret */, // only for non serverless runtime, random bytes by default
encryptAlgorithm: 'aes-256-cbc', // by default 'aes-256-cbc' (node), 'AES-CBC' (serverless)
addCsrfTokenToEventCtx: true // default false, to run useCsrfFetch on server set it to true
addCsrfTokenToEventCtx: true, // default false, to run useCsrfFetch on server set it to true
headerName: 'csrf-token' // the header where the csrf token is stored
}
})
```
Expand Down
3 changes: 2 additions & 1 deletion playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default defineNuxtConfig({
},
csurf: {
https: process.env.NODE_ENV === 'production',
methodsToProtect: ['POST']
methodsToProtect: ['POST'],
headerName: 'X-CSRF-TOKEN'
}
})
5 changes: 5 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default defineNuxtModule<ModuleOptions>({
httpOnly: true,
sameSite: 'strict'
},
headerName: 'csrf-token',
methodsToProtect: ['POST', 'PUT', 'PATCH']
},
setup (options, nuxt) {
Expand All @@ -31,6 +32,7 @@ export default defineNuxtModule<ModuleOptions>({
}

nuxt.options.runtimeConfig.csurf = defuReplaceArray(nuxt.options.runtimeConfig.csurf, { ...options })
nuxt.options.runtimeConfig.public.csurf = { headerName: nuxt.options.runtimeConfig.csurf.headerName }

if (options.enabled !== false) {
addServerHandler({ handler: resolve('runtime/server/middleware/csrf') })
Expand All @@ -54,6 +56,9 @@ declare module 'nuxt/schema' {
interface RuntimeConfig {
csurf: ModuleOptions
}
interface PublicRuntimeConfig {
csurf: Pick<ModuleOptions, 'headerName'>
}
}

declare module 'nitropack' {
Expand Down
11 changes: 6 additions & 5 deletions src/runtime/composables.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Adapted from https://github.com/nuxt/nuxt/blob/7046930a677f4c987afaee5a0165841b0e0a517f/packages/nuxt/src/app/composables/fetch.ts
import { useFetch, type FetchResult, type UseFetchOptions, useNuxtApp } from '#app'
import { useFetch, type FetchResult, type UseFetchOptions, useNuxtApp , useRuntimeConfig } from '#app'
import type { Ref } from 'vue'
import type { FetchError } from 'ofetch'
import type { NitroFetchRequest, AvailableRouterMethod as _AvailableRouterMethod } from 'nitropack'
Expand Down Expand Up @@ -49,9 +49,9 @@ export function useCsrfFetch<
arg2?: string
) {
const [opts = {}, autoKey] = typeof arg1 === 'string' ? [{}, arg1] : [arg1, arg2]
const { csrf } = useCsrf()
const { csrf, headerName } = useCsrf()
opts.headers = (opts.headers || {}) as Record<string, string>
opts.headers['csrf-token'] = csrf // add csrf token to req headers
opts.headers[headerName] = csrf // add csrf token to req headers
return useFetch<ResT, ErrorT, ReqT, Method, _ResT, DataT, PickKeys, DefaultT>(
request,
opts,
Expand Down Expand Up @@ -111,9 +111,10 @@ export function useLazyCsrfFetch<
}

export function useCsrf() {
const headerName = useRuntimeConfig().public.csurf.headerName ?? ''
if (import.meta.server) {
return { csrf: useNuxtApp().ssrContext?.event?.context?.csrfToken }
return { csrf: useNuxtApp().ssrContext?.event?.context?.csrfToken, headerName }
}
const metaTag = window.document.querySelector('meta[name="csrf-token"]')
return { csrf: metaTag?.getAttribute('content') }
return { csrf: metaTag?.getAttribute('content'), headerName }
}
4 changes: 2 additions & 2 deletions src/runtime/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ type MappedType<R extends ResponseType, JsonType = any> = R extends keyof Respon
type $CsrfFetch = <T = any, R extends ResponseType = "json">(request: FetchRequest, options?: FetchOptions<R>, fetch?: $Fetch) => Promise<MappedType<R, T>>

export default defineNuxtPlugin(() => {
const { csrf } = useCsrf()
const { csrf, headerName } = useCsrf()
const csrfFetch: $CsrfFetch = (request, options, fetch = $fetch) => {
if (!options) { options = {} }
options.headers = (options.headers || {}) as Record<string, string>
options.headers['csrf-token'] = csrf
options.headers[headerName] = csrf
return fetch(request, options)
}
return {
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/server/middleware/csrf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default defineEventHandler(async (event) => {
if (!methodsToProtect.includes(method)) { return }

const secret = getCookie(event, csrfConfig.cookieKey!) ?? ''
const token = getHeader(event, 'csrf-token') ?? ''
const token = getHeader(event, baseConfig.headerName!) ?? ''
// verify the incoming csrf token
const isValidToken = await csrf.verify(secret, token, await useSecretKey(csrfConfig), csrfConfig.encryptAlgorithm)
if (!isValidToken) {
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ export interface ModuleOptions {
encryptAlgorithm?: EncryptAlgorithm
addCsrfTokenToEventCtx?: boolean // to run useCsrfFetch on server
enabled?: boolean // disabled module server middleware/plugin when `enabled` is set to `false` (you will still have access to `useCsrf`/`useCsrfFetch` client composables)
headerName?: string
}

0 comments on commit 2e093cc

Please sign in to comment.