• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

box / box-node-sdk / 15559984448

10 Jun 2025 12:51PM UTC coverage: 95.194%. Remained the same
15559984448

push

github

web-flow
chore: Bump dependencies and fix AI integration tests (#892)

846 of 916 branches covered (92.36%)

Branch coverage included in aggregate %.

2599 of 2653 new or added lines in 51 files covered. (97.96%)

1 existing line in 1 file now uncovered.

2977 of 3100 relevant lines covered (96.03%)

580.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.0
/src/box-node-sdk.ts
1
/**
2
 * @fileoverview Box SDK for Node.js
3
 */
4

5
// ------------------------------------------------------------------------------
6
// Requirements
7
// ------------------------------------------------------------------------------
8

9
import { EventEmitter } from 'events';
1,120✔
10
import * as qs from 'querystring';
1,120✔
11
import CCGAPISession = require('./sessions/ccg-session');
1,120✔
12
import APIRequestManager = require('./api-request-manager');
1,120✔
13
import BoxClient = require('./box-client');
1,120✔
14
import TokenManager = require('./token-manager');
1,120✔
15

16
const Config = require('./util/config'),
1,120✔
17
  BasicAPISession = require('./sessions/basic-session'),
1,120✔
18
  PersistentAPISession = require('./sessions/persistent-session'),
1,120✔
19
  AppAuthSession = require('./sessions/app-auth-session'),
1,120✔
20
  Webhooks = require('./managers/webhooks');
1,120✔
21

22
// ------------------------------------------------------------------------------
23
// Typedefs and Callbacks
24
// ------------------------------------------------------------------------------
25

26
/**
27
 * Object representing interface functions for PersistentClient to interact with the consumer app's central storage layer.
28
 * @typedef {Object} TokenStore
29
 * @property {ReadTokenInfoFromStore} read - read TokenInfo from app central store.
30
 * @property {WriteTokenInfoToStore} write - write TokenInfo to the app's central store.
31
 * @property {ClearTokenInfoFromStore} clear - delete TokenInfo from the app's central store.
32
 */
33

34
/**
35
 * Acquires TokenInfo from the consumer app's central store.
36
 * @typedef {Function} ReadTokenInfoFromStore
37
 * @param {Function} callback - err if store read issue occurred, otherwise propagates a TokenInfo object
38
 */
39

40
/**
41
 * Writes TokenInfo to the consumer app's central store
42
 * @typedef {Function} WriteTokenInfoToStore
43
 * @param {TokenInfo} tokenInfo - the token info to be written
44
 * @param {Function} callback - err if store write issue occurred, otherwise propagates null err
45
 *  and null result to indicate success
46
 */
47

48
/**
49
 * Clears TokenInfo from the consumer app's central store
50
 * @typedef {Function} ClearTokenInfoFromStore
51
 * @param {Function} callback - err if store delete issue occurred, otherwise propagates null err
52
 *  and null result to indicate success
53
 */
54

55
type TokenStore = object /* FIXME */;
56
type UserConfigurationOptions = object /* FIXME */;
57
type TokenRequestOptions = object /* FIXME */;
58
type CCGConfig = {
59
  boxSubjectType: 'user' | 'enterprise';
60
  boxSubjectId: string;
61
};
62

63
// ------------------------------------------------------------------------------
64
// Private
65
// ------------------------------------------------------------------------------
66

67
// ------------------------------------------------------------------------------
68
// Public
69
// ------------------------------------------------------------------------------
70

71
/**
72
 * A backend NodeJS SDK to interact with the Box V2 API.
73
 * This is the single entry point for all SDK consumer interactions. This is the only file that a 3rd party app
74
 * should require. All other components are private and reached out to via this component.
75
 * 1. Provides getters to spawn client instances for users to interact with the Box API.
76
 * 2. Provides manual capability to acquire tokens via token grant endpoints.
77
 *    However, it is recommended to use clients to do this for you.
78
 * 3. Emits notification events about relevant request/response events. Useful for logging Box API interactions.
79
 *    Notification events: request retries, exceeding max retries, permanent failures.
80
 *
81
 * @param {UserConfigurationOptions} params User settings used to initialize and customize the SDK
82
 * @constructor
83
 */
84
class BoxSDKNode extends EventEmitter {
1,120✔
85
  accessLevels: any /* FIXME */;
86
  collaborationRoles: any /* FIXME */;
87
  CURRENT_USER_ID: any /* FIXME */;
88
  config: any /* FIXME */;
89
  _eventBus!: EventEmitter;
90

91
  requestManager!: APIRequestManager;
92
  tokenManager!: TokenManager;
93
  ccgSession!: CCGAPISession;
94

95
  /**
96
   * Expose the BoxClient property enumerations to the SDK as a whole. This allows
97
   * the consumer to access and use these values from anywhere in their application
98
   * (like a helper) by requiring the SDK, instead of needing to pass the client.
99
   */
100
  static accessLevels = BoxSDKNode.prototype.accessLevels;
1,120✔
101
  static collaborationRoles = BoxSDKNode.prototype.collaborationRoles;
1,120✔
102
  static CURRENT_USER_ID = BoxSDKNode.prototype.CURRENT_USER_ID;
1,120✔
103

104
  /**
105
   * Expose Webhooks.validateMessage() to the SDK as a whole. This allows
106
   * the consumer to call BoxSDK.validateWebhookMessage() by just requiring the SDK,
107
   * instead of needing to create a client (which is not needed to validate messages).
108
   */
109
  static validateWebhookMessage = Webhooks.validateMessage;
1,120✔
110

111
  constructor(params: UserConfigurationOptions) {
112
    super();
2,016✔
113

114
    const eventBus = new EventEmitter();
2,016✔
115

116
    const self = this;
2,016✔
117
    eventBus.on('response', function () {
2,016✔
118
      const args: any /* FIXME */ = [].slice.call(arguments);
1,212✔
119
      args.unshift('response');
1,212✔
120
      self.emit.apply(self, args);
1,212✔
121
    });
122

123
    // Setup the configuration with the given params
124
    this.config = new Config(params);
2,016✔
125
    this._eventBus = eventBus;
2,012✔
126
    this._setup();
2,012✔
127
  }
128

129
  /**
130
   * Setup the SDK instance by instantiating necessary objects with current
131
   * configuration values.
132
   *
133
   * @returns {void}
134
   * @private
135
   */
136
  _setup() {
1,120✔
137
    // Instantiate the request manager
138
    this.requestManager = new APIRequestManager(this.config, this._eventBus);
2,020✔
139

140
    // Initialize the rest of the SDK with the given configuration
141
    this.tokenManager = new TokenManager(this.config, this.requestManager);
2,020✔
142
    this.ccgSession = new CCGAPISession(this.config, this.tokenManager);
2,020✔
143
  }
144

145
  /**
146
   * Gets the BoxSDKNode instance by passing boxAppSettings json downloaded from the developer console.
147
   *
148
   * @param {Object} appConfig boxAppSettings object retrieved from Dev Console.
149
   * @returns {BoxSDKNode} an instance that has been preconfigured with the values from the Dev Console
150
   */
151
  static getPreconfiguredInstance(appConfig: any /* FIXME */) {
1,120✔
152
    if (typeof appConfig.boxAppSettings !== 'object') {
48✔
153
      throw new TypeError(
8✔
154
        'Configuration does not include boxAppSettings object.'
155
      );
156
    }
157

158
    const boxAppSettings = appConfig.boxAppSettings;
40✔
159
    const webhooks = appConfig.webhooks;
40✔
160
    if (typeof webhooks === 'object') {
40✔
161
      Webhooks.setSignatureKeys(webhooks.primaryKey, webhooks.secondaryKey);
4✔
162
    }
163

164
    const params: {
165
      clientID?: string;
166
      clientSecret?: string;
167
      appAuth?: {
168
        keyID?: string;
169
        privateKey?: string;
170
        passphrase?: string;
171
      };
172
      enterpriseID?: string;
173
    } = {};
40✔
174

175
    if (typeof boxAppSettings.clientID === 'string') {
40!
176
      params.clientID = boxAppSettings.clientID;
40✔
177
    }
178

179
    if (typeof boxAppSettings.clientSecret === 'string') {
40!
180
      params.clientSecret = boxAppSettings.clientSecret;
40✔
181
    }
182

183
    // Only try to assign app auth settings if they are present
184
    // Some configurations do not include them (but might include other info, e.g. webhooks)
185
    if (
40✔
186
      typeof boxAppSettings.appAuth === 'object' &&
72✔
187
      boxAppSettings.appAuth.publicKeyID
188
    ) {
189
      params.appAuth = {
28✔
190
        keyID: boxAppSettings.appAuth.publicKeyID, // Assign publicKeyID to keyID
191
        privateKey: boxAppSettings.appAuth.privateKey,
192
      };
193

194
      const passphrase = boxAppSettings.appAuth.passphrase;
28✔
195
      if (typeof passphrase === 'string') {
28✔
196
        params.appAuth.passphrase = passphrase;
24✔
197
      }
198
    }
199

200
    if (typeof appConfig.enterpriseID === 'string') {
40✔
201
      params.enterpriseID = appConfig.enterpriseID;
28✔
202
    }
203

204
    return new BoxSDKNode(params);
40✔
205
  }
206

207
  /**
208
   * Updates the SDK configuration with new parameters.
209
   *
210
   * @param {UserConfigurationOptions} params User settings
211
   * @returns {void}
212
   */
213
  configure(params: UserConfigurationOptions) {
1,120✔
214
    this.config = this.config.extend(params);
8✔
215
    this._setup();
8✔
216
  }
217

218
  /**
219
   * Returns a Box Client with a Basic API Session. The client is able to make requests on behalf of a user.
220
   * A basic session has no access to a user's refresh token. Because of this, once the session's tokens
221
   * expire the client cannot recover and a new session will need to be generated.
222
   *
223
   * @param {string} accessToken A user's Box API access token
224
   * @returns {BoxClient} Returns a new Box Client paired to a new BasicAPISession
225
   */
226
  getBasicClient(accessToken: string) {
1,120✔
227
    const apiSession = new BasicAPISession(accessToken, this.tokenManager);
1,816✔
228
    return new BoxClient(apiSession, this.config, this.requestManager);
1,816✔
229
  }
230

231
  /**
232
   * Returns a Box Client with a Basic API Session. The client is able to make requests on behalf of a user.
233
   * A basic session has no access to a user's refresh token. Because of this, once the session's tokens
234
   * expire the client cannot recover and a new session will need to be generated.
235
   *
236
   * @param {string} accessToken A user's Box API access token
237
   * @returns {BoxClient} Returns a new Box Client paired to a new BasicAPISession
238
   */
239
  static getBasicClient(accessToken: string) {
1,120✔
240
    return new BoxSDKNode({
4✔
241
      clientID: '',
242
      clientSecret: '',
243
    }).getBasicClient(accessToken);
244
  }
245

246
  /**
247
   * Returns a Box Client with a persistent API session. A persistent API session helps manage the user's tokens,
248
   * and can refresh them automatically if the access token expires. If a central data-store is given, the session
249
   * can read & write tokens to it.
250
   *
251
   * NOTE: If tokenInfo or tokenStore are formatted incorrectly, this method will throw an error. If you
252
   * haven't explicitly created either of these objects or are otherwise not completly confident in their validity,
253
   * you should wrap your call to getPersistentClient in a try-catch to handle any potential errors.
254
   *
255
   * @param {TokenInfo} tokenInfo A tokenInfo object to use for authentication
256
   * @param {TokenStore} [tokenStore] An optional token store for reading/writing tokens to session
257
   * @returns {BoxClient} Returns a new Box Client paired to a new PersistentAPISession
258
   */
259
  getPersistentClient(tokenInfo: any /* FIXME */, tokenStore?: TokenStore) {
1,120✔
260
    const apiSession = new PersistentAPISession(
16✔
261
      tokenInfo,
262
      tokenStore,
263
      this.config,
264
      this.tokenManager
265
    );
266
    return new BoxClient(apiSession, this.config, this.requestManager);
16✔
267
  }
268

269
  /**
270
   * Returns a Box Client configured to use Client Credentials Grant for a service account. Requires enterprise ID
271
   * to be set when configuring SDK instance.
272
   *
273
   * @returns {BoxClient} Returns a new Box Client paired to a AnonymousAPISession. All Anonymous API Sessions share the
274
   * same tokens, which allows them to refresh them efficiently and reduce load on both the application and
275
   * the API.
276
   */
277
  getAnonymousClient() {
1,120✔
278
    if (!this.config.enterpriseID) {
12!
NEW
279
      throw new Error('Enterprise ID must be passed');
×
280
    }
281
    return this._getCCGClient({
12✔
282
      boxSubjectType: 'enterprise',
283
      boxSubjectId: this.config.enterpriseID,
284
    });
285
  }
286

287
  /**
288
   * Returns a Box Client configured to use Client Credentials Grant for a specified user.
289
   *
290
   * @param userId the user ID to use when getting the access token
291
   * @returns {BoxClient} Returns a new Box Client paired to a AnonymousAPISession. All Anonymous API Sessions share the
292
   * same tokens, which allows them to refresh them efficiently and reduce load on both the application and
293
   * the API.
294
   */
295
  getCCGClientForUser(userId: string) {
1,120✔
296
    return this._getCCGClient({
12✔
297
      boxSubjectType: 'user',
298
      boxSubjectId: userId,
299
    });
300
  }
301

302
  _getCCGClient(config: CCGConfig) {
1,120✔
303
    const anonymousTokenManager = new TokenManager(
24✔
304
      {
305
        ...this.config,
306
        ...config,
307
      },
308
      this.requestManager
309
    );
310
    const newAnonymousSession = new CCGAPISession(
24✔
311
      this.config,
312
      anonymousTokenManager
313
    );
314
    return new BoxClient(newAnonymousSession, this.config, this.requestManager);
24✔
315
  }
316

317
  /**
318
   * Create a new client using App Auth for the given entity. This allows either
319
   * managing App Users (as the enterprise) or performing operations as the App
320
   * Users or Managed Users themselves (as a user).
321
   *
322
   * @param {string} type The type of entity to operate as, "enterprise" or "user"
323
   * @param {string} [id] (Optional) The Box ID of the entity to operate as
324
   * @param {TokenStore} [tokenStore] (Optional) the token store to use for caching tokens
325
   * @returns {BoxClient} A new client authorized as the app user or enterprise
326
   */
327
  getAppAuthClient(type: string, id?: string, tokenStore?: TokenStore) {
1,120✔
328
    if (type === 'enterprise' && !id) {
32✔
329
      if (this.config.enterpriseID) {
8✔
330
        id = this.config.enterpriseID;
4✔
331
      } else {
332
        throw new Error('Enterprise ID must be passed');
4✔
333
      }
334
    }
335

336
    const appAuthSession = new AppAuthSession(
28✔
337
      type,
338
      id,
339
      this.config,
340
      this.tokenManager,
341
      tokenStore
342
    );
343
    return new BoxClient(appAuthSession, this.config, this.requestManager);
28✔
344
  }
345

346
  /**
347
   * Generate the URL for the authorize page to send users to for the first leg of
348
   * the OAuth2 flow.
349
   *
350
   * @param {Object} params The OAuth2 parameters
351
   * @returns {string} The authorize page URL
352
   */
353
  getAuthorizeURL(params: { client_id?: string }) {
1,120✔
354
    params.client_id = this.config.clientID;
4✔
355

356
    return `${this.config.authorizeRootURL}/oauth2/authorize?${qs.stringify(
4✔
357
      params
358
    )}`;
359
  }
360

361
  /**
362
   * Acquires token info using an authorization code
363
   *
364
   * @param {string} authorizationCode - authorization code issued by Box
365
   * @param {TokenRequestOptions} [options] - Sets optional behavior for the token grant, null for default behavior
366
   * @param {Function} [callback] - passed a TokenInfo object if tokens were granted successfully
367
   * @returns {Promise<TokenInfo>} Promise resolving to the token info
368
   */
369
  getTokensAuthorizationCodeGrant(
1,120✔
370
    authorizationCode: string,
371
    options?: TokenRequestOptions | null,
372
    callback?: Function
373
  ) {
374
    return this.tokenManager
4✔
375
      .getTokensAuthorizationCodeGrant(
376
        authorizationCode,
377
        options as any /* FIXME */
378
      )
379
      .asCallback(callback);
380
  }
381

382
  /**
383
   * Refreshes the access and refresh tokens for a given refresh token.
384
   *
385
   * @param {string} refreshToken - A valid OAuth refresh token
386
   * @param {TokenRequestOptions} [options] - Sets optional behavior for the token grant, null for default behavior
387
   * @param {Function} [callback] - passed a TokenInfo object if tokens were granted successfully
388
   * @returns {Promise<TokenInfo>} Promise resolving to the token info
389
   */
390
  getTokensRefreshGrant(
1,120✔
391
    refreshToken: string,
392
    options?: TokenRequestOptions | Function | null,
393
    callback?: Function
394
  ) {
395
    if (typeof options === 'function') {
12✔
396
      callback = options;
4✔
397
      options = null;
4✔
398
    }
399

400
    return this.tokenManager
12✔
401
      .getTokensRefreshGrant(refreshToken, options as any /* FIXME */)
402
      .asCallback(callback);
403
  }
404

405
  /**
406
   * Gets tokens for enterprise administration of app users
407
   * @param {string} enterpriseID The ID of the enterprise to generate a token for
408
   * @param {TokenRequestOptions} [options] - Sets optional behavior for the token grant, null for default behavior
409
   * @param {Function} [callback] Passed the tokens if successful
410
   * @returns {Promise<TokenInfo>} Promise resolving to the token info
411
   */
412
  getEnterpriseAppAuthTokens(
1,120✔
413
    enterpriseID: string,
414
    options?: TokenRequestOptions | Function | null,
415
    callback?: Function
416
  ) {
417
    if (typeof options === 'function') {
20✔
418
      callback = options;
12✔
419
      options = null;
12✔
420
    }
421

422
    if (!enterpriseID) {
20✔
423
      if (this.config.enterpriseID) {
4!
NEW
424
        enterpriseID = this.config.enterpriseID;
×
425
      } else {
426
        throw new Error('Enterprise id must be passed');
4✔
427
      }
428
    }
429

430
    return this.tokenManager
16✔
431
      .getTokensJWTGrant('enterprise', enterpriseID, options as any /* FIXME */)
432
      .asCallback(callback);
433
  }
434

435
  /**
436
   * Gets tokens for App Users via a JWT grant
437
   * @param {string} userID The ID of the App User to generate a token for
438
   * @param {TokenRequestOptions} [options] - Sets optional behavior for the token grant, null for default behavior
439
   * @param {Function} [callback] Passed the tokens if successful
440
   * @returns {Promise<TokentInfo>} Promise resolving to the token info
441
   */
442
  getAppUserTokens(
1,120✔
443
    userID: string,
444
    options?: TokenRequestOptions | Function | null,
445
    callback?: Function
446
  ) {
447
    if (typeof options === 'function') {
12✔
448
      callback = options;
4✔
449
      options = null;
4✔
450
    }
451

452
    return this.tokenManager
12✔
453
      .getTokensJWTGrant('user', userID, options as any /* FIXME */)
454
      .asCallback(callback);
455
  }
456

457
  /**
458
   * Revokes a token pair associated with a given access or refresh token.
459
   *
460
   * @param {string} token - A valid access or refresh token to revoke
461
   * @param {TokenRequestOptions} [options] - Sets optional behavior for the token grant, null for default behavior
462
   * @param {Function} [callback] - If err, revoke failed. Otherwise, revoke succeeded.
463
   * @returns {Promise<TokenInfo>} Promise resolving to the token info
464
   */
465
  revokeTokens(
1,120✔
466
    token: string,
467
    options?: TokenRequestOptions | Function | null,
468
    callback?: Function
469
  ) {
470
    if (typeof options === 'function') {
12✔
471
      callback = options;
4✔
472
      options = null;
4✔
473
    }
474

475
    return this.tokenManager
12✔
476
      .revokeTokens(token, options as any /* FIXME */)
477
      .asCallback(callback);
478
  }
479
}
1,120✔
480

481
/**
482
 * Expose the BoxClient property enumerations to the SDK as a whole. This allows
483
 * the consumer to access and use these values from anywhere in their application
484
 * (like a helper) by requiring the SDK, instead of needing to pass the client.
485
 */
486
BoxSDKNode.prototype.accessLevels = BoxClient.prototype.accessLevels;
1,120✔
487
BoxSDKNode.prototype.collaborationRoles =
1,120✔
488
  BoxClient.prototype.collaborationRoles;
489
BoxSDKNode.prototype.CURRENT_USER_ID = BoxClient.prototype.CURRENT_USER_ID;
1,120✔
490

491
/** @module box-node-sdk/lib/box-node-sdk */
492
export = BoxSDKNode;
1,120✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc