0
0

More than 1 year has passed since last update.

Lambda(Node.js)でBox Platform APIを使用する

Last updated at Posted at 2023-08-13

APIを実行するBOXユーザアカウントについて

管理対象ユーザ(通常のBoxユーザ)とBox Platformユーザがあります。
管理対象ユーザを特定の個人と関連づけられないユースケースで使用することは禁止されています。APIを使用してバッチ処理を行う場合などは必ずBox Platformユーザを使用します。

カスタムアプリの作成

Box Platform APIを使用するにはBoxでマイアプリを作成する必要があります。

  1. Boxの開発者コンソールにアクセスする。
    • 初回はこちらの「開発者コンソールに移動」からアクセスする。一度アクセスすると、Box UIの左下に開発者コンソールのリンクが表示される。
  2. マイアプリ > アプリの新規作成をクリックする。
  3. カスタムアプリをクリックし、アプリ名と目的を入力する(後で変更可能)。認証方法は「サーバー認証(JWT使用)」を選択する。
  4. 構成タブの設定
    • 「アプリアクセスレベル」を「アプリ+Enterpriseアクセス」に変更する。
    • アプリケーションスコープで必要な権限にチェックをつける。
    • 管理対象ユーザとしてAPIを実行する場合は高度な機能の「as-userヘッダーを使用してAPI呼び出しを行う」にチェックをつける。
    • Box UI Elementsなどで、管理対象ユーザのアクセストークンが必要な場合は、高度な機能の「ユーザーアクセストークンを生成する」にチェックをつける。
    • クライアントサイドからAPIを実行する場合はCORSドメインを登録する。
  5. 構成タブの公開キーの追加と管理から、「公開/秘密キーペアを生成」をクリックし、config.jsonを作成しておく。
  6. 承認タブ「確認して送信」をクリックし、管理コンソールのアプリ > カスタムアプリマネージャでアプリケーションの承認を行う。

Lambda

Box Node.js SDKがあるため、簡単に実装できます。

% npm install --save box-node-sdk

まず上記の「カスタムアプリの作成」で作成したアプリの認証情報を使用してSDKを初期化します。次にBox APIクライアントを作成します。
下記はconfig.jsonを使った認証の例になります。公式のドキュメントには他の方法の認証方法も記載されています。
Box Node.js SDKがサポートしている機能はこちら
シークレットマネージャーについてはこちらで記事にしています。

src/lambda/sample/index.ts
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の値を設定します。

lib/sample-stack.ts
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
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を下記のように修正することで解消します。

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"
}

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0