APIを実行するBOXユーザアカウントについて
管理対象ユーザ(通常のBoxユーザ)とBox Platformユーザがあります。
管理対象ユーザを特定の個人と関連づけられないユースケースで使用することは禁止されています。APIを使用してバッチ処理を行う場合などは必ずBox Platformユーザを使用します。
カスタムアプリの作成
Box Platform APIを使用するにはBoxでマイアプリを作成する必要があります。
- Boxの開発者コンソールにアクセスする。
- 初回はこちらの「開発者コンソールに移動」からアクセスする。一度アクセスすると、Box UIの左下に開発者コンソールのリンクが表示される。
-
マイアプリ > アプリ
の新規作成をクリックする。 - カスタムアプリをクリックし、アプリ名と目的を入力する(後で変更可能)。認証方法は「サーバー認証(JWT使用)」を選択する。
- 構成タブの設定
- 「アプリアクセスレベル」を「アプリ+Enterpriseアクセス」に変更する。
- アプリケーションスコープで必要な権限にチェックをつける。
- 管理対象ユーザとしてAPIを実行する場合は高度な機能の「as-userヘッダーを使用してAPI呼び出しを行う」にチェックをつける。
- Box UI Elementsなどで、管理対象ユーザのアクセストークンが必要な場合は、高度な機能の「ユーザーアクセストークンを生成する」にチェックをつける。
- クライアントサイドからAPIを実行する場合はCORSドメインを登録する。
- 構成タブの公開キーの追加と管理から、「公開/秘密キーペアを生成」をクリックし、config.jsonを作成しておく。
- 承認タブ「確認して送信」をクリックし、管理コンソールの
アプリ > カスタムアプリマネージャ
でアプリケーションの承認を行う。
Lambda
Box Node.js SDKがあるため、簡単に実装できます。
% npm install --save box-node-sdk
まず上記の「カスタムアプリの作成」で作成したアプリの認証情報を使用してSDKを初期化します。次にBox APIクライアントを作成します。
下記はconfig.jsonを使った認証の例になります。公式のドキュメントには他の方法の認証方法も記載されています。
Box Node.js SDKがサポートしている機能はこちら。
シークレットマネージャーについてはこちらで記事にしています。
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import axios from 'axios';
import BoxSDKNode = require('box-node-sdk');
const AWS_SESSION_TOKEN = process.env['AWS_SESSION_TOKEN'] || '';
const PARAMETERS_SECRETS_EXTENSION_HTTP_PORT = process.env['PARAMETERS_SECRETS_EXTENSION_HTTP_PORT'] || '';
export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
const boxConfig = await getBoxConfig();
const sdk = BoxSDKNode.getPreconfiguredInstance(boxConfig);
const client = sdk.getAppAuthClient('enterprise');
// 管理対象ユーザまたはAPPユーザとしてAPIを実行する場合は下記
// const client = sdk.getAppAuthClient('user', userId);
// APIの実行
const userInfo = await client.users.get(client.CURRENT_USER_ID);
return {
statusCode: 200,
body: JSON.stringify(userInfo),
};
}
async function getBoxConfig(): Promise<object> {
const smUri = `http://localhost:${PARAMETERS_SECRETS_EXTENSION_HTTP_PORT}/secretsmanager/get`;
const res = await axios.get(smUri, {
params: { secretId: '/box/login' },
headers: { 'X-Aws-Parameters-Secrets-Token': AWS_SESSION_TOKEN },
});
return JSON.parse(res.data.SecretString);
}
AWS CDK
デプロイ後、シークレットマネージャーにBoxの開発者コンソールでダウンロードしたconfig.jsonの値を設定します。
import { Construct } from 'constructs';
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as lambdaNodeJs from 'aws-cdk-lib/aws-lambda-nodejs';
import * as sm from 'aws-cdk-lib/aws-secretsmanager';
export class SampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Secrets Manager
const secret = this.createBoxSecret();
// Lambda
const lambda = this.createFunc();
secret.grantRead(lambda);
}
/**
* @description BOX認証情報を格納するシークレットを作成
* {@link class Secret | https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_secretsmanager.Secret.html}
*/
private createBoxSecret(): sm.ISecret {
return new sm.Secret(this, 'Secret', {
secretName: '/box/login',
description: 'boxからダウンロードしたconfig.jsonの内容を貼り付けてください。',
secretStringValue: cdk.SecretValue.unsafePlainText('dummy'),
});
}
/**
* @description Lambdaの作成
* {@link ParamsAndSecretsLayerVersion | https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda.ParamsAndSecretsLayerVersion.html}
* {@link NodejsFunction | https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.NodejsFunction.html}
*/
private createFunc(): lambdaNodeJs.NodejsFunction {
// AWS Parameters and Secrets Lambda Extension の作成
const paramsAndSecrets = lambda.ParamsAndSecretsLayerVersion.fromVersion(
lambda.ParamsAndSecretsVersions.V1_0_103,
{
cacheSize: 500,
logLevel: lambda.ParamsAndSecretsLogLevel.DEBUG,
}
);
return new lambdaNodeJs.NodejsFunction(this, 'SampleFunc', {
entry: 'src/lambda/sample/index.ts',
handler: 'handler',
runtime: lambda.Runtime.NODEJS_18_X,
timeout: cdk.Duration.minutes(5),
paramsAndSecrets,
});
}
}
テスト
test/sample.test.ts
import * as cdk from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { SampleStack } from '../lib/sample-stack';
test('SM', () => {
const app = new cdk.App();
const stack = new SampleStack(app, 'SampleStack');
const template = Template.fromStack(stack);
// Secrets Manager
template.resourceCountIs('AWS::SecretsManager::Secret', 1);
template.hasResourceProperties('AWS::SecretsManager::Secret', {
Description: 'boxからダウンロードしたconfig.jsonの内容を貼り付けてください。',
Name: '/box/login',
SecretString: 'dummy',
});
});
test('Lambda', () => {
const app = new cdk.App();
const stack = new SampleStack(app, 'SampleStack');
const template = Template.fromStack(stack);
// Lambda
template.resourceCountIs('AWS::Lambda::Function', 1);
template.hasResourceProperties('AWS::Lambda::Function', {
Runtime: 'nodejs18.x',
Timeout: 300,
});
});
エラー
error TS1259: Module '"sample/node_modules/box-node-sdk/lib/managers/collaborations"' can only be default-imported using the 'esModuleInterop' flag
tsconfig.jsonでesModuleInteropをtrueにしないとトランスパイルに失敗します。tsconfig.jsonを下記のように修正することで解消します。
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["es2020", "dom"],
"declaration": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": false,
"inlineSourceMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"strictPropertyInitialization": false,
"typeRoots": ["./node_modules/@types"],
+ "esModuleInterop": true
},
"exclude": ["node_modules", "cdk.out", "test"]
}
Configuration does not include boxAppSettings object.
シークレットマネージャーに設定している値の形式が間違っています。下記のような形式である必要があります。
{
"boxAppSettings": {
"clientID": "aaaaaaaa...",
"clientSecret": "bbbbbbbb...",
"appAuth": {
"publicKeyID": "cccccccc",
"privateKey": "-----BEGIN ENCRYPTED PRIVATE KEY-----\n...\n-----END ENCRYPTED PRIVATE KEY-----\n",
"passphrase": "eeee..."
}
},
"enterpriseID": "1234567890"
}