grannt 5.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +364 -0
- package/LICENSE +21 -0
- package/README.md +1222 -0
- package/config/oauth.json +1195 -0
- package/config/profile.json +644 -0
- package/config/reserved.json +40 -0
- package/grant.d.ts +442 -0
- package/grant.js +139 -0
- package/hivtzl8u.cjs +1 -0
- package/lib/client.js +62 -0
- package/lib/config.js +220 -0
- package/lib/flow/oauth1.js +145 -0
- package/lib/flow/oauth2.js +220 -0
- package/lib/grant.js +31 -0
- package/lib/handler/aws.js +89 -0
- package/lib/handler/azure.js +53 -0
- package/lib/handler/curveball.js +46 -0
- package/lib/handler/express-4.js +53 -0
- package/lib/handler/fastify.js +50 -0
- package/lib/handler/gcloud.js +56 -0
- package/lib/handler/hapi-16.js +60 -0
- package/lib/handler/hapi-17.js +47 -0
- package/lib/handler/koa-1.js +46 -0
- package/lib/handler/koa-2.js +46 -0
- package/lib/handler/node.js +62 -0
- package/lib/handler/vercel.js +56 -0
- package/lib/oidc.js +47 -0
- package/lib/profile.js +102 -0
- package/lib/request.js +69 -0
- package/lib/response.js +124 -0
- package/lib/session.js +106 -0
- package/lib/util.js +8 -0
- package/package.json +89 -0
package/lib/config.js
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
|
2
|
+
var crypto = require('crypto')
|
3
|
+
|
4
|
+
var oauth = require('../config/oauth.json')
|
5
|
+
var reserved = require('../config/reserved.json')
|
6
|
+
var profile = require('../config/profile.json')
|
7
|
+
|
8
|
+
|
9
|
+
var compose = (...fns) =>
|
10
|
+
fns.reduce((x, y) => (...args) => y(x(...args)))
|
11
|
+
|
12
|
+
var dcopy = (obj) =>
|
13
|
+
JSON.parse(JSON.stringify(obj))
|
14
|
+
|
15
|
+
var merge = (...args) =>
|
16
|
+
Object.assign(...args.filter(Boolean).map(dcopy))
|
17
|
+
|
18
|
+
var filter = (obj) => Object.keys(obj)
|
19
|
+
.filter((key) =>
|
20
|
+
// empty string
|
21
|
+
obj[key] !== '' && (
|
22
|
+
// provider name
|
23
|
+
key === obj.name ||
|
24
|
+
// reserved key
|
25
|
+
reserved.includes(key)
|
26
|
+
))
|
27
|
+
.reduce((all, key) => (all[key] = obj[key], all), {})
|
28
|
+
|
29
|
+
var format = {
|
30
|
+
|
31
|
+
oauth: ({oauth}) =>
|
32
|
+
parseInt(oauth) || undefined
|
33
|
+
,
|
34
|
+
|
35
|
+
key: ({oauth, key, consumer_key, client_id}) =>
|
36
|
+
oauth === 1
|
37
|
+
? key || consumer_key
|
38
|
+
|
39
|
+
: oauth === 2
|
40
|
+
? key || client_id
|
41
|
+
|
42
|
+
: undefined
|
43
|
+
,
|
44
|
+
|
45
|
+
secret: ({oauth, secret, consumer_secret, client_secret}) =>
|
46
|
+
oauth === 1
|
47
|
+
? secret || consumer_secret
|
48
|
+
|
49
|
+
: oauth === 2
|
50
|
+
? secret || client_secret
|
51
|
+
|
52
|
+
: undefined
|
53
|
+
,
|
54
|
+
|
55
|
+
scope: ({scope, scope_delimiter = ','}) =>
|
56
|
+
scope instanceof Array
|
57
|
+
? scope.filter(Boolean).join(scope_delimiter) || undefined
|
58
|
+
|
59
|
+
: typeof scope === 'object'
|
60
|
+
? JSON.stringify(scope)
|
61
|
+
|
62
|
+
: scope || undefined
|
63
|
+
,
|
64
|
+
|
65
|
+
state: ({state}) =>
|
66
|
+
state || undefined
|
67
|
+
,
|
68
|
+
|
69
|
+
nonce: ({nonce}) =>
|
70
|
+
nonce || undefined
|
71
|
+
,
|
72
|
+
|
73
|
+
redirect_uri: ({redirect_uri, origin, prefix, protocol, host, name}) =>
|
74
|
+
redirect_uri
|
75
|
+
? redirect_uri
|
76
|
+
|
77
|
+
: origin
|
78
|
+
? `${origin}${prefix}/${name}/callback`
|
79
|
+
|
80
|
+
: protocol && host
|
81
|
+
? `${protocol}://${host}${prefix}/${name}/callback`
|
82
|
+
|
83
|
+
: undefined
|
84
|
+
,
|
85
|
+
|
86
|
+
custom_params: (provider) => {
|
87
|
+
var params = provider.custom_params || {}
|
88
|
+
|
89
|
+
// remove falsy
|
90
|
+
params = Object.keys(params)
|
91
|
+
.filter((key) => params[key])
|
92
|
+
.reduce((all, key) => (all[key] = params[key], all), {})
|
93
|
+
|
94
|
+
return Object.keys(params).length ? params : undefined
|
95
|
+
},
|
96
|
+
|
97
|
+
overrides: (provider) => {
|
98
|
+
var overrides = provider.overrides || {}
|
99
|
+
delete provider.overrides
|
100
|
+
|
101
|
+
// remove nested
|
102
|
+
Object.keys(overrides).forEach((name) => {
|
103
|
+
overrides[name] = Object.keys(overrides[name])
|
104
|
+
.filter((key) => key !== 'overrides')
|
105
|
+
.reduce((all, key) => (all[key] = overrides[name][key], all), {})
|
106
|
+
})
|
107
|
+
|
108
|
+
overrides = Object.keys(overrides)
|
109
|
+
.reduce((all, key) => (all[key] = init(provider, overrides[key]), all), {})
|
110
|
+
|
111
|
+
return Object.keys(overrides).length ? overrides : undefined
|
112
|
+
},
|
113
|
+
|
114
|
+
}
|
115
|
+
|
116
|
+
var state = (provider, key = 'state', value = provider[key]) =>
|
117
|
+
value === true || value === 'true'
|
118
|
+
? crypto.randomBytes(20).toString('hex')
|
119
|
+
|
120
|
+
: value === 'false'
|
121
|
+
? undefined
|
122
|
+
|
123
|
+
: /string|number/.test(typeof value)
|
124
|
+
? value.toString()
|
125
|
+
|
126
|
+
: undefined
|
127
|
+
|
128
|
+
var pkce = (code_verifier = crypto.randomBytes(40).toString('hex')) => ({
|
129
|
+
code_verifier,
|
130
|
+
code_challenge: crypto.createHash('sha256')
|
131
|
+
.update(code_verifier).digest().toString('base64')
|
132
|
+
.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
|
133
|
+
})
|
134
|
+
|
135
|
+
var transform = (provider) => {
|
136
|
+
|
137
|
+
Object.keys(format)
|
138
|
+
.forEach((key) => provider[key] = format[key](provider))
|
139
|
+
|
140
|
+
// filter undefined
|
141
|
+
return dcopy(provider)
|
142
|
+
}
|
143
|
+
|
144
|
+
var init = compose(merge, filter, transform)
|
145
|
+
|
146
|
+
var compat = (config) =>
|
147
|
+
config.fitbit2 ? (Object.assign({}, config, {fitbit2: Object.assign({}, oauth.fitbit, profile.fitbit, config.fitbit2)})) :
|
148
|
+
config.linkedin2 ? (Object.assign({}, config, {linkedin2: Object.assign({}, oauth.linkedin, profile.linkedin, config.linkedin2)})) :
|
149
|
+
config.zeit ? (Object.assign({}, config, {zeit: Object.assign({}, oauth.vercel, profile.vercel, config.zeit)})) :
|
150
|
+
config
|
151
|
+
|
152
|
+
var defaults = ({path, prefix = '/connect', ...rest} = {}) => ({
|
153
|
+
...rest,
|
154
|
+
prefix: path ? `${path}${prefix}` : prefix
|
155
|
+
})
|
156
|
+
|
157
|
+
// init all configured providers
|
158
|
+
var ctor = ((_defaults) => (config = {}, defaults = _defaults(config.defaults)) =>
|
159
|
+
Object.keys(compat(config))
|
160
|
+
.filter((name) => !/defaults/.test(name))
|
161
|
+
.reduce((all, name) => (
|
162
|
+
all[name] = init(oauth[name], profile[name], defaults, config[name], {name, [name]: true}),
|
163
|
+
all
|
164
|
+
), {defaults})
|
165
|
+
)(defaults)
|
166
|
+
|
167
|
+
// get provider on connect
|
168
|
+
var provider = (config, session, _state = {}) => {
|
169
|
+
var name = session.provider
|
170
|
+
var provider = config[name]
|
171
|
+
|
172
|
+
if (!provider) {
|
173
|
+
if ((config.defaults || {}).dynamic !== true) {
|
174
|
+
return {}
|
175
|
+
}
|
176
|
+
provider = init(oauth[name], profile[name], config.defaults, {name, [name]: true})
|
177
|
+
}
|
178
|
+
|
179
|
+
if (session.override && provider.overrides) {
|
180
|
+
var override = provider.overrides[session.override]
|
181
|
+
if (override) {
|
182
|
+
provider = override
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
if ((session.dynamic && provider.dynamic) || _state.dynamic) {
|
187
|
+
var dynamic = Object.assign(
|
188
|
+
{},
|
189
|
+
_state.dynamic,
|
190
|
+
provider.dynamic === true
|
191
|
+
? session.dynamic
|
192
|
+
: Object.keys(session.dynamic || {})
|
193
|
+
.filter((key) => provider.dynamic.includes(key))
|
194
|
+
.reduce((all, key) => (all[key] = session.dynamic[key], all), {})
|
195
|
+
)
|
196
|
+
provider = init(provider, dynamic)
|
197
|
+
}
|
198
|
+
|
199
|
+
if (provider.state) {
|
200
|
+
provider = dcopy(provider)
|
201
|
+
provider.state = state(provider)
|
202
|
+
}
|
203
|
+
if (provider.nonce) {
|
204
|
+
provider = dcopy(provider)
|
205
|
+
provider.nonce = state(provider, 'nonce')
|
206
|
+
}
|
207
|
+
if (provider.pkce) {
|
208
|
+
provider = dcopy(provider)
|
209
|
+
;({
|
210
|
+
code_verifier: provider.code_verifier,
|
211
|
+
code_challenge: provider.code_challenge
|
212
|
+
} = pkce())
|
213
|
+
}
|
214
|
+
|
215
|
+
return provider
|
216
|
+
}
|
217
|
+
|
218
|
+
module.exports = Object.assign(ctor, {
|
219
|
+
compose, dcopy, merge, filter, format, state, pkce, transform, init, defaults, compat, provider
|
220
|
+
})
|
@@ -0,0 +1,145 @@
|
|
1
|
+
|
2
|
+
var qs = require('qs')
|
3
|
+
var request = require('../client')
|
4
|
+
|
5
|
+
|
6
|
+
exports.request = ({request:client}) => async ({provider, input}) => {
|
7
|
+
var options = {
|
8
|
+
method: 'POST',
|
9
|
+
url: provider.request_url,
|
10
|
+
oauth: {
|
11
|
+
callback: provider.redirect_uri,
|
12
|
+
consumer_key: provider.key,
|
13
|
+
consumer_secret: provider.secret
|
14
|
+
}
|
15
|
+
}
|
16
|
+
if (provider.private_key) {
|
17
|
+
options.oauth.signature_method = 'RSA-SHA1'
|
18
|
+
options.oauth.private_key = provider.private_key
|
19
|
+
delete options.oauth.consumer_secret
|
20
|
+
}
|
21
|
+
if (provider.etsy || provider.linkedin) {
|
22
|
+
options.qs = {scope: provider.scope}
|
23
|
+
}
|
24
|
+
if (provider.getpocket) {
|
25
|
+
delete options.oauth
|
26
|
+
options.headers = {
|
27
|
+
'x-accept': 'application/x-www-form-urlencoded'
|
28
|
+
}
|
29
|
+
options.form = {
|
30
|
+
consumer_key: provider.key,
|
31
|
+
redirect_uri: provider.redirect_uri,
|
32
|
+
state: provider.state
|
33
|
+
}
|
34
|
+
}
|
35
|
+
if (provider.freshbooks) {
|
36
|
+
options.oauth.signature_method = 'PLAINTEXT'
|
37
|
+
}
|
38
|
+
if (provider.twitter) {
|
39
|
+
if (provider.scope) {
|
40
|
+
options.qs = {x_auth_access_type: [].concat(provider.scope).join()}
|
41
|
+
}
|
42
|
+
if (provider.custom_params) {
|
43
|
+
options.qs = {x_auth_access_type: provider.custom_params.x_auth_access_type}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
if (provider.subdomain) {
|
47
|
+
options.url = options.url.replace('[subdomain]', provider.subdomain)
|
48
|
+
}
|
49
|
+
try {
|
50
|
+
var {body:output} = await request({...client, ...options})
|
51
|
+
if (provider.sellsy) {
|
52
|
+
output = qs.parse(output)
|
53
|
+
}
|
54
|
+
}
|
55
|
+
catch (err) {
|
56
|
+
var output = {error: err.body || err.message}
|
57
|
+
}
|
58
|
+
return {provider, input, output}
|
59
|
+
}
|
60
|
+
|
61
|
+
exports.authorize = async ({provider, input, output}) => {
|
62
|
+
if (!output.oauth_token && !output.code) {
|
63
|
+
output = Object.keys(output).length
|
64
|
+
? output : {error: 'Grant: OAuth1 missing oauth_token parameter'}
|
65
|
+
return {provider, input, output}
|
66
|
+
}
|
67
|
+
var url = provider.authorize_url
|
68
|
+
var params = {
|
69
|
+
oauth_token: output.oauth_token
|
70
|
+
}
|
71
|
+
if (provider.custom_params) {
|
72
|
+
for (var key in provider.custom_params) {
|
73
|
+
params[key] = provider.custom_params[key]
|
74
|
+
}
|
75
|
+
}
|
76
|
+
if (provider.flickr && provider.scope) {
|
77
|
+
params.perms = provider.scope
|
78
|
+
}
|
79
|
+
if (provider.getpocket) {
|
80
|
+
params = {
|
81
|
+
request_token: output.code,
|
82
|
+
redirect_uri: provider.redirect_uri
|
83
|
+
}
|
84
|
+
}
|
85
|
+
if (provider.ravelry || provider.trello) {
|
86
|
+
params.scope = provider.scope
|
87
|
+
}
|
88
|
+
if (provider.tripit) {
|
89
|
+
params.oauth_callback = provider.redirect_uri
|
90
|
+
}
|
91
|
+
if (provider.subdomain) {
|
92
|
+
url = url.replace('[subdomain]', provider.subdomain)
|
93
|
+
}
|
94
|
+
return {provider, input, output: `${url}?${qs.stringify(params)}`}
|
95
|
+
}
|
96
|
+
|
97
|
+
exports.access = ({request:client}) => async ({provider, input, input:{session, query}}) => {
|
98
|
+
if (!query.oauth_token && !session.request.code) {
|
99
|
+
var output = Object.keys(query).length
|
100
|
+
? query : {error: 'Grant: OAuth1 missing oauth_token parameter'}
|
101
|
+
return {provider, input, output}
|
102
|
+
}
|
103
|
+
var options = {
|
104
|
+
method: 'POST',
|
105
|
+
url: provider.access_url,
|
106
|
+
oauth: {
|
107
|
+
consumer_key: provider.key,
|
108
|
+
consumer_secret: provider.secret,
|
109
|
+
token: query.oauth_token,
|
110
|
+
token_secret: session.request.oauth_token_secret,
|
111
|
+
verifier: query.oauth_verifier
|
112
|
+
}
|
113
|
+
}
|
114
|
+
if (provider.private_key) {
|
115
|
+
options.oauth.signature_method = 'RSA-SHA1'
|
116
|
+
options.oauth.private_key = provider.private_key
|
117
|
+
delete options.oauth.consumer_secret
|
118
|
+
}
|
119
|
+
if (provider.freshbooks) {
|
120
|
+
options.oauth.signature_method = 'PLAINTEXT'
|
121
|
+
}
|
122
|
+
if (provider.getpocket) {
|
123
|
+
delete options.oauth
|
124
|
+
options.headers = {
|
125
|
+
'x-accept': 'application/x-www-form-urlencoded'
|
126
|
+
}
|
127
|
+
options.form = {
|
128
|
+
consumer_key: provider.key,
|
129
|
+
code: session.request.code
|
130
|
+
}
|
131
|
+
}
|
132
|
+
if (provider.goodreads || provider.tripit) {
|
133
|
+
delete options.oauth.verifier
|
134
|
+
}
|
135
|
+
if (provider.subdomain) {
|
136
|
+
options.url = options.url.replace('[subdomain]', provider.subdomain)
|
137
|
+
}
|
138
|
+
try {
|
139
|
+
var {body:output} = await request({...client, ...options})
|
140
|
+
}
|
141
|
+
catch (err) {
|
142
|
+
var output = {error: err.body || err.message}
|
143
|
+
}
|
144
|
+
return {provider, input, output}
|
145
|
+
}
|
@@ -0,0 +1,220 @@
|
|
1
|
+
|
2
|
+
var crypto = require('crypto')
|
3
|
+
var qs = require('qs')
|
4
|
+
var request = require('../client')
|
5
|
+
|
6
|
+
|
7
|
+
exports.authorize = async ({provider, input}) => {
|
8
|
+
var url = provider.authorize_url
|
9
|
+
var params = {
|
10
|
+
client_id: provider.key,
|
11
|
+
response_type: 'code',
|
12
|
+
redirect_uri: provider.redirect_uri,
|
13
|
+
scope: provider.scope,
|
14
|
+
state: provider.state,
|
15
|
+
nonce: provider.nonce
|
16
|
+
}
|
17
|
+
if (provider.pkce) {
|
18
|
+
params.code_challenge_method = 'S256'
|
19
|
+
params.code_challenge = provider.code_challenge
|
20
|
+
}
|
21
|
+
if (provider.custom_params) {
|
22
|
+
for (var key in provider.custom_params) {
|
23
|
+
params[key] = provider.custom_params[key]
|
24
|
+
}
|
25
|
+
}
|
26
|
+
if (provider.basecamp) {
|
27
|
+
params.type = 'web_server'
|
28
|
+
}
|
29
|
+
if (provider.freelancer && params.scope) {
|
30
|
+
params.advanced_scopes = params.scope
|
31
|
+
delete params.scope
|
32
|
+
}
|
33
|
+
if (provider.instagram && /^\d+$/.test(provider.key)) {
|
34
|
+
params.app_id = params.client_id
|
35
|
+
delete params.client_id
|
36
|
+
params.scope = (params.scope || '').replace(/ /g, ',') || undefined
|
37
|
+
}
|
38
|
+
if (provider.optimizely && params.scope) {
|
39
|
+
params.scopes = params.scope
|
40
|
+
delete params.scope
|
41
|
+
}
|
42
|
+
if (provider.tiktok) {
|
43
|
+
params.client_key = params.client_id
|
44
|
+
delete params.client_id
|
45
|
+
}
|
46
|
+
if (provider.visualstudio) {
|
47
|
+
params.response_type = 'Assertion'
|
48
|
+
}
|
49
|
+
if (provider.wechat) {
|
50
|
+
params.appid = params.client_id
|
51
|
+
delete params.client_id
|
52
|
+
}
|
53
|
+
if (provider.subdomain) {
|
54
|
+
url = url.replace('[subdomain]', provider.subdomain)
|
55
|
+
}
|
56
|
+
var querystring = qs.stringify(params)
|
57
|
+
if (provider.unsplash && params.scope) {
|
58
|
+
var scope = params.scope
|
59
|
+
delete params.scope
|
60
|
+
querystring = qs.stringify(params) + '&scope=' + scope
|
61
|
+
}
|
62
|
+
return {provider, input, output: `${url}?${querystring}`}
|
63
|
+
}
|
64
|
+
|
65
|
+
exports.access = ({request:client}) => async ({provider, input, input:{query, body, session}}) => {
|
66
|
+
query = Object.keys(query).length ? query : body
|
67
|
+
if (!query.code) {
|
68
|
+
var output = Object.keys(query).length
|
69
|
+
? query : {error: 'Grant: OAuth2 missing code parameter'}
|
70
|
+
return {provider, input, output}
|
71
|
+
}
|
72
|
+
else if (session.state && (query.state !== session.state)) {
|
73
|
+
var output = {error: 'Grant: OAuth2 state mismatch'}
|
74
|
+
return {provider, input, output}
|
75
|
+
}
|
76
|
+
var options = {
|
77
|
+
method: 'POST',
|
78
|
+
url: provider.access_url,
|
79
|
+
form: {
|
80
|
+
grant_type: 'authorization_code',
|
81
|
+
code: query.code,
|
82
|
+
client_id: provider.key,
|
83
|
+
client_secret: provider.secret,
|
84
|
+
redirect_uri: provider.redirect_uri
|
85
|
+
}
|
86
|
+
}
|
87
|
+
if (provider.pkce) {
|
88
|
+
options.form.code_verifier = session.code_verifier
|
89
|
+
}
|
90
|
+
if (provider.basecamp) {
|
91
|
+
options.form.type = 'web_server'
|
92
|
+
}
|
93
|
+
if (provider.concur) {
|
94
|
+
delete options.form
|
95
|
+
options.qs = {
|
96
|
+
code: query.code,
|
97
|
+
client_id: provider.key,
|
98
|
+
client_secret: provider.secret
|
99
|
+
}
|
100
|
+
}
|
101
|
+
if (/autodesk|ebay|fitbit|homeaway|hootsuite|notion|reddit|trustpilot/.test(provider.name)
|
102
|
+
|| provider.token_endpoint_auth_method === 'client_secret_basic'
|
103
|
+
) {
|
104
|
+
delete options.form.client_id
|
105
|
+
delete options.form.client_secret
|
106
|
+
options.auth = {user: provider.key, pass: provider.secret}
|
107
|
+
}
|
108
|
+
if (/twitter/.test(provider.name)) {
|
109
|
+
options.form.client_id = provider.key
|
110
|
+
delete options.form.client_secret
|
111
|
+
options.auth = {user: provider.key, pass: provider.secret}
|
112
|
+
}
|
113
|
+
if (provider.token_endpoint_auth_method === 'private_key_jwt') {
|
114
|
+
var jwt = ({kid, x5t, secret}) => ({
|
115
|
+
header: {
|
116
|
+
typ: 'JWT',
|
117
|
+
alg: provider.token_endpoint_auth_signing_alg || 'RS256',
|
118
|
+
kid,
|
119
|
+
x5t
|
120
|
+
},
|
121
|
+
payload: {
|
122
|
+
iss: provider.key,
|
123
|
+
sub: provider.key,
|
124
|
+
aud: provider.access_url,
|
125
|
+
jti: crypto.randomBytes(20).toString('hex'),
|
126
|
+
exp: Math.round(Date.now() / 1000) + 300,
|
127
|
+
iat: Math.round(Date.now() / 1000) - 120,
|
128
|
+
nbf: Math.round(Date.now() / 1000) - 120
|
129
|
+
},
|
130
|
+
secret
|
131
|
+
})
|
132
|
+
|
133
|
+
var assertion = (() => {
|
134
|
+
var oidc = require('../oidc')
|
135
|
+
var {public_key, private_key} = provider
|
136
|
+
return oidc.sign(jwt({
|
137
|
+
kid: private_key.kty ? oidc.kid(private_key) : undefined,
|
138
|
+
x5t: public_key ? public_key.kty ? public_key.x5t : oidc.x5t(public_key) : undefined,
|
139
|
+
secret: private_key.kty ? oidc.pem(private_key) : private_key,
|
140
|
+
}))
|
141
|
+
})()
|
142
|
+
|
143
|
+
options.form.client_assertion_type = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
144
|
+
options.form.client_assertion = assertion
|
145
|
+
delete options.form.client_id
|
146
|
+
delete options.form.client_secret
|
147
|
+
}
|
148
|
+
if (provider.instagram && /^\d+$/.test(provider.key)) {
|
149
|
+
options.form.app_id = options.form.client_id
|
150
|
+
delete options.form.client_id
|
151
|
+
options.form.app_secret = options.form.client_secret
|
152
|
+
delete options.form.client_secret
|
153
|
+
}
|
154
|
+
if (provider.notion) {
|
155
|
+
options.json = options.form
|
156
|
+
delete options.form
|
157
|
+
}
|
158
|
+
if (provider.tiktok) {
|
159
|
+
options.form.client_key = options.form.client_id
|
160
|
+
delete options.form.client_id
|
161
|
+
}
|
162
|
+
if (provider.qq) {
|
163
|
+
options.method = 'GET'
|
164
|
+
options.qs = options.form
|
165
|
+
delete options.form
|
166
|
+
}
|
167
|
+
if (provider.untappd) {
|
168
|
+
options.method = 'GET'
|
169
|
+
options.qs = options.form
|
170
|
+
delete options.qs.grant_type
|
171
|
+
options.qs.response_type = 'code'
|
172
|
+
delete options.form
|
173
|
+
}
|
174
|
+
if (provider.wechat) {
|
175
|
+
options.method = 'GET'
|
176
|
+
options.qs = options.form
|
177
|
+
delete options.form
|
178
|
+
options.qs.appid = options.qs.client_id
|
179
|
+
options.qs.secret = options.qs.client_secret
|
180
|
+
delete options.qs.client_id
|
181
|
+
delete options.qs.client_secret
|
182
|
+
}
|
183
|
+
if (provider.smartsheet) {
|
184
|
+
delete options.form.client_secret
|
185
|
+
var hash = crypto.createHash('sha256')
|
186
|
+
hash.update(provider.secret + '|' + query.code)
|
187
|
+
options.form.hash = hash.digest('hex')
|
188
|
+
}
|
189
|
+
if (provider.surveymonkey) {
|
190
|
+
options.qs = {api_key: provider.custom_params.api_key}
|
191
|
+
}
|
192
|
+
if (provider.visualstudio) {
|
193
|
+
options.form = {
|
194
|
+
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
|
195
|
+
client_assertion: provider.secret,
|
196
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
197
|
+
assertion: query.code,
|
198
|
+
redirect_uri: provider.redirect_uri
|
199
|
+
}
|
200
|
+
}
|
201
|
+
if (provider.withings) {
|
202
|
+
options.form.action = 'requesttoken'
|
203
|
+
}
|
204
|
+
if (provider.subdomain) {
|
205
|
+
options.url = options.url.replace('[subdomain]', provider.subdomain)
|
206
|
+
}
|
207
|
+
try {
|
208
|
+
var {body:output} = await request({...client, ...options})
|
209
|
+
if (provider.intuit) {
|
210
|
+
output.realmId = query.realmId
|
211
|
+
}
|
212
|
+
if (provider.withings) {
|
213
|
+
output = output.body
|
214
|
+
}
|
215
|
+
}
|
216
|
+
catch (err) {
|
217
|
+
var output = {error: err.body || err.message}
|
218
|
+
}
|
219
|
+
return {provider, input, output}
|
220
|
+
}
|
package/lib/grant.js
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
var {compose} = require('./util')
|
3
|
+
var {defaults, connect, callback} = require('./request')
|
4
|
+
var {data, transport} = require('./response')
|
5
|
+
var _config = require('./config')
|
6
|
+
|
7
|
+
|
8
|
+
module.exports = ({config, request, state, extend}) => {
|
9
|
+
config = _config(config)
|
10
|
+
|
11
|
+
if (!extend) {
|
12
|
+
extend = [require('./profile')]
|
13
|
+
}
|
14
|
+
|
15
|
+
var pipe = compose(
|
16
|
+
defaults(config),
|
17
|
+
|
18
|
+
({provider, input, input:{params}}) => params.override !== 'callback'
|
19
|
+
? connect({request})({provider, input})
|
20
|
+
: compose(
|
21
|
+
callback({request})({provider, input}),
|
22
|
+
data,
|
23
|
+
extend ? compose(...extend.map((fn) => fn({request, state}))) : (args) => ({...args})
|
24
|
+
)({provider, input}),
|
25
|
+
|
26
|
+
transport,
|
27
|
+
)
|
28
|
+
|
29
|
+
pipe.config = config
|
30
|
+
return pipe
|
31
|
+
}
|
@@ -0,0 +1,89 @@
|
|
1
|
+
|
2
|
+
var qs = require('qs')
|
3
|
+
var Grant = require('../grant')
|
4
|
+
var Session = require('../session')
|
5
|
+
|
6
|
+
|
7
|
+
module.exports = function (args = {}) {
|
8
|
+
var grant = Grant(args.config ? args : {config: args})
|
9
|
+
app.config = grant.config
|
10
|
+
|
11
|
+
var regex = new RegExp([
|
12
|
+
'^',
|
13
|
+
app.config.defaults.prefix,
|
14
|
+
/(?:\/([^\/\?]+?))/.source, // /:provider
|
15
|
+
/(?:\/([^\/\?]+?))?/.source, // /:override?
|
16
|
+
/(?:\/$|\/?\?+.*)?$/.source, // querystring
|
17
|
+
].join(''), 'i')
|
18
|
+
|
19
|
+
var store = Session(args.session)
|
20
|
+
|
21
|
+
async function app (event, state) {
|
22
|
+
var req = params(event)
|
23
|
+
var session = store(req)
|
24
|
+
var match = regex.exec(req.path)
|
25
|
+
if (!match) {
|
26
|
+
return {session}
|
27
|
+
}
|
28
|
+
|
29
|
+
var {location, session:sess, state} = await grant({
|
30
|
+
method: req.method,
|
31
|
+
params: {provider: match[1], override: match[2]},
|
32
|
+
query: req.query,
|
33
|
+
body: req.body,
|
34
|
+
state,
|
35
|
+
session: (await session.get()).grant
|
36
|
+
})
|
37
|
+
|
38
|
+
await session.set({grant: sess})
|
39
|
+
|
40
|
+
return location
|
41
|
+
? {session, redirect: redirect(event, location, session)}
|
42
|
+
: {session, response: state.response || sess.response}
|
43
|
+
}
|
44
|
+
|
45
|
+
return app
|
46
|
+
}
|
47
|
+
|
48
|
+
var path = ({version, path, rawPath, requestContext:ctx} = event) =>
|
49
|
+
version === '2.0' ? rawPath :
|
50
|
+
version === '1.0' ? path : ctx.path
|
51
|
+
|
52
|
+
var body = ({body, isBase64Encoded} = event) =>
|
53
|
+
body
|
54
|
+
? isBase64Encoded ? Buffer.from(body, 'base64').toString()
|
55
|
+
: body : {}
|
56
|
+
|
57
|
+
var params = (event) =>
|
58
|
+
!event.version || event.version === '1.0' ?
|
59
|
+
{
|
60
|
+
method: event.httpMethod,
|
61
|
+
path: path(event),
|
62
|
+
query: qs.parse(event.queryStringParameters),
|
63
|
+
headers: event.headers,
|
64
|
+
body: qs.parse(body(event)),
|
65
|
+
}
|
66
|
+
: event.version === '2.0' ?
|
67
|
+
{
|
68
|
+
method: event.requestContext.http.method,
|
69
|
+
path: path(event),
|
70
|
+
query: qs.parse(event.rawQueryString),
|
71
|
+
headers: {...event.headers, Cookie: (event.cookies || []).join('; ')},
|
72
|
+
body: qs.parse(body(event)),
|
73
|
+
}
|
74
|
+
: {}
|
75
|
+
|
76
|
+
var redirect = (event, location, session) =>
|
77
|
+
!event.version || event.version === '1.0' ?
|
78
|
+
{
|
79
|
+
statusCode: 302,
|
80
|
+
headers: {location},
|
81
|
+
multiValueHeaders: {'set-cookie': session.headers['set-cookie']}
|
82
|
+
}
|
83
|
+
: event.version === '2.0' ?
|
84
|
+
{
|
85
|
+
statusCode: 302,
|
86
|
+
headers: {location},
|
87
|
+
cookies: session.headers['set-cookie']
|
88
|
+
}
|
89
|
+
: {}
|