Skip to content

Commit 3b1ab83

Browse files
vtkacVladimir Tkac
andauthored
feat: options to customize cookie names (#272)
* Options to customize cookie names * Apply PR code suggestions --------- Co-authored-by: Vladimir Tkac <[email protected]>
1 parent 1094c28 commit 3b1ab83

File tree

5 files changed

+186
-8
lines changed

5 files changed

+186
-8
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,17 @@ fastify.register(oauthPlugin, {
9595
})
9696
```
9797

98+
Additionally, you can customize the names of the cookies by setting the `redirectStateCookieName` and `verifierCookieName` options.
99+
The default values for these cookies are `oauth2-code-verifier` for `verifierCookieName` and `oauth2-redirect-state` for `redirectStateCookieName`.
100+
101+
```js
102+
fastify.register(oauthPlugin, {
103+
...,
104+
redirectStateCookieName: 'custom-redirect-state',
105+
verifierCookieName: 'custom-code-verifier'
106+
})
107+
```
108+
98109
### Preset configurations
99110

100111
You can choose some default setup to assign to `auth` option.

index.js

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ const kGenerateCallbackUriParams = Symbol.for('fastify-oauth2.generate-callback-
1111

1212
const { promisify, callbackify } = require('node:util')
1313

14+
const DEFAULT_VERIFIER_COOKIE_NAME = 'oauth2-code-verifier'
15+
const DEFAULT_REDIRECT_STATE_COOKIE_NAME = 'oauth2-redirect-state'
1416
const USER_AGENT = 'fastify-oauth2'
15-
const VERIFIER_COOKIE_NAME = 'oauth2-code-verifier'
1617
const PKCE_METHODS = ['S256', 'plain']
1718

1819
const random = (bytes = 32) => randomBytes(bytes).toString('base64url')
@@ -25,7 +26,10 @@ function defaultGenerateStateFunction (request, callback) {
2526

2627
function defaultCheckStateFunction (request, callback) {
2728
const state = request.query.state
28-
const stateCookie = request.cookies['oauth2-redirect-state']
29+
const stateCookie =
30+
request.cookies[
31+
this.redirectStateCookieName
32+
]
2933
if (stateCookie && state === stateCookie) {
3034
callback()
3135
return
@@ -98,6 +102,20 @@ function fastifyOauth2 (fastify, options, next) {
98102
if (!options.discovery && !options.credentials.auth) {
99103
return next(new Error('options.discovery.issuer or credentials.auth have to be given'))
100104
}
105+
if (
106+
options.verifierCookieName &&
107+
typeof options.verifierCookieName !== 'string'
108+
) {
109+
return next(new Error('options.verifierCookieName should be a string'))
110+
}
111+
if (
112+
options.redirectStateCookieName &&
113+
typeof options.redirectStateCookieName !== 'string'
114+
) {
115+
return next(
116+
new Error('options.redirectStateCookieName should be a string')
117+
)
118+
}
101119
if (!fastify.hasReplyDecorator('cookie')) {
102120
fastify.register(require('@fastify/cookie'))
103121
}
@@ -116,10 +134,16 @@ function fastifyOauth2 (fastify, options, next) {
116134
tokenRequestParams = {},
117135
scope,
118136
generateStateFunction = defaultGenerateStateFunction,
119-
checkStateFunction = defaultCheckStateFunction,
137+
checkStateFunction = defaultCheckStateFunction.bind({
138+
redirectStateCookieName:
139+
configured.redirectStateCookieName ||
140+
DEFAULT_REDIRECT_STATE_COOKIE_NAME
141+
}),
120142
startRedirectPath,
121143
tags = [],
122-
schema = { tags }
144+
schema = { tags },
145+
redirectStateCookieName = DEFAULT_REDIRECT_STATE_COOKIE_NAME,
146+
verifierCookieName = DEFAULT_VERIFIER_COOKIE_NAME
123147
} = configured
124148

125149
if (userAgent) {
@@ -153,7 +177,7 @@ function fastifyOauth2 (fastify, options, next) {
153177
return
154178
}
155179

156-
reply.setCookie('oauth2-redirect-state', state, cookieOpts)
180+
reply.setCookie(redirectStateCookieName, state, cookieOpts)
157181

158182
// when PKCE extension is used
159183
let pkceParams = {}
@@ -164,7 +188,7 @@ function fastifyOauth2 (fastify, options, next) {
164188
code_challenge: challenge,
165189
code_challenge_method: configured.pkce
166190
}
167-
reply.setCookie(VERIFIER_COOKIE_NAME, verifier, cookieOpts)
191+
reply.setCookie(verifierCookieName, verifier, cookieOpts)
168192
}
169193

170194
const urlOptions = Object.assign({}, generateCallbackUriParams(callbackUriParams, request, scope, state), {
@@ -227,7 +251,7 @@ function fastifyOauth2 (fastify, options, next) {
227251

228252
function getAccessTokenFromAuthorizationCodeFlowCallbacked (request, reply, callback) {
229253
const code = request.query.code
230-
const pkceParams = configured.pkce ? { code_verifier: request.cookies['oauth2-code-verifier'] } : {}
254+
const pkceParams = configured.pkce ? { code_verifier: request.cookies[verifierCookieName] } : {}
231255

232256
const _callback = typeof reply === 'function' ? reply : callback
233257

@@ -299,7 +323,7 @@ function fastifyOauth2 (fastify, options, next) {
299323
}
300324

301325
function clearCodeVerifierCookie (reply) {
302-
reply.clearCookie(VERIFIER_COOKIE_NAME, cookieOpts)
326+
reply.clearCookie(verifierCookieName, cookieOpts)
303327
}
304328

305329
const pUserInfo = promisify(userInfoCallbacked)

test/index.test.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2920,3 +2920,142 @@ t.test('options.checkStateFunction', t => {
29202920

29212921
t.end()
29222922
})
2923+
2924+
t.test('options.redirectStateCookieName', (t) => {
2925+
t.plan(2)
2926+
2927+
t.test('should be a string', (t) => {
2928+
t.plan(1)
2929+
2930+
const fastify = createFastify({ logger: { level: 'silent' } })
2931+
2932+
fastify
2933+
.register(
2934+
fastifyOauth2, {
2935+
name: 'the-name',
2936+
credentials: {
2937+
client: {
2938+
id: 'my-client-id',
2939+
secret: 'my-secret'
2940+
},
2941+
auth: fastifyOauth2.GITHUB_CONFIGURATION
2942+
},
2943+
callbackUri: '/callback',
2944+
redirectStateCookieName: 42
2945+
}
2946+
)
2947+
.ready((err) => {
2948+
t.strictSame(
2949+
err.message,
2950+
'options.redirectStateCookieName should be a string'
2951+
)
2952+
})
2953+
})
2954+
2955+
t.test('with custom cookie name', (t) => {
2956+
t.plan(4)
2957+
2958+
const fastify = createFastify({ logger: { level: 'silent' } })
2959+
2960+
fastify.register(fastifyOauth2, {
2961+
name: 'the-name',
2962+
credentials: {
2963+
client: {
2964+
id: 'my-client-id',
2965+
secret: 'my-secret'
2966+
},
2967+
auth: fastifyOauth2.GITHUB_CONFIGURATION
2968+
},
2969+
callbackUri: '/callback',
2970+
startRedirectPath: '/login',
2971+
redirectStateCookieName: 'custom-redirect-state'
2972+
})
2973+
2974+
t.teardown(fastify.close.bind(fastify))
2975+
2976+
fastify.inject(
2977+
{
2978+
method: 'GET',
2979+
url: '/login'
2980+
},
2981+
function (err, responseEnd) {
2982+
t.error(err)
2983+
2984+
t.equal(responseEnd.statusCode, 302)
2985+
t.matchStrict(responseEnd.cookies[0].name, 'custom-redirect-state')
2986+
t.matchStrict(responseEnd.cookies[0].value, String)
2987+
2988+
t.end()
2989+
}
2990+
)
2991+
})
2992+
})
2993+
2994+
t.test('options.verifierCookieName', (t) => {
2995+
t.plan(2)
2996+
2997+
t.test('should be a string', (t) => {
2998+
t.plan(1)
2999+
3000+
const fastify = createFastify({ logger: { level: 'silent' } })
3001+
3002+
fastify
3003+
.register(fastifyOauth2, {
3004+
name: 'the-name',
3005+
credentials: {
3006+
client: {
3007+
id: 'my-client-id',
3008+
secret: 'my-secret'
3009+
},
3010+
auth: fastifyOauth2.GITHUB_CONFIGURATION
3011+
},
3012+
callbackUri: '/callback',
3013+
verifierCookieName: 42
3014+
})
3015+
.ready((err) => {
3016+
t.strictSame(
3017+
err.message,
3018+
'options.verifierCookieName should be a string'
3019+
)
3020+
})
3021+
})
3022+
3023+
t.test('with custom cookie name', (t) => {
3024+
t.plan(4)
3025+
3026+
const fastify = createFastify({ logger: { level: 'silent' } })
3027+
3028+
fastify.register(fastifyOauth2, {
3029+
name: 'the-name',
3030+
credentials: {
3031+
client: {
3032+
id: 'my-client-id',
3033+
secret: 'my-secret'
3034+
},
3035+
auth: fastifyOauth2.GITHUB_CONFIGURATION
3036+
},
3037+
callbackUri: '/callback',
3038+
startRedirectPath: '/login',
3039+
verifierCookieName: 'custom-verifier',
3040+
pkce: 'plain'
3041+
})
3042+
3043+
t.teardown(fastify.close.bind(fastify))
3044+
3045+
fastify.inject(
3046+
{
3047+
method: 'GET',
3048+
url: '/login'
3049+
},
3050+
function (err, responseEnd) {
3051+
t.error(err)
3052+
3053+
t.equal(responseEnd.statusCode, 302)
3054+
t.matchStrict(responseEnd.cookies[1].name, 'custom-verifier')
3055+
t.matchStrict(responseEnd.cookies[1].value, String)
3056+
3057+
t.end()
3058+
}
3059+
)
3060+
})
3061+
})

types/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ declare namespace fastifyOauth2 {
4646
userAgent?: string | false;
4747
pkce?: 'S256' | 'plain';
4848
discovery?: { issuer: string; }
49+
redirectStateCookieName?: string;
50+
verifierCookieName?: string;
4951
}
5052

5153
export type TToken = 'access_token' | 'refresh_token'

types/index.test-d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ const OAuth2Options: FastifyOAuth2Options = {
5858
secure: true,
5959
sameSite: 'none'
6060
},
61+
redirectStateCookieName: 'redirect-state-cookie',
62+
verifierCookieName: 'verifier-cookie',
6163
};
6264

6365
expectAssignable<FastifyOAuth2Options>({

0 commit comments

Comments
 (0)