Skip to content

Commit e643253

Browse files
authored
feat: dedicated uri params generation for preset (#118)
* feat: dedicated uri params generation for preset * chore: update more detail through comment
1 parent df6d2b4 commit e643253

File tree

2 files changed

+137
-2
lines changed

2 files changed

+137
-2
lines changed

index.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const defaultState = require('crypto').randomBytes(10).toString('hex')
44

55
const fp = require('fastify-plugin')
66
const oauth2Module = require('simple-oauth2')
7+
const kGenerateCallbackUriParams = Symbol.for('fastify-oauth2.generate-callback-uri-params')
78

89
const promisify = require('util').promisify
910
const callbackify = require('util').callbackify
@@ -20,6 +21,10 @@ function defaultCheckStateFunction (state, callback) {
2021
callback(new Error('Invalid state'))
2122
}
2223

24+
function defaultGenerateCallbackUriParams (callbackUriParams) {
25+
return callbackUriParams
26+
}
27+
2328
const oauthPlugin = fp(function (fastify, options, next) {
2429
if (typeof options.name !== 'string') {
2530
return next(new Error('options.name should be a string'))
@@ -59,13 +64,14 @@ const oauthPlugin = fp(function (fastify, options, next) {
5964
const scope = options.scope
6065
const generateStateFunction = options.generateStateFunction || defaultGenerateStateFunction
6166
const checkStateFunction = options.checkStateFunction || defaultCheckStateFunction
67+
const generateCallbackUriParams = (credentials.auth && credentials.auth[kGenerateCallbackUriParams]) || defaultGenerateCallbackUriParams
6268
const startRedirectPath = options.startRedirectPath
6369
const tags = options.tags || []
6470
const schema = options.schema || { tags: tags }
6571

6672
function generateAuthorizationUri (requestObject) {
6773
const state = generateStateFunction(requestObject)
68-
const urlOptions = Object.assign({}, callbackUriParams, {
74+
const urlOptions = Object.assign({}, generateCallbackUriParams(callbackUriParams, requestObject, scope, state), {
6975
redirect_uri: callbackUri,
7076
scope: scope,
7177
state: state
@@ -149,7 +155,24 @@ oauthPlugin.APPLE_CONFIGURATION = {
149155
authorizeHost: 'https://appleid.apple.com',
150156
authorizePath: '/auth/authorize',
151157
tokenHost: 'https://appleid.apple.com',
152-
tokenPath: '/auth/token'
158+
tokenPath: '/auth/token',
159+
// kGenerateCallbackUriParams is used for dedicated behavior for each OAuth2.0 provider
160+
// It can update the callbackUriParams based on requestObject, scope and state
161+
//
162+
// Symbol used in here because we would not like the user to modify this behavior and
163+
// do not want to mess up with property name collision
164+
[kGenerateCallbackUriParams]: function (callbackUriParams, requestObject, scope, state) {
165+
const stringifyScope = Array.isArray(scope) ? scope.join(' ') : scope
166+
// This behavior is not documented on Apple Developer Docs but it display through runtime error.
167+
// Related Docs: https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms
168+
// Related Issue: https://github.com/fastify/fastify-oauth2/issues/116
169+
//
170+
// `response_mode` must be `form_post` when scope include `email` or `name`
171+
if (stringifyScope.includes('email') || stringifyScope.includes('name')) {
172+
callbackUriParams.response_mode = 'form_post'
173+
}
174+
return callbackUriParams
175+
}
153176
}
154177

155178
oauthPlugin.FACEBOOK_CONFIGURATION = {

test.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,3 +565,115 @@ t.test('already decorated', t => {
565565
t.strictSame(err.message, 'The decorator \'githubOAuth2\' has already been added!')
566566
})
567567
})
568+
569+
t.test('preset configuration generate-callback-uri-params', t => {
570+
t.plan(3)
571+
572+
t.test('array scope', t => {
573+
const fastify = createFastify({ logger: { level: 'silent' } })
574+
575+
fastify.register(oauthPlugin, {
576+
name: 'the-name',
577+
credentials: {
578+
client: {
579+
id: 'my-client-id',
580+
secret: 'my-secret'
581+
},
582+
auth: oauthPlugin.APPLE_CONFIGURATION
583+
},
584+
startRedirectPath: '/login/apple',
585+
callbackUri: '/callback',
586+
scope: ['email']
587+
})
588+
589+
t.tearDown(fastify.close.bind(fastify))
590+
591+
fastify.listen(0, function (err) {
592+
t.error(err)
593+
594+
fastify.inject({
595+
method: 'GET',
596+
url: '/login/apple'
597+
}, function (err, responseStart) {
598+
t.error(err)
599+
600+
t.equal(responseStart.statusCode, 302)
601+
const matched = responseStart.headers.location.match(/https:\/\/appleid\.apple\.com\/auth\/authorize\?response_type=code&client_id=my-client-id&response_mode=form_post&redirect_uri=%2Fcallback&scope=email&state=(.*)/)
602+
t.ok(matched)
603+
t.end()
604+
})
605+
})
606+
})
607+
608+
t.test('string scope', t => {
609+
const fastify = createFastify({ logger: { level: 'silent' } })
610+
611+
fastify.register(oauthPlugin, {
612+
name: 'the-name',
613+
credentials: {
614+
client: {
615+
id: 'my-client-id',
616+
secret: 'my-secret'
617+
},
618+
auth: oauthPlugin.APPLE_CONFIGURATION
619+
},
620+
startRedirectPath: '/login/apple',
621+
callbackUri: '/callback',
622+
scope: 'name'
623+
})
624+
625+
t.tearDown(fastify.close.bind(fastify))
626+
627+
fastify.listen(0, function (err) {
628+
t.error(err)
629+
630+
fastify.inject({
631+
method: 'GET',
632+
url: '/login/apple'
633+
}, function (err, responseStart) {
634+
t.error(err)
635+
636+
t.equal(responseStart.statusCode, 302)
637+
const matched = responseStart.headers.location.match(/https:\/\/appleid\.apple\.com\/auth\/authorize\?response_type=code&client_id=my-client-id&response_mode=form_post&redirect_uri=%2Fcallback&scope=name&state=(.*)/)
638+
t.ok(matched)
639+
t.end()
640+
})
641+
})
642+
})
643+
644+
t.test('no scope', t => {
645+
const fastify = createFastify({ logger: { level: 'silent' } })
646+
647+
fastify.register(oauthPlugin, {
648+
name: 'the-name',
649+
credentials: {
650+
client: {
651+
id: 'my-client-id',
652+
secret: 'my-secret'
653+
},
654+
auth: oauthPlugin.APPLE_CONFIGURATION
655+
},
656+
startRedirectPath: '/login/apple',
657+
callbackUri: '/callback',
658+
scope: ''
659+
})
660+
661+
t.tearDown(fastify.close.bind(fastify))
662+
663+
fastify.listen(0, function (err) {
664+
t.error(err)
665+
666+
fastify.inject({
667+
method: 'GET',
668+
url: '/login/apple'
669+
}, function (err, responseStart) {
670+
t.error(err)
671+
672+
t.equal(responseStart.statusCode, 302)
673+
const matched = responseStart.headers.location.match(/https:\/\/appleid\.apple\.com\/auth\/authorize\?response_type=code&client_id=my-client-id&redirect_uri=%2Fcallback&scope=&state=(.*)/)
674+
t.ok(matched)
675+
t.end()
676+
})
677+
})
678+
})
679+
})

0 commit comments

Comments
 (0)