SSTの独自軽量Authを複数APIにアタッチする
SST独自実装の軽量Authコンストラクタは、実は複数のAPI Gatewayにアタッチできるよ。SSTのDiscordで紹介されていたけれど、公式ドキュメントではあまり言及されていない(実例なし)から、この記事で紹介するよ。
※画像引用:https://sst.dev/
SST(Serverless Stack。CDKをラップして便利機能を追加したサーバレスアプリ爆速開発ツール)には独自実装の軽量Authコンストラクトが用意されているんだ。このAuthコンストラクトは、一般的には次のようにAPIにアタッチして使用する。
import { StackContext, Api, Auth } from "sst/constructs";
export function MyAuth({ stack }: StackContext) {
const auth = new Auth(stack, "myAuth", {
// 認証処理を下記で指定した関数に実装(実装内容の紹介は割愛)
authenticator: "packages/functions/src/auth.handler",
});
const myApi = new Api(stack, "myApi", {
// ここにAPI Gatewayのルート定義等を記述(割愛)
});
// 認証処理をAPI Gatewayの/auth以下のルートにアタッチする
auth.attach(stack, {
api: myApi,
prefix: "/auth",
});
// エンドポイントの標準出力(確認用)
stack.addOutputs({
MyApiEndpoint: myApi.url,
});
}
こうすれば、アタッチしたAPI Gatewayの各ルートでトークンの検証を行いセッション情報を取得できるようになる。ただ、アプリケーションの規模が拡大して、複数のAPI Gatewayを運用するようになった時、認可処理が可能なAPI Gatewayがひとつしかなかったら、不便だよね。
そうならないように、SSTのAuthコンストラクタは、複数のAPI Gatewayにアタッチできるようになっているんだ。
実例を挙げるならこうだね。
// ---- Auth提供用スタック(AuthStack.ts) ----
import { StackContext, Auth } from "sst/constructs";
export function MyAuth({ stack }: StackContext) {
const auth = new Auth(stack, "myAuth", {
authenticator: "packages/functions/src/auth.handler",
});
return {
auth,
}
}
// ---- APIその1用スタック(ApiStack01.ts) ----
import { StackContext, Api, use } from "sst/constructs";
const { auth } = use(MyAuth);
export function MyApi01({ stack }: StackContext) {
const myApi01 = new Api(stack, "myApi01", {
// ここにAPI Gatewayのルート定義等を記述(割愛)
});
// 認証処理をAPI Gatewayの/auth以下のルートにアタッチする
auth.attach(stack, {
api: myApi01,
prefix: "/auth",
});
// エンドポイントの標準出力(確認用)
stack.addOutputs({
MyApi01Endpoint: myApi01.url,
});
}
// ---- APIその2用スタック(ApiStack02.ts) ----
import { StackContext, Api, use } from "sst/constructs";
const { auth } = use(MyAuth);
export function MyApi02({ stack }: StackContext) {
const myApi02 = new Api(stack, "myApi02", {
// ここにAPI Gatewayのルート定義等を記述(割愛)
});
// 認証処理をAPI Gatewayの/auth以下のルートにアタッチする
auth.attach(stack, {
api: myApi02,
prefix: "/auth",
});
// エンドポイントの標準出力(確認用)
stack.addOutputs({
MyApi02Endpoint: myApi02.url,
});
}
// ---- sst.config.ts ----
import { SSTConfig } from "sst";
import { MyAuth } from "./stacks/AuthStack";
import { MyApi01 } from "./stacks/ApiStack01";
import { MyApi02 } from "./stacks/ApiStack02";
export default {
config(_input) {
return {
name: "myapp",
region: "ap-northeast-1",
};
},
stacks(app) {
app
.stack(MyAuth)
.stack(MyApi01)
.stack(MyApi02);
// ※下記でもOK
// app.stack(VaesiteAuth);
// app.stack(VaesiteApi);
// app.stack(VaesiteSite);
},
} satisfies SSTConfig;
これで複数のAPI Gatewayに同一のセッションをグローバルに共有可能な認可処理をアタッチできた。
注意点
現在のSSTのバージョン(2.2.4)の時点では、なぜか下記のように 「同一スタック内で複数のAPI Gatewayを作成&Authにアタッチする」 と、npm run dev(sst dev)時にエラーが発生するケースがあるよ。
// ---- API2つ同時定義スタック ----
import { StackContext, Api, use } from "sst/constructs";
const { auth } = use(MyAuth);
export function MyApi({ stack }: StackContext) {
const myApi01 = new Api(stack, "myApi01", {});
auth.attach(stack, {
api: myApi01,
prefix: "/auth",
});
const myApi02 = new Api(stack, "myApi02", {});
auth.attach(stack, {
api: myApi02,
prefix: "/auth",
});
}
Error: There is already a Construct with name 'Lambda_ANY_--auth--{proxy+}' in EmptyStack [sig-vaesite-VaesiteApi]
at Node.addChild (/Users/sig/[project_name]/node_modules/constructs/src/construct.ts:403:13)
at null.Node (/Users/sig/[project_name]/node_modules/constructs/src/construct.ts:71:17)
at null.Construct (/Users/sig/[project_name]/node_modules/constructs/src/construct.ts:463:17)
at new Resource (/Users/sig/[project_name]/node_modules/aws-cdk-lib/core/lib/resource.js:1:622)
at new FunctionBase (/Users/sig/[project_name]/node_modules/aws-cdk-lib/aws-lambda/lib/function-base.js:1:614)
at new Function (/Users/sig/[project_name]/node_modules/aws-cdk-lib/aws-lambda/lib/function.js:1:1155)
at new Function (file:///Users/sig/[project_name]/node_modules/sst/constructs/Function.js:144:13)
at Function.fromDefinition (file:///Users/sig/[project_name]/node_modules/sst/constructs/Function.js:430:24)
at Api.createFunctionIntegration (file:///Users/sig/[project_name]/node_modules/sst/constructs/Api.js:644:27)
at file:///Users/sig/[project_name]/node_modules/sst/constructs/Api.js:479:26
スタックを分ければ発生しないけれど、もし同一スタックそのままでエラーを回避したいなら、現状では下記のようにAuth割り当てルート名を変えることで対応可能だったよ。
// ---- API2つ同時定義スタック ----
import { StackContext, Api, use } from "sst/constructs";
const { auth } = use(MyAuth);
export function MyApi({ stack }: StackContext) {
const myApi01 = new Api(stack, "myApi01", {});
auth.attach(stack, {
api: myApi01,
prefix: "/auth",
});
const myApi02 = new Api(stack, "myApi02", {});
auth.attach(stack, {
api: myApi02,
prefix: "/auth2", // 名前を変えた
});
}
レアケースかもしれないけれど、今後のためにメモメモ……