box-node-sdk
Version:
Official SDK for Box Platform APIs
254 lines (253 loc) • 10.6 kB
text/typescript
import { PostOAuth2TokenGrantTypeField } from '../schemas/postOAuth2Token';
import { PostOAuth2TokenSubjectTokenTypeField } from '../schemas/postOAuth2Token';
import { AccessToken } from '../schemas/accessToken';
import { PostOAuth2TokenBoxSubjectTypeField } from '../schemas/postOAuth2Token';
import { Authentication } from '../networking/auth';
import { NetworkSession } from '../networking/network';
import { TokenStorage } from './tokenStorage';
import { InMemoryTokenStorage } from './tokenStorage';
import { AuthorizationManager } from '../managers/authorization';
import { BoxSdkError } from './errors';
import { PostOAuth2Token } from '../schemas/postOAuth2Token';
import { PostOAuth2Revoke } from '../schemas/postOAuth2Revoke';
export class CcgConfig {
/**
* Box API key used for identifying the application the user is authenticating with */
readonly clientId!: string;
/**
* Box API secret used for making auth requests. */
readonly clientSecret!: string;
/**
* The ID of the Box Developer Edition enterprise. */
readonly enterpriseId?: string;
/**
* The user id to authenticate. This value is not required. But if it is provided, then the user will be auto-authenticated at the time of the first API call. */
readonly userId?: string;
/**
* Object responsible for storing token. If no custom implementation provided,the token will be stored in memory. */
readonly tokenStorage: TokenStorage = new InMemoryTokenStorage({});
constructor(
fields: Omit<CcgConfig, 'tokenStorage'> &
Partial<Pick<CcgConfig, 'tokenStorage'>>,
) {
if (fields.clientId !== undefined) {
this.clientId = fields.clientId;
}
if (fields.clientSecret !== undefined) {
this.clientSecret = fields.clientSecret;
}
if (fields.enterpriseId !== undefined) {
this.enterpriseId = fields.enterpriseId;
}
if (fields.userId !== undefined) {
this.userId = fields.userId;
}
if (fields.tokenStorage !== undefined) {
this.tokenStorage = fields.tokenStorage;
}
}
}
export interface CcgConfigInput {
/**
* Box API key used for identifying the application the user is authenticating with */
readonly clientId: string;
/**
* Box API secret used for making auth requests. */
readonly clientSecret: string;
/**
* The ID of the Box Developer Edition enterprise. */
readonly enterpriseId?: string;
/**
* The user id to authenticate. This value is not required. But if it is provided, then the user will be auto-authenticated at the time of the first API call. */
readonly userId?: string;
/**
* Object responsible for storing token. If no custom implementation provided,the token will be stored in memory. */
readonly tokenStorage?: TokenStorage;
}
export class BoxCcgAuth implements Authentication {
/**
* Configuration object of Client Credentials Grant auth. */
readonly config!: CcgConfig;
/**
* An object responsible for storing token. If no custom implementation provided, the token will be stored in memory. */
readonly tokenStorage: TokenStorage;
/**
* The ID of the user or enterprise to authenticate as. If not provided, defaults to the enterprise ID if set, otherwise defaults to the user ID. */
readonly subjectId?: string;
/**
* The type of the subject ID provided. Must be either 'user' or 'enterprise'. */
readonly subjectType?: PostOAuth2TokenBoxSubjectTypeField;
constructor(
fields: Omit<
BoxCcgAuth,
| 'tokenStorage'
| 'subjectId'
| 'subjectType'
| 'refreshToken'
| 'retrieveToken'
| 'retrieveAuthorizationHeader'
| 'withUserSubject'
| 'withEnterpriseSubject'
| 'downscopeToken'
| 'revokeToken'
>,
) {
if (fields.config !== undefined) {
this.config = fields.config;
}
this.tokenStorage = this.config.tokenStorage;
this.subjectId = !(this.config.userId == void 0)
? this.config.userId
: this.config.enterpriseId;
this.subjectType = !(this.config.userId == void 0)
? ('user' as PostOAuth2TokenBoxSubjectTypeField)
: ('enterprise' as PostOAuth2TokenBoxSubjectTypeField);
}
/**
* Get a new access token using CCG auth
* @param {NetworkSession} networkSession An object to keep network session state
* @returns {Promise<AccessToken>}
*/
async refreshToken(networkSession?: NetworkSession): Promise<AccessToken> {
const authManager: AuthorizationManager = new AuthorizationManager({
networkSession: !(networkSession == void 0)
? networkSession!
: new NetworkSession({}),
});
const token: AccessToken = await authManager.requestAccessToken({
grantType: 'client_credentials' as PostOAuth2TokenGrantTypeField,
clientId: this.config.clientId,
clientSecret: this.config.clientSecret,
boxSubjectType: this.subjectType,
boxSubjectId: this.subjectId,
} satisfies PostOAuth2Token);
await this.tokenStorage.store(token);
return token;
}
/**
* Return a current token or get a new one when not available.
* @param {NetworkSession} networkSession An object to keep network session state
* @returns {Promise<AccessToken>}
*/
async retrieveToken(networkSession?: NetworkSession): Promise<AccessToken> {
const oldToken: undefined | AccessToken = await this.tokenStorage.get();
if (oldToken == void 0) {
const newToken: AccessToken = await this.refreshToken(networkSession);
return newToken;
}
return oldToken!;
}
/**
* @param {NetworkSession} networkSession
* @returns {Promise<string>}
*/
async retrieveAuthorizationHeader(
networkSession?: NetworkSession,
): Promise<string> {
const token: AccessToken = await this.retrieveToken(networkSession);
return ''.concat('Bearer ', token.accessToken!) as string;
}
/**
* Create a new BoxCCGAuth instance that uses the provided user ID as the subject ID.
* May be one of this application's created App User. Depending on the configured User Access Level, may also be any other App User or Managed User in the enterprise.
* <https://developer.box.com/en/guides/applications/>
* <https://developer.box.com/en/guides/authentication/select/>
* @param {string} userId The id of the user to authenticate
* @param {TokenStorage} tokenStorage Object responsible for storing token in newly created BoxCCGAuth. If no custom implementation provided, the token will be stored in memory.
* @returns {BoxCcgAuth}
*/
withUserSubject(
userId: string,
tokenStorage: TokenStorage = new InMemoryTokenStorage({}),
): BoxCcgAuth {
const newConfig: CcgConfig = new CcgConfig({
clientId: this.config.clientId,
clientSecret: this.config.clientSecret,
enterpriseId: this.config.enterpriseId,
userId: userId,
tokenStorage: tokenStorage,
});
return new BoxCcgAuth({ config: newConfig });
}
/**
* Create a new BoxCCGAuth instance that uses the provided enterprise ID as the subject ID.
* @param {string} enterpriseId The id of the enterprise to authenticate
* @param {TokenStorage} tokenStorage Object responsible for storing token in newly created BoxCCGAuth. If no custom implementation provided, the token will be stored in memory.
* @returns {BoxCcgAuth}
*/
withEnterpriseSubject(
enterpriseId: string,
tokenStorage: TokenStorage = new InMemoryTokenStorage({}),
): BoxCcgAuth {
const newConfig: CcgConfig = new CcgConfig({
clientId: this.config.clientId,
clientSecret: this.config.clientSecret,
enterpriseId: enterpriseId,
userId: void 0,
tokenStorage: tokenStorage,
});
return new BoxCcgAuth({ config: newConfig });
}
/**
* Downscope access token to the provided scopes. Returning a new access token with the provided scopes, with the original access token unchanged.
* @param {readonly string[]} scopes The scope(s) to apply to the resulting token.
* @param {string} resource The file or folder to get a downscoped token for. If None and shared_link None, the resulting token will not be scoped down to just a single item. The resource should be a full URL to an item, e.g. https://api.box.com/2.0/files/123456.
* @param {string} sharedLink The shared link to get a downscoped token for. If None and item None, the resulting token will not be scoped down to just a single item.
* @param {NetworkSession} networkSession An object to keep network session state
* @returns {Promise<AccessToken>}
*/
async downscopeToken(
scopes: readonly string[],
resource?: string,
sharedLink?: string,
networkSession?: NetworkSession,
): Promise<AccessToken> {
const token: undefined | AccessToken =
await this.retrieveToken(networkSession);
if (token == void 0) {
throw new BoxSdkError({
message:
'No access token is available. Make an API call to retrieve a token before calling this method.',
});
}
const authManager: AuthorizationManager = new AuthorizationManager({
networkSession: !(networkSession == void 0)
? networkSession!
: new NetworkSession({}),
});
const downscopedToken: AccessToken = await authManager.requestAccessToken({
grantType:
'urn:ietf:params:oauth:grant-type:token-exchange' as PostOAuth2TokenGrantTypeField,
subjectToken: token!.accessToken,
subjectTokenType:
'urn:ietf:params:oauth:token-type:access_token' as PostOAuth2TokenSubjectTokenTypeField,
scope: scopes.join(' ') as string,
resource: resource,
boxSharedLink: sharedLink,
} satisfies PostOAuth2Token);
return downscopedToken;
}
/**
* Revoke the current access token and remove it from token storage.
* @param {NetworkSession} networkSession An object to keep network session state
* @returns {Promise<undefined>}
*/
async revokeToken(networkSession?: NetworkSession): Promise<undefined> {
const oldToken: undefined | AccessToken = await this.tokenStorage.get();
if (oldToken == void 0) {
return void 0;
}
const authManager: AuthorizationManager = new AuthorizationManager({
networkSession: !(networkSession == void 0)
? networkSession!
: new NetworkSession({}),
});
await authManager.revokeAccessToken({
clientId: this.config.clientId,
clientSecret: this.config.clientSecret,
token: oldToken!.accessToken,
} satisfies PostOAuth2Revoke);
await this.tokenStorage.clear();
return void 0;
}
}