メインコンテンツまでスキップ

セッション認証

@zeltjs/auth-sessionはサーバーレンダリングアプリケーション向けのCookieベースのセッション管理を提供します。

インストール

pnpm add @zeltjs/auth-session @zeltjs/kv

クイックスタート

1. シークレットを設定

SESSION_SECRET環境変数を設定:

# .env
SESSION_SECRET=your-secret-key-at-least-32-characters

2. セッションストアを設定

セッションデータ用のKVストアを提供するカスタム設定を作成:

import { Config, inject } from '@zeltjs/core';
import { MemoryKVService } from '@zeltjs/kv';
import { SessionConfig } from '@zeltjs/auth-session';

@Config
class MySessionConfig extends SessionConfig {
private kv = inject(MemoryKVService);

override get store() {
return this.kv.namespace('sessions');
}
}

3. ミドルウェアを登録

import { createApp } from '@zeltjs/core';
import { MemoryKVService } from '@zeltjs/kv';
import { SessionMiddleware } from '@zeltjs/auth-session';

const app = createApp({
http: {
controllers: [AuthController, UserController],
middlewares: [SessionMiddleware],
},
configs: [MySessionConfig],
injectables: [MemoryKVService],
});

4. セッションを管理

ハンドラーでセッション関数を使用:

import { Controller, Post, Get, bodyParam } from '@zeltjs/core';
import { getSession, setSession, destroySession } from '@zeltjs/auth-session';

@Controller('/auth')
class AuthController {
@Post('/login')
async login(body = bodyParam(LoginSchema)) {
const user = await validateCredentials(body.email, body.password);
if (!user) {
throw new HTTPException(401, { message: '認証情報が無効です' });
}

setSession({ userId: user.id, name: user.name });
return { success: true };
}

@Get('/me')
me() {
const session = getSession();
if (!session) {
throw new HTTPException(401, { message: 'ログインしていません' });
}
return session;
}

@Post('/logout')
logout() {
destroySession();
return { success: true };
}
}

セッションAPI

関数説明
getSession()現在のセッションデータを取得(未ログインの場合はundefined
setSession(data)セッションデータを設定(既存を置換)
updateSession(fn)関数でセッションデータを更新
destroySession()セッションを破棄しCookieをクリア
isNewSession()新規作成されたセッションかチェック
getSessionId()現在のセッションIDを取得

setSession

セッションを作成または置換:

setSession({
userId: '123',
name: 'Alice',
cart: [{ productId: 'abc', qty: 2 }],
});

updateSession

セッションを部分的に更新:

updateSession((session) => ({
...session,
lastActivity: Date.now(),
}));

destroySession

セッションとCookieをクリア(ログアウト用):

destroySession();

型安全なセッション

型安全なセッションアクセスのためにSessionSchemaを拡張:

declare module '@zeltjs/auth-session' {
interface SessionSchema {
userId?: string;
name?: string;
email?: string;
cart?: CartItem[];
}
}

これですべてのセッション関数が型付けされます:

const session = getSession();
// TypeScriptが認識: session?.userId, session?.name, session?.cart

setSession({ userId: '123', name: 'Alice' });
// SessionSchemaに対して型チェック

設定

SessionConfigを継承して動作をカスタマイズ:

import { Config, inject } from '@zeltjs/core';
import { RedisKVService } from '@zeltjs/kv-redis';
import { SessionConfig } from '@zeltjs/auth-session';

@Config
class MySessionConfig extends SessionConfig {
private kv = inject(RedisKVService);

override get store() {
return this.kv.namespace('sessions');
}

override get cookieName(): string {
return 'my_session'; // デフォルト: 'session'
}

override get ttlSec(): number {
return 86400 * 7; // 7日間(デフォルト: 1日)
}

override get cookieOptions() {
return {
httpOnly: true,
secure: true,
sameSite: 'Strict' as const,
path: '/',
};
}
}

設定オプション

オプションデフォルト説明
storeKVNamespace必須セッションストレージ用KV名前空間
secretstringprocess.env.SESSION_SECRETセッションID署名用シークレット
cookieNamestring'session'Cookie名
ttlSecnumber86400(1日)セッションTTL(秒)
cookieOptionsobject下記参照Cookie設定

デフォルトCookieオプション

{
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'Lax',
path: '/',
}

ストレージバックエンド

メモリ(開発用)

import { MemoryKVService } from '@zeltjs/kv';

@Config
class MySessionConfig extends SessionConfig {
private kv = inject(MemoryKVService);
override get store() {
return this.kv.namespace('sessions');
}
}

Redis(本番用)

import { RedisKVService } from '@zeltjs/kv-redis';

@Config
class MySessionConfig extends SessionConfig {
private kv = inject(RedisKVService);
override get store() {
return this.kv.namespace('sessions');
}
}

Cloudflare KV

import { CloudflareKVService } from '@zeltjs/kv-cloudflare';

@Config
class MySessionConfig extends SessionConfig {
private kv = inject(CloudflareKVService);
override get store() {
return this.kv.namespace('sessions');
}
}

ユーザーコンテキストとの連携

セッションは自動的にユーザーコンテキストを設定しません。ブリッジするミドルウェアを追加:

import type { FunctionMiddleware } from '@zeltjs/core';
import { setUser } from '@zeltjs/core';
import { getSession } from '@zeltjs/auth-session';

export const sessionAuthMiddleware: FunctionMiddleware = async (c, next) => {
const session = getSession();

if (session?.userId) {
const user = await db.users.findById(session.userId);
setUser(
{ id: user.id, name: user.name, email: user.email },
user.roles
);
}

await next();
};

SessionMiddlewareの後に登録:

const app = createApp({
http: {
controllers: [UserController],
middlewares: [SessionMiddleware, sessionAuthMiddleware],
},
configs: [MySessionConfig],
injectables: [MemoryKVService],
});

セキュリティ考慮事項

CSRF保護

セッションベース認証にはCSRF保護が必要です。以下を検討:

  • SameSite=Strict Cookie(最強、UXに影響する場合あり)
  • SameSite=Lax Cookieとミューテーション用CSRFトークン
  • ダブルサブミットCookieパターン

セッション固定攻撃

ログイン後は常にセッションIDを再生成:

@Post('/login')
async login(body = bodyParam(LoginSchema)) {
const user = await validateCredentials(body.email, body.password);

destroySession(); // 古いセッションをクリア
setSession({ userId: user.id, name: user.name }); // 新しいIDを作成

return { success: true };
}

セキュアCookie

本番環境では常にセキュアCookieを使用:

override get cookieOptions() {
return {
httpOnly: true,
secure: true, // HTTPSのみ
sameSite: 'Strict' as const,
path: '/',
};
}