@hapi/bell 13.1.0 → 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 CHANGED
@@ -88,7 +88,7 @@ export interface OptionalOptions {
88
88
  * * Twitter supports `force_login`, `screen_name`.
89
89
  * * Linkedin supports `fields`.
90
90
  */
91
- providerParams?: StringLikeMap | ((request: Request) => StringLikeMap) | undefined;
91
+ providerParams?: StringLikeMap | ((request: Request) => StringLikeMap | Promise<StringLikeMap>) | undefined;
92
92
  /**
93
93
  * allows passing query parameters from a bell protected endpoint to the auth request.
94
94
  * It will merge the query params you pass along with the providerParams and any other predefined ones.
@@ -129,7 +129,7 @@ export interface OptionalOptions {
129
129
  * It may be passed either as an object to merge into the query string,
130
130
  * or a function which takes the client's request and returns an object.
131
131
  */
132
- tokenParams?: StringLikeMap | ((request: Request) => StringLikeMap) | undefined;
132
+ tokenParams?: StringLikeMap | ((request: Request) => StringLikeMap | Promise<StringLikeMap>) | undefined;
133
133
  /**
134
134
  * an object of key-value pairs that specify additional
135
135
  * URL query parameters to send with the profile request to the provider.
@@ -204,6 +204,12 @@ export interface Credentials2 extends Credentials {
204
204
  expiresIn?: number | undefined;
205
205
  }
206
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
+
207
213
  export interface CustomProtocol {
208
214
  /**
209
215
  * The name of the protocol.
@@ -213,11 +219,11 @@ export interface CustomProtocol {
213
219
  /**
214
220
  * the authorization endpoint URI.
215
221
  */
216
- auth: string;
222
+ auth: ProviderUri;
217
223
  /**
218
224
  * the access token endpoint URI.
219
225
  */
220
- token: string;
226
+ token: ProviderUri;
221
227
  /**
222
228
  * a headers object with additional headers required by the provider
223
229
  * (e.g. GitHub required the 'User-Agent' header which is set by default).
@@ -233,12 +239,14 @@ export interface CustomProtocol {
233
239
  * Change the object directly within the function (profile information is typically stored under credentials.profile).
234
240
  * @param params the parsed information received from the provider (e.g. token, secret, and other custom fields).
235
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.
236
243
  */
237
244
  export type ProfileGetter<C extends Credentials> = (
238
245
  this: CustomProviderOptions,
239
246
  credentials: C,
240
247
  params: { [key: string]: string },
241
248
  get: AuthedRequest,
249
+ request: Request,
242
250
  ) => Promise<void>;
243
251
 
244
252
  export interface CustomProtocol1 extends CustomProtocol {
@@ -256,7 +264,7 @@ export interface CustomProtocol1 extends CustomProtocol {
256
264
  /**
257
265
  * the temporary credentials (request token) endpoint).
258
266
  */
259
- temporary?: string | undefined;
267
+ temporary?: ProviderUri | undefined;
260
268
 
261
269
  profile: ProfileGetter<Credentials1>;
262
270
  }
@@ -307,4 +315,4 @@ export function simulate(credentialsFunc: RequestPassThrough): void;
307
315
  * [See docs](https://github.com/hapijs/bell/blob/master/API.md#simulated-authentication)
308
316
  * Disables simulation mode
309
317
  */
310
- 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: Joi.string().required(),
42
- token: Joi.string().required(),
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: Joi.string().required(),
51
+ temporary: internals.endpoint.required(),
51
52
  signatureMethod: Joi.valid('HMAC-SHA1', 'RSA-SHA1').default('HMAC-SHA1')
52
53
  }),
53
54
  otherwise: Joi.object({
package/lib/oauth.js CHANGED
@@ -19,10 +19,16 @@ const internals = {
19
19
 
20
20
  exports.v1 = function (settings) {
21
21
 
22
- const client = new internals.Client(settings);
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);
@@ -73,12 +79,13 @@ exports.v1 = function (settings) {
73
79
  h.state(cookie, state);
74
80
 
75
81
  const authQuery = {
76
- ...internals.resolveProviderParams(request, settings.providerParams),
82
+ ...await internals.resolveProviderParams(request, settings.providerParams),
77
83
  oauth_token: temp.oauth_token,
78
84
  ...(settings.allowRuntimeProviderParams && request.query)
79
85
  };
80
86
 
81
- return h.redirect(settings.provider.auth + '?' + internals.queryString(authQuery)).takeover();
87
+ const authUri = await internals.resolveProviderUri(request, settings.provider.auth);
88
+ return h.redirect(authUri + '?' + internals.queryString(authQuery)).takeover();
82
89
  }
83
90
 
84
91
  // Authorization callback
@@ -130,7 +137,7 @@ exports.v1 = function (settings) {
130
137
  };
131
138
 
132
139
  try {
133
- await settings.provider.profile.call(settings, credentials, token, get);
140
+ await settings.provider.profile.call(settings, credentials, token, get, request);
134
141
  }
135
142
  catch (err) {
136
143
  return h.unauthenticated(err, { credentials });
@@ -178,7 +185,7 @@ exports.v2 = function (settings) {
178
185
 
179
186
  const nonce = Cryptiles.randomAlphanumString(internals.nonceLength);
180
187
  const query = {
181
- ...internals.resolveProviderParams(request, settings.providerParams),
188
+ ...await internals.resolveProviderParams(request, settings.providerParams),
182
189
  ...(settings.allowRuntimeProviderParams && request.query),
183
190
  client_id: settings.clientId,
184
191
  response_type: 'code',
@@ -227,7 +234,9 @@ exports.v2 = function (settings) {
227
234
  }
228
235
 
229
236
  h.state(cookie, state);
230
- return h.redirect(settings.provider.auth + '?' + internals.queryString(query)).takeover();
237
+
238
+ const authUri = await internals.resolveProviderUri(request, settings.provider.auth);
239
+ return h.redirect(authUri + '?' + internals.queryString(query)).takeover();
231
240
  }
232
241
 
233
242
  // Authorization callback
@@ -249,7 +258,7 @@ exports.v2 = function (settings) {
249
258
  grant_type: 'authorization_code',
250
259
  code: request.query.code,
251
260
  redirect_uri: internals.location(request, protocol, settings.location),
252
- ...internals.resolveProviderParams(request, settings.tokenParams)
261
+ ...await internals.resolveProviderParams(request, settings.tokenParams)
253
262
  };
254
263
 
255
264
  if (settings.provider.pkce) {
@@ -287,8 +296,9 @@ exports.v2 = function (settings) {
287
296
 
288
297
  // Obtain token
289
298
 
299
+ const tokenUri = await internals.resolveProviderUri(request, settings.provider.token);
290
300
  try {
291
- var { res: tokenRes, payload } = await Wreck.post(settings.provider.token, requestOptions);
301
+ var { res: tokenRes, payload } = await Wreck.post(tokenUri, requestOptions);
292
302
  }
293
303
  catch (err) {
294
304
  return h.unauthenticated(Boom.internal('Failed obtaining ' + name + ' access token', err), { credentials });
@@ -358,7 +368,7 @@ exports.v2 = function (settings) {
358
368
  };
359
369
 
360
370
  try {
361
- await settings.provider.profile.call(settings, credentials, payload, get);
371
+ await settings.provider.profile.call(settings, credentials, payload, get, request);
362
372
  }
363
373
  catch (err) {
364
374
  return h.unauthenticated(err, { credentials });
@@ -727,7 +737,25 @@ internals.getProtocol = function (request, settings) {
727
737
  };
728
738
 
729
739
 
730
- 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
+ };
731
759
 
732
- return (typeof params === 'function' ? params(request) : params) ?? {};
760
+ return new internals.Client({ ...settings, provider });
733
761
  };
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.1.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.1",
55
- "@hapi/cryptiles": "^6.0.1",
56
- "@hapi/hoek": "^11.0.2",
57
- "@hapi/wreck": "^18.0.1",
58
- "joi": "^17.7.1"
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
62
  "@hapi/eslint-plugin": "^6.0.0",
63
- "@hapi/hapi": "^21.2.1",
63
+ "@hapi/hapi": "^21.4.10",
64
64
  "@hapi/hawk": "^8.0.0",
65
- "@hapi/lab": "^25.0.1",
66
- "@hapi/teamwork": "^6.0.0",
67
- "@types/node": "^20.11.30",
68
- "typescript": "^5.4.2"
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",