@hapi/bell 13.0.2 → 13.2.0
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/lib/index.d.ts +19 -6
- package/lib/index.js +6 -3
- package/lib/oauth.js +52 -27
- package/lib/providers/index.js +0 -1
- package/package.json +12 -12
- package/lib/providers/mixer.js +0 -25
package/lib/index.d.ts
CHANGED
|
@@ -30,7 +30,6 @@ export type Provider =
|
|
|
30
30
|
| 'live'
|
|
31
31
|
| 'medium'
|
|
32
32
|
| 'meetup'
|
|
33
|
-
| 'mixer'
|
|
34
33
|
| 'nest'
|
|
35
34
|
| 'okta'
|
|
36
35
|
| 'phabricator'
|
|
@@ -89,7 +88,7 @@ export interface OptionalOptions {
|
|
|
89
88
|
* * Twitter supports `force_login`, `screen_name`.
|
|
90
89
|
* * Linkedin supports `fields`.
|
|
91
90
|
*/
|
|
92
|
-
providerParams?: StringLikeMap | ((request: Request) => StringLikeMap) | undefined;
|
|
91
|
+
providerParams?: StringLikeMap | ((request: Request) => StringLikeMap | Promise<StringLikeMap>) | undefined;
|
|
93
92
|
/**
|
|
94
93
|
* allows passing query parameters from a bell protected endpoint to the auth request.
|
|
95
94
|
* It will merge the query params you pass along with the providerParams and any other predefined ones.
|
|
@@ -125,6 +124,12 @@ export interface OptionalOptions {
|
|
|
125
124
|
| { extendedProfile?: boolean | undefined; getMethod?: string | undefined }
|
|
126
125
|
| { uri?: string | undefined }
|
|
127
126
|
| undefined;
|
|
127
|
+
/**
|
|
128
|
+
* provider-specific query parameters for the token endpoint.
|
|
129
|
+
* It may be passed either as an object to merge into the query string,
|
|
130
|
+
* or a function which takes the client's request and returns an object.
|
|
131
|
+
*/
|
|
132
|
+
tokenParams?: StringLikeMap | ((request: Request) => StringLikeMap | Promise<StringLikeMap>) | undefined;
|
|
128
133
|
/**
|
|
129
134
|
* an object of key-value pairs that specify additional
|
|
130
135
|
* URL query parameters to send with the profile request to the provider.
|
|
@@ -199,6 +204,12 @@ export interface Credentials2 extends Credentials {
|
|
|
199
204
|
expiresIn?: number | undefined;
|
|
200
205
|
}
|
|
201
206
|
|
|
207
|
+
/**
|
|
208
|
+
* an endpoint URI, or a function of the request returning one.
|
|
209
|
+
* @param request the hapi request object being authenticated.
|
|
210
|
+
*/
|
|
211
|
+
export type ProviderUri = string | ((request: Request) => string | Promise<string>);
|
|
212
|
+
|
|
202
213
|
export interface CustomProtocol {
|
|
203
214
|
/**
|
|
204
215
|
* The name of the protocol.
|
|
@@ -208,11 +219,11 @@ export interface CustomProtocol {
|
|
|
208
219
|
/**
|
|
209
220
|
* the authorization endpoint URI.
|
|
210
221
|
*/
|
|
211
|
-
auth:
|
|
222
|
+
auth: ProviderUri;
|
|
212
223
|
/**
|
|
213
224
|
* the access token endpoint URI.
|
|
214
225
|
*/
|
|
215
|
-
token:
|
|
226
|
+
token: ProviderUri;
|
|
216
227
|
/**
|
|
217
228
|
* a headers object with additional headers required by the provider
|
|
218
229
|
* (e.g. GitHub required the 'User-Agent' header which is set by default).
|
|
@@ -228,12 +239,14 @@ export interface CustomProtocol {
|
|
|
228
239
|
* Change the object directly within the function (profile information is typically stored under credentials.profile).
|
|
229
240
|
* @param params the parsed information received from the provider (e.g. token, secret, and other custom fields).
|
|
230
241
|
* @param get an OAuth helper function to make authenticated requests using the credentials received.
|
|
242
|
+
* @param request the hapi request object of the authorization callback.
|
|
231
243
|
*/
|
|
232
244
|
export type ProfileGetter<C extends Credentials> = (
|
|
233
245
|
this: CustomProviderOptions,
|
|
234
246
|
credentials: C,
|
|
235
247
|
params: { [key: string]: string },
|
|
236
248
|
get: AuthedRequest,
|
|
249
|
+
request: Request,
|
|
237
250
|
) => Promise<void>;
|
|
238
251
|
|
|
239
252
|
export interface CustomProtocol1 extends CustomProtocol {
|
|
@@ -251,7 +264,7 @@ export interface CustomProtocol1 extends CustomProtocol {
|
|
|
251
264
|
/**
|
|
252
265
|
* the temporary credentials (request token) endpoint).
|
|
253
266
|
*/
|
|
254
|
-
temporary?:
|
|
267
|
+
temporary?: ProviderUri | undefined;
|
|
255
268
|
|
|
256
269
|
profile: ProfileGetter<Credentials1>;
|
|
257
270
|
}
|
|
@@ -302,4 +315,4 @@ export function simulate(credentialsFunc: RequestPassThrough): void;
|
|
|
302
315
|
* [See docs](https://github.com/hapijs/bell/blob/master/API.md#simulated-authentication)
|
|
303
316
|
* Disables simulation mode
|
|
304
317
|
*/
|
|
305
|
-
export function simulate(state: false): void;
|
|
318
|
+
export function simulate(state: false): void;
|
package/lib/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const Providers = require('./providers');
|
|
|
9
9
|
|
|
10
10
|
const internals = {
|
|
11
11
|
simulate: false,
|
|
12
|
+
endpoint: Joi.alternatives(Joi.string(), Joi.func()),
|
|
12
13
|
flexBoolean: Joi.boolean().truthy('yes', 1, '1').falsy('no', 0, '0')
|
|
13
14
|
};
|
|
14
15
|
|
|
@@ -38,8 +39,8 @@ exports.plugin = {
|
|
|
38
39
|
internals.provider = Joi.object({
|
|
39
40
|
name: Joi.string().optional().default('custom'),
|
|
40
41
|
protocol: ['oauth', 'oauth2'],
|
|
41
|
-
auth:
|
|
42
|
-
token:
|
|
42
|
+
auth: internals.endpoint.required(),
|
|
43
|
+
token: internals.endpoint.required(),
|
|
43
44
|
headers: Joi.object(),
|
|
44
45
|
profile: Joi.func(),
|
|
45
46
|
profileMethod: Joi.valid('get', 'post').default('get')
|
|
@@ -47,7 +48,7 @@ internals.provider = Joi.object({
|
|
|
47
48
|
.when('.protocol', {
|
|
48
49
|
is: 'oauth',
|
|
49
50
|
then: Joi.object({
|
|
50
|
-
temporary:
|
|
51
|
+
temporary: internals.endpoint.required(),
|
|
51
52
|
signatureMethod: Joi.valid('HMAC-SHA1', 'RSA-SHA1').default('HMAC-SHA1')
|
|
52
53
|
}),
|
|
53
54
|
otherwise: Joi.object({
|
|
@@ -104,6 +105,8 @@ internals.schema = Joi.object({
|
|
|
104
105
|
|
|
105
106
|
config: Joi.object(),
|
|
106
107
|
|
|
108
|
+
tokenParams: Joi.alternatives(Joi.object(), Joi.func()),
|
|
109
|
+
|
|
107
110
|
profileParams: Joi.object(),
|
|
108
111
|
|
|
109
112
|
skipProfile: internals.flexBoolean.optional().default(false),
|
package/lib/oauth.js
CHANGED
|
@@ -19,10 +19,16 @@ const internals = {
|
|
|
19
19
|
|
|
20
20
|
exports.v1 = function (settings) {
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// The client uses temporary and token, so we can share one unless either is a function
|
|
23
|
+
|
|
24
|
+
const shared = [settings.provider.temporary, settings.provider.token].some((endpoint) => typeof endpoint === 'function') ?
|
|
25
|
+
null :
|
|
26
|
+
new internals.Client(settings);
|
|
23
27
|
|
|
24
28
|
return async function (request, h) {
|
|
25
29
|
|
|
30
|
+
const client = shared ?? await internals.client(request, settings);
|
|
31
|
+
|
|
26
32
|
const cookie = settings.cookie;
|
|
27
33
|
const name = settings.name;
|
|
28
34
|
const protocol = internals.getProtocol(request, settings);
|
|
@@ -72,14 +78,14 @@ exports.v1 = function (settings) {
|
|
|
72
78
|
|
|
73
79
|
h.state(cookie, state);
|
|
74
80
|
|
|
75
|
-
const authQuery =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
+
const authQuery = {
|
|
82
|
+
...await internals.resolveProviderParams(request, settings.providerParams),
|
|
83
|
+
oauth_token: temp.oauth_token,
|
|
84
|
+
...(settings.allowRuntimeProviderParams && request.query)
|
|
85
|
+
};
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
const authUri = await internals.resolveProviderUri(request, settings.provider.auth);
|
|
88
|
+
return h.redirect(authUri + '?' + internals.queryString(authQuery)).takeover();
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
// Authorization callback
|
|
@@ -123,7 +129,7 @@ exports.v1 = function (settings) {
|
|
|
123
129
|
const get = async (uri, params = {}) => {
|
|
124
130
|
|
|
125
131
|
if (settings.profileParams) {
|
|
126
|
-
|
|
132
|
+
Object.assign(params, settings.profileParams);
|
|
127
133
|
}
|
|
128
134
|
|
|
129
135
|
const { payload: resource } = await client.resource('get', uri, params, { token: token.oauth_token, secret: token.oauth_token_secret });
|
|
@@ -131,7 +137,7 @@ exports.v1 = function (settings) {
|
|
|
131
137
|
};
|
|
132
138
|
|
|
133
139
|
try {
|
|
134
|
-
await settings.provider.profile.call(settings, credentials, token, get);
|
|
140
|
+
await settings.provider.profile.call(settings, credentials, token, get, request);
|
|
135
141
|
}
|
|
136
142
|
catch (err) {
|
|
137
143
|
return h.unauthenticated(err, { credentials });
|
|
@@ -178,16 +184,14 @@ exports.v2 = function (settings) {
|
|
|
178
184
|
credentials.query = request.query;
|
|
179
185
|
|
|
180
186
|
const nonce = Cryptiles.randomAlphanumString(internals.nonceLength);
|
|
181
|
-
const query =
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
query.redirect_uri = internals.location(request, protocol, settings.location);
|
|
190
|
-
query.state = nonce;
|
|
187
|
+
const query = {
|
|
188
|
+
...await internals.resolveProviderParams(request, settings.providerParams),
|
|
189
|
+
...(settings.allowRuntimeProviderParams && request.query),
|
|
190
|
+
client_id: settings.clientId,
|
|
191
|
+
response_type: 'code',
|
|
192
|
+
redirect_uri: internals.location(request, protocol, settings.location),
|
|
193
|
+
state: nonce
|
|
194
|
+
};
|
|
191
195
|
|
|
192
196
|
if (settings.runtimeStateCallback) {
|
|
193
197
|
const runtimeState = settings.runtimeStateCallback(request);
|
|
@@ -230,7 +234,9 @@ exports.v2 = function (settings) {
|
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
h.state(cookie, state);
|
|
233
|
-
|
|
237
|
+
|
|
238
|
+
const authUri = await internals.resolveProviderUri(request, settings.provider.auth);
|
|
239
|
+
return h.redirect(authUri + '?' + internals.queryString(query)).takeover();
|
|
234
240
|
}
|
|
235
241
|
|
|
236
242
|
// Authorization callback
|
|
@@ -251,7 +257,8 @@ exports.v2 = function (settings) {
|
|
|
251
257
|
const query = {
|
|
252
258
|
grant_type: 'authorization_code',
|
|
253
259
|
code: request.query.code,
|
|
254
|
-
redirect_uri: internals.location(request, protocol, settings.location)
|
|
260
|
+
redirect_uri: internals.location(request, protocol, settings.location),
|
|
261
|
+
...await internals.resolveProviderParams(request, settings.tokenParams)
|
|
255
262
|
};
|
|
256
263
|
|
|
257
264
|
if (settings.provider.pkce) {
|
|
@@ -289,8 +296,9 @@ exports.v2 = function (settings) {
|
|
|
289
296
|
|
|
290
297
|
// Obtain token
|
|
291
298
|
|
|
299
|
+
const tokenUri = await internals.resolveProviderUri(request, settings.provider.token);
|
|
292
300
|
try {
|
|
293
|
-
var { res: tokenRes, payload } = await Wreck.post(
|
|
301
|
+
var { res: tokenRes, payload } = await Wreck.post(tokenUri, requestOptions);
|
|
294
302
|
}
|
|
295
303
|
catch (err) {
|
|
296
304
|
return h.unauthenticated(Boom.internal('Failed obtaining ' + name + ' access token', err), { credentials });
|
|
@@ -360,7 +368,7 @@ exports.v2 = function (settings) {
|
|
|
360
368
|
};
|
|
361
369
|
|
|
362
370
|
try {
|
|
363
|
-
await settings.provider.profile.call(settings, credentials, payload, get);
|
|
371
|
+
await settings.provider.profile.call(settings, credentials, payload, get, request);
|
|
364
372
|
}
|
|
365
373
|
catch (err) {
|
|
366
374
|
return h.unauthenticated(err, { credentials });
|
|
@@ -729,8 +737,25 @@ internals.getProtocol = function (request, settings) {
|
|
|
729
737
|
};
|
|
730
738
|
|
|
731
739
|
|
|
732
|
-
internals.resolveProviderParams = function (request, params) {
|
|
740
|
+
internals.resolveProviderParams = async function (request, params) {
|
|
741
|
+
|
|
742
|
+
return (typeof params === 'function' ? await params(request) : params) ?? {};
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
internals.resolveProviderUri = async function (request, uri) {
|
|
747
|
+
|
|
748
|
+
return typeof uri === 'function' ? await uri(request) : uri;
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
internals.client = async function (request, settings) {
|
|
753
|
+
|
|
754
|
+
const provider = {
|
|
755
|
+
...settings.provider,
|
|
756
|
+
temporary: await internals.resolveProviderUri(request, settings.provider.temporary),
|
|
757
|
+
token: await internals.resolveProviderUri(request, settings.provider.token)
|
|
758
|
+
};
|
|
733
759
|
|
|
734
|
-
|
|
735
|
-
return obj ? Hoek.clone(obj) : {};
|
|
760
|
+
return new internals.Client({ ...settings, provider });
|
|
736
761
|
};
|
package/lib/providers/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hapi/bell",
|
|
3
3
|
"description": "Third-party login plugin for hapi",
|
|
4
|
-
"version": "13.0
|
|
4
|
+
"version": "13.2.0",
|
|
5
5
|
"repository": "git://github.com/hapijs/bell",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/index.d.ts",
|
|
@@ -51,21 +51,21 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@hapi/boom": "^10.0.1",
|
|
54
|
-
"@hapi/bounce": "^3.0.
|
|
55
|
-
"@hapi/cryptiles": "^6.0.
|
|
56
|
-
"@hapi/hoek": "^11.0.
|
|
57
|
-
"@hapi/wreck": "^18.
|
|
58
|
-
"joi": "^17.7
|
|
54
|
+
"@hapi/bounce": "^3.0.2",
|
|
55
|
+
"@hapi/cryptiles": "^6.0.3",
|
|
56
|
+
"@hapi/hoek": "^11.0.7",
|
|
57
|
+
"@hapi/wreck": "^18.1.2",
|
|
58
|
+
"joi": "^17.13.7"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@hapi/code": "^9.0.3",
|
|
62
|
-
"@hapi/eslint-plugin": "
|
|
63
|
-
"@hapi/hapi": "^21.
|
|
62
|
+
"@hapi/eslint-plugin": "^6.0.0",
|
|
63
|
+
"@hapi/hapi": "^21.4.10",
|
|
64
64
|
"@hapi/hawk": "^8.0.0",
|
|
65
|
-
"@hapi/lab": "^25.
|
|
66
|
-
"@hapi/teamwork": "^6.0.
|
|
67
|
-
"@types/node": "^20.
|
|
68
|
-
"typescript": "^
|
|
65
|
+
"@hapi/lab": "^25.3.2",
|
|
66
|
+
"@hapi/teamwork": "^6.0.1",
|
|
67
|
+
"@types/node": "^20.19.43",
|
|
68
|
+
"typescript": "^6.0.3"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
71
|
"test": "lab -a @hapi/code -t 100 -L -m 3000 -Y",
|
package/lib/providers/mixer.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const internals = {};
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
exports = module.exports = function (options) {
|
|
7
|
-
|
|
8
|
-
return {
|
|
9
|
-
protocol: 'oauth2',
|
|
10
|
-
useParamsAuth: true,
|
|
11
|
-
auth: 'https://mixer.com/oauth/authorize',
|
|
12
|
-
token: 'https://mixer.com/api/v1/oauth/token',
|
|
13
|
-
scope: ['user:details:self'],
|
|
14
|
-
scopeSeparator: ' ',
|
|
15
|
-
profile: async function (credentials, params, get) {
|
|
16
|
-
|
|
17
|
-
const queryOptions = {
|
|
18
|
-
oauth_token: params.access_token
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const profile = await get('https://mixer.com/api/v1/users/current', queryOptions);
|
|
22
|
-
credentials.profile = profile;
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
};
|