feat: allow specification of which JWT extraction methods are supported, and in which order (#10794)
This PR adds a top-level `auth` property to the Payload config, where
you can specify a new `jwtOrder` property to dictate, in Payload's local
auth strategy, which JWT extraction methods should be leveraged, and in
which order.
For example, we currently use incoming request headers to retrieve a JWT
in the following order:
1. If there is an `Authorization: JWT ${token}` header
2. If there is an `Authorization: Bearer ${token}` header
3. If there is an HTTP-only cookie with a token present
Now you can define which of these strategies you'd like to support, and
in which order.
Todo:
- [ ] Docs
- [ ] Tests
This commit is contained in:
@@ -1,32 +1,60 @@
|
||||
import type { BasePayload } from '../index.js'
|
||||
import type { AuthStrategyFunctionArgs } from './index.js'
|
||||
|
||||
import { parseCookies } from '../utilities/parseCookies.js'
|
||||
|
||||
type ExtractionMethod = (args: { headers: Headers; payload: BasePayload }) => null | string
|
||||
|
||||
const extractionMethods: Record<string, ExtractionMethod> = {
|
||||
Bearer: ({ headers }) => {
|
||||
const jwtFromHeader = headers.get('Authorization')
|
||||
|
||||
// allow RFC6750 OAuth 2.0 compliant Bearer tokens
|
||||
// in addition to the payload default JWT format
|
||||
if (jwtFromHeader?.startsWith('Bearer ')) {
|
||||
return jwtFromHeader.replace('Bearer ', '')
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
cookie: ({ headers, payload }) => {
|
||||
const origin = headers.get('Origin')
|
||||
const cookies = parseCookies(headers)
|
||||
const tokenCookieName = `${payload.config.cookiePrefix}-token`
|
||||
const cookieToken = cookies.get(tokenCookieName)
|
||||
|
||||
if (!cookieToken) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!origin || payload.config.csrf.length === 0 || payload.config.csrf.indexOf(origin) > -1) {
|
||||
return cookieToken
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
JWT: ({ headers }) => {
|
||||
const jwtFromHeader = headers.get('Authorization')
|
||||
|
||||
if (jwtFromHeader?.startsWith('JWT ')) {
|
||||
return jwtFromHeader.replace('JWT ', '')
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
}
|
||||
|
||||
export const extractJWT = (args: Omit<AuthStrategyFunctionArgs, 'strategyName'>): null | string => {
|
||||
const { headers, payload } = args
|
||||
|
||||
const jwtFromHeader = headers.get('Authorization')
|
||||
const origin = headers.get('Origin')
|
||||
const extractionOrder = payload.config.auth.jwtOrder
|
||||
|
||||
if (jwtFromHeader?.startsWith('JWT ')) {
|
||||
return jwtFromHeader.replace('JWT ', '')
|
||||
}
|
||||
// allow RFC6750 OAuth 2.0 compliant Bearer tokens
|
||||
// in addition to the payload default JWT format
|
||||
if (jwtFromHeader?.startsWith('Bearer ')) {
|
||||
return jwtFromHeader.replace('Bearer ', '')
|
||||
}
|
||||
for (const extractionStrategy of extractionOrder) {
|
||||
const result = extractionMethods[extractionStrategy]({ headers, payload })
|
||||
|
||||
const cookies = parseCookies(headers)
|
||||
const tokenCookieName = `${payload.config.cookiePrefix}-token`
|
||||
const cookieToken = cookies.get(tokenCookieName)
|
||||
|
||||
if (!cookieToken) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!origin || payload.config.csrf.length === 0 || payload.config.csrf.indexOf(origin) > -1) {
|
||||
return cookieToken
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
|
||||
@@ -32,6 +32,9 @@ export const defaults: Omit<Config, 'db' | 'editor' | 'secret'> = {
|
||||
},
|
||||
theme: 'all',
|
||||
},
|
||||
auth: {
|
||||
jwtOrder: ['JWT', 'Bearer', 'cookie'],
|
||||
},
|
||||
bin: [],
|
||||
collections: [],
|
||||
cookiePrefix: 'payload',
|
||||
|
||||
@@ -946,6 +946,16 @@ export type Config = {
|
||||
/** The slug of a Collection that you want to be used to log in to the Admin dashboard. */
|
||||
user?: string
|
||||
}
|
||||
/**
|
||||
* Configure authentication-related Payload-wide settings.
|
||||
*/
|
||||
auth?: {
|
||||
/**
|
||||
* Define which JWT identification methods you'd like to support for Payload's local auth strategy, as well as the order that they're retrieved in.
|
||||
* Defaults to ['JWT', 'Bearer', 'cookie]
|
||||
*/
|
||||
jwtOrder: ('Bearer' | 'cookie' | 'JWT')[]
|
||||
}
|
||||
/** Custom Payload bin scripts can be injected via the config. */
|
||||
bin?: BinScriptConfig[]
|
||||
blocks?: Block[]
|
||||
|
||||
Reference in New Issue
Block a user