Server SDK (@auth-platform/server)#
@auth-platform/server は、Web BFF で Hosted Login の OIDC フローとサーバーセッションを扱うランタイム中立な SDK である。Web 標準の Request、Response、fetch、Web Crypto を使い、Node.js と Cloudflare Workers の両方で利用できる。
インストールと初期化#
pnpm add @auth-platform/server
web_bff の confidential client と、Management API に登録したコールバック URI を使う。クライアントシークレットはサーバーだけに置く。
import { createServerAuth } from "@auth-platform/server";
export const auth = createServerAuth({
issuer: process.env.AUTH_ISSUER!,
clientId: process.env.AUTH_CLIENT_ID!,
clientSecret: process.env.AUTH_CLIENT_SECRET!,
redirectUri: process.env.AUTH_REDIRECT_URI!,
cookieSecret: process.env.AUTH_COOKIE_SECRET!,
});
cookieSecret は32文字以上の秘密値とし、暗号学的に安全な乱数から一度生成して Secret Manager などで安定して保持する。同一の BFF を複数インスタンスで動かす場合は全インスタンスで同じ値を使う。値を変えると既存 cookie を復号できなくなり、利用者は再ログインする。
createServerAuth 設定#
| 設定 | 必須 | 既定値・制約 |
|---|---|---|
issuer |
はい | OIDC issuer の絶対 HTTPS URL。末尾の / は正規化される。 |
clientId |
はい | web_bff client ID。空文字列は不可。 |
clientSecret |
はい | confidential client secret。空文字列は不可。トークンエンドポイントでは client_secret_basic を使う。 |
redirectUri |
はい | 登録済みの絶対 URI と完全一致させる。HTTPS が必要で、HTTP は localhost と 127.0.0.1 に限り許可される。[::1] は例外に含まれない。fragment は不可。 |
cookieSecret |
はい | 32文字以上。AES-256-GCM の cookie seal key を導出する。 |
basePath |
いいえ | "/auth"。先頭 / が必要で、/ 以外の末尾 / は不可。ログイン・callback・logout のパスを組み立てる。 |
scopes |
いいえ | ["openid", "profile", "email", "offline_access"]。openid を含める。 |
postLoginRedirect |
いいえ | "/"。安全な相対パスまたは HTTPS URL。 |
postLogoutRedirect |
いいえ | "/"。安全な相対パスまたは HTTPS URL。 |
allowedRedirectTargets |
いいえ | []。return_to に許可する追加の相対パスまたは HTTPS URL。絶対 URL は文字列が完全一致するものだけ許可する。 |
allowedOrigins |
いいえ | []。CSRF の Origin/Referer 検査で追加許可する origin。絶対 URL の origin 部分だけを比較する。 |
sessionTtlSeconds |
いいえ | refresh-token idle TTL(30日)。正の整数。BFF セッション cookie の有効期間。 |
pkceCookieTtlSeconds |
いいえ | 600 秒。正の整数。PKCE handshake cookie の有効期間。 |
refreshLeewaySeconds |
いいえ | 60 秒。非負整数。access token の残り有効期間がこの値以下なら refresh する。 |
fetch |
いいえ | Web 標準 fetch を使う。テストや独自トランスポートを使う場合に実装を渡す。 |
cookieDomain |
— | サポートしない。値を設定すると初期化時に TypeError となる。__Host- cookie に Domain は設定できない。 |
設定値の不正は createServerAuth() の呼び出し時に TypeError になる。URI の登録規則はOIDC / OAuth 2.0を参照する。
ハンドラー#
createServerAuth() が返す ServerAuth の handler は、ルーターの Web Request を受け取り Promise<Response> を返す。basePath が "/auth" の場合のルートは次のとおりである。
ServerAuth property |
HTTP method | Path (basePath: "/auth") |
動作 |
|---|---|---|---|
loginHandler |
GET, POST |
/auth/login |
return_to を安全化し、state・nonce・PKCE verifier を含む一回限りの handshake cookie を設定して issuer へリダイレクトする。 |
callbackHandler |
GET |
/auth/callback |
code と state、PKCE cookie を検証し、authorization code を交換して iss / aud / exp / nonce を含む ID token claims を検証する。成功時は暗号化セッション cookie を設定し、検証済みの戻り先へリダイレクトする。callback の処理に入った後は、成功・失敗にかかわらず PKCE cookie を消去する。 |
logoutHandler |
GET, POST |
/auth/logout |
refresh token family の失効をベストエフォートで要求し、セッションと PKCE cookie を消去して return_to または postLogoutRedirect にリダイレクトする。 |
各 handler は未対応メソッドに 405、Origin 検証失敗に 403、OAuth や内部エラーに RFC 9457 形式の application/problem+json を返す。ログイン時・ログアウト時の return_to は安全な相対パスか、allowedRedirectTargets に完全一致する URL だけを受け入れる。それ以外は設定した fallback に戻る。
CSRF と redirect 対策#
GET、HEAD、OPTIONS は safe method として扱う。GET に Origin が付いている場合は、リクエスト自身の origin または allowedOrigins のいずれかに一致しなければならない。POST などの unsafe method は Origin を検査し、ない場合は Referer の origin を検査する。どちらもない場合や Origin: null は拒否する。cookie の SameSite=Lax に加え、ログイン callback は state と PKCE verifier も検証する。
相対 redirect は / から始まり // で始まらないパスだけを許可する。backslash、制御文字、長さ上限を超える値は受け付けない。絶対 URL は追加 allowlist と完全一致する必要がある。
Cookie とセッション#
SDK が設定する cookie は次の2つである。SESSION_COOKIE_NAME と PKCE_COOKIE_NAME も export される。セッション cookie 名は auth.sessionCookieName からも取得できる。
| Cookie | 用途 | 属性 |
|---|---|---|
__Host-auth-session |
暗号化された BFF セッション | Secure; HttpOnly; SameSite=Lax; Path=/ |
__Host-auth-pkce |
一時的な PKCE handshake | Secure; HttpOnly; SameSite=Lax; Path=/ |
Domain 属性は出力せず、cookieDomain も設定できない。セッション値は cookieSecret から HKDF-SHA-256 で導出した鍵による AES-256-GCM 暗号文で、access token・refresh token・ID token を平文 cookie 値として公開しない。改ざん、形式不正、別鍵で作られた cookie は無効として扱う。
getSession(request) の戻り値は次の判別 union である。
{ status: "authenticated", session, setCookie? }: 認証済み。setCookieは refresh またはローテーション後の cookie がある場合にだけ返る。{ status: "unauthenticated", reason, clearCookie? }: 未認証。破棄すべき cookie がある場合はclearCookieが返る。
公開される session は user、accessToken、accessTokenExpiresAt、scope、sessionExpiresAt を持つ。user は sub と任意の name、email、emailVerified を持つ。長期 credential の refreshToken と raw ID token は公開 session に含まれない。
getSession は access token の期限が refreshLeewaySeconds 以下なら refresh を試みる。refreshSession(request) は期限に関係なく refresh を強制する。同一 createServerAuth() インスタンス内の refresh grant は直列化され、直近のローテーション結果を再利用する。既定の scope には offline_access が含まれる。セッションに refresh token がない場合は access token の期限後に継続できず、session_expired となる。
reason |
意味 | cookie / retry |
|---|---|---|
no_session |
cookie がない | cookie 消去なし |
invalid_session_cookie |
cookie の seal/内容が無効 | clearCookie があれば適用する |
session_expired |
BFF セッションまたは refresh 不可能な access token が期限切れ | clearCookie があれば適用する |
refresh_rejected |
refresh grant が invalid_grant で拒否された |
cookie を破棄する |
refresh_unavailable |
refresh grant が invalid_grant 以外の理由で完了しなかった(通常は timeout・ネットワーク障害・一時的なサーバーエラー) |
cookie は保持し、後続リクエストで retry する。clearCookie は返らない。 |
requireSession(request, options?) は次の union を返す。
{ ok: true, session, setCookie? }: 保護対象へ続行する。setCookieがあれば outgoing response に追加する。{ ok: false, reason, response }: そのまま返せる認証失敗 response。
options.onUnauthenticated は "redirect"(既定値)または "json"。通常の未認証は、redirect なら安全な return_to 付きで ${basePath}/login に 302、json なら 401 problem JSON となる。refresh grant が invalid_grant 以外の理由で完了しない場合は 503 を返し、ログインへ redirect しない。cookie を更新・消去する場合は、setCookie / clearCookie を outgoing response の Set-Cookie に追加する。
Result とエラー#
verifyAccessToken() と createAccessTokenVerifier() は Promise<Result<VerifiedAccessToken, ServerAuthError>> を返す。成功は { type: "Success", value }、失敗は { type: "Failure", error } であり、ok boolean 形式ではない。Result.isSuccess()、Result.isFailure()、Result.map()、Result.andThen()、Result.orElse() などの helper を export する。通常の handler は Result ではなく HTTP Response を返し、HTTP problem response に内部 error type を code として含める。
ServerAuthError は判別可能な type を持つ。種類には upstream_timeout、upstream_unreachable、response_too_large、discovery_failed / discovery_invalid、jwks_fetch_failed / jwks_invalid、token_endpoint_rejected / token_endpoint_failed / token_response_invalid、id_token_invalid、access_token_invalid、revocation_failed、sealing_failed、seal_verification_failed、cookie_serialization_failed、crypto_operation_failed がある。isRetryableError(error) は error.retryable === true のときだけ true を返す。再試行ではこの helper に従い、invalid credential や検証エラーを盲目的に再試行しない。
Handler が ServerAuthError を HTTP response に変換するとき、retryable: true は 503、upstream_timeout / upstream_unreachable は 503、non-retryable な ID token / token response rejection は 400、無効 access token は 401、response_too_large を含む discovery/JWKS/token/revocation upstream 応答エラーは 502、sealing・cookie serialization・crypto failure は 500 となる。Response は application/problem+json と Cache-Control: no-store を使う。
import { isRetryableError, Result } from "@auth-platform/server";
const result = await auth.verifyAccessToken(bearerToken, "https://api.example.com");
if (Result.isFailure(result)) {
const retryable = isRetryableError(result.error);
// error.type と retryable に応じて、この API の error response を選ぶ。
} else {
const subject = result.value.sub;
// 検証済み subject を resource authorization に使う。
}
error detail に token、cookie 値、response body、利用者の個人情報は含まれない。Errors、rate limit、retry、冪等性も参照する。
API 用 access token verifier#
BFF セッションを使わない resource server は createAccessTokenVerifier() を使える。issuer は HTTPS issuer、audience は任意の期待 audience(文字列または配列)、fetch は任意の fetch override である。SDK は issuer discovery と JWKS を使い、ES256、必須 kid、issuer、期限、typ: at+jwt を検証する。audience を指定すると token audience も照合される。成功の VerifiedAccessToken は sub、scope、任意の clientId と expiresAt、全検証済み claims を持つ。
import { createAccessTokenVerifier, isRetryableError, Result } from "@auth-platform/server";
const verify = createAccessTokenVerifier({
issuer: process.env.AUTH_ISSUER!,
audience: "https://api.example.com",
});
const result = await verify(accessToken);
if (Result.isFailure(result)) {
const errorType = result.error.type;
const retryable = isRetryableError(result.error);
// errorType と retryable に応じて、この API の error response を選ぶ。
} else {
const subject = result.value.sub;
// result.value.scope とともに resource authorization に使う。
}
Hono で Authorization: Bearer を検証する場合は Hono SDK (@auth-platform/hono)、Next.js ルート接続は Next.js SDK (@auth-platform/next) を参照する。BFF client と end-to-end の初期設定は Quickstart: Web BFF、verifyWebhook() による Webhook 署名検証は Webhooks を参照する。