./knowledge-base/lambda/edge-auth/cf-function.js
/**
* CloudFront Function: /era-assist/* 認証ガード + パスリライト
*
* 処理フロー:
* 1. Cookie "okta_access_token" の存在・有効期限をチェック
* 2. 無効 → /?redirect_to=/era-assist/ へ 302 リダイレクト(React SPA でログイン)
* 3. 有効 → /era-assist/xxx を /xxx にリライトして App Runner へ転送
*
* NOTE: JWT 署名検証は行わない(Lambda@Edge が不要なライトウェイト実装)
* 署名の真正性は Okta が発行元であることと、exp クレームの確認のみで担保
*
* Runtime: cloudfront-js-2.0
*/
function handler(event) {
var request = event.request;
var uri = request.uri;
// ── 1. Cookie から okta_access_token を取得 ──────────────────────────────
var cookies = request.cookies || {};
var tokenCookie = cookies["okta_access_token"];
var token = tokenCookie ? tokenCookie.value : null;
// ── 2. JWT の exp クレームで有効期限チェック ────────────────────────────
var isValid = false;
if (token) {
try {
var parts = token.split(".");
if (parts.length === 3) {
// base64url → 標準 base64 変換(+/パディング追加)
var b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
var pad = b64.length % 4;
if (pad === 2) b64 += "==";
else if (pad === 3) b64 += "=";
var payload = JSON.parse(atob(b64));
var nowSec = Math.floor(Date.now() / 1000);
if (payload.exp && payload.exp > nowSec) {
isValid = true;
}
}
} catch (_) {
isValid = false;
}
}
// ── 3. 未認証 → React SPA ログイン画面へリダイレクト ──────────────────
if (!isValid) {
// redirect_to に元のパスを渡す(LoginCallbackPage がログイン後にここへ戻す)
var dest = "/?redirect_to=" + encodeURIComponent("/era-assist/");
return {
statusCode: 302,
statusDescription: "Found",
headers: {
location: { value: dest },
"cache-control": { value: "no-store, no-cache" },
},
};
}
// ── 4. 認証済み → /era-assist/xxx を /xxx にリライト ─────────────────
// /era-assist → /
// /era-assist/ → /
// /era-assist/foo → /foo
// /era-assist/foo/ → /foo/
if (uri.startsWith("/era-assist")) {
var rewritten = uri.slice(11); // "/era-assist" は 11 文字
request.uri = rewritten === "" ? "/" : rewritten;
}
return request;
}