認証システム仕様書
1. システム概要
1.1 目的
本システムは、蒼翔祭(学祭)関連サービス全体で使用される統合認証基盤である。
- スタッフ認証: Discord OAuth2によるログイン
- ゲスト認証: LINE OAuth2によるログイン
- JWT管理: トークン発行・検証・BAN管理
1.2 アーキテクチャ構成
1.3 JWT利用サービス一覧
スタッフJWT(soshosai_staff_jwt):
| ドメイン | サービス名 | 用途 |
|---|---|---|
room.soshosai.com | Sho-Room | 学祭委員管理画面 |
fwd.soshosai.com | trackable-links | リンク管理 |
docs.soshosai.com | ドキュメント | Docusaurusドキュメント共有 |
apf.soshosai.com | 申請システム | 各種申請管理 |
v3.api.soshosai.com | 共通API | バックエンドAPI |
ゲストJWT(soshosai_customer_jwt):
| ドメイン | サービス名 | 用途 |
|---|---|---|
app.soshosai.com | 来場者アプリ | 来場者向けサービス |
v3.api.soshosai.com | 共通API | バックエンドAPI |
1.4 BFFによるJWT選択とService Binding
各フロントエンドのPages Functions(BFF)が、サイトの役割に応じて適切なCookieのみを選択し、Service Binding経由でAPIに転送する。
設計原則:
- クライアント(ブラウザ)は直接APIを呼ばない
- 全API呼び出しはBFF経由
- BFFはService BindingでWorkerを直接呼び出し(HTTP経由ではない)
- APIは
Authorization: Bearer xxxヘッダーのみで認証(Cookieは使用しない)
JWT選択ルール:
| サイト | BFFが選択するCookie | 用途 |
|---|---|---|
room.soshosai.com | soshosai_staff_jwt | スタッフ専用 |
fwd.soshosai.com | soshosai_staff_jwt | スタッフ専用 |
docs.soshosai.com | soshosai_staff_jwt | スタッフ専用 |
apf.soshosai.com | soshosai_staff_jwt | スタッフ専用 |
app.soshosai.com | soshosai_customer_jwt | ゲスト専用 |
Service Binding設定(wrangler.jsonc):
{
"services": [
{ "binding": "AUTH_SERVICE", "service": "unified-auth-worker" },
{ "binding": "GATEWAY", "service": "system-gateway-worker" },
],
}
BFF実装パターン(Service Binding経由):
interface Env {
AUTH_SERVICE: Fetcher;
GATEWAY: Fetcher;
}
// room.soshosai.com/functions/api/[[path]].ts(スタッフサイト)
export const onRequest: PagesFunction<Env> = async (context) => {
const { request, env, params } = context;
const staffJwt = getCookie(request, 'soshosai_staff_jwt');
if (!staffJwt) {
return Response.redirect('/login');
}
// Service Binding経由でGateway Workerを呼び出し
return env.GATEWAY.fetch(`https://gateway/api/${params.path}`, {
method: request.method,
headers: {
Authorization: `Bearer ${staffJwt}`,
'Content-Type':
request.headers.get('Content-Type') || 'application/json',
},
body: request.body,
});
};
メリット:
- 低レイテンシ: ネットワークを経由しないWorker間直接通信
- シンプル: APIは
Authorizationヘッダーのみを確認 - セキュア: JWT混同問題が根本的に解決、内部通信なので外部からアクセス不可
- 明確: 各サイトの役割が明確
重要: APIは Cookieを一切確認しない。 BFFがService Binding経由でWorkerを直接呼び出し、適切なJWTのみを転送する。
2. コンポーネント詳細
2.1 login-api
ドメイン: loginapi.soshosai.com
役割: 認証フロントエンド。OAuthフローの開始・コールバック処理・Cookie設定を担当。
主要エンドポイント:
| Method | Path | 説明 |
|---|---|---|
| GET | /discord-login | Discord OAuth2認証開始 |
| GET | /auth/discord-callback | Discord OAuthコールバック |
| GET | /line-login | LINE OAuth2認証開始 |
| GET | /login-callback | LINE OAuthコールバック |
| GET | /api/me | 現在のユーザー情報取得 |
| GET | /api/token | JWT取得(Authorization header用) |
| POST | /api/logout | ログアウト |
Service Binding:
[[services]]
binding = "DISCORD_AUTH_SERVICE"
service = "discord-auth-worker"
[[services]]
binding = "LINE_AUTH_SERVICE"
service = "line-auth-worker"
[[services]]
binding = "UNIFIED_AUTH_SERVICE"
service = "unified-auth-worker"
2.2 discord-auth-worker
役割: Discord OAuth2認証・ロール検証・スタッフ権限計算
主要エンドポイント:
| Method | Path | 説明 |
|---|---|---|
| POST | /staff/authenticate-discord | スタッフ認証(Discord ID直接) |
| POST | /token/verify | JWTトークン検証・BANチェック |
| GET | /internal/discord/members/:discordUserId | Discordメンバー情報取得 |
| GET | /internal/discord/guild/roles | ギルドロール一覧取得 |
| GET | /authority/info/:discordId | 権限情報取得 |
| POST | /authority/refresh/:discordId | 権限更新 |
認証フロー:
- Discord User IDを受け取り
- BANチェック(BannedUsersテーブル)
- Discord APIでギルドメンバー情報取得
- 必須ロール(
REQUIRED_ROLE_ID)の確認 - ロールから権限レベル計算(admin/staff)
- Staffsテーブルにユーザー作成/更新
- JWT発行(3日間有効)
JWT Payload(スタッフ):
interface StaffJwtPayload {
sub: string; // Discord User ID
userType: 'staff';
/**
* permission bits(整数)。各ビットが個別の権限を表す。
* Discordロールから計算され、ORで合成される。
* 例: BASE_STAFF_PERMISSIONS = 5(TRACKABLE_LINKS_VIEW | TRACKABLE_LINKS_ANALYTICS)
*/
permissions?: number;
discordUser?: {
id: string;
username: string;
global_name?: string;
avatar?: string;
};
metadata?: {
authoritySource: 'discord_roles' | 'database' | 'fallback';
departments: string[];
lastUpdated: string;
};
exp: number;
iat: number;
}
権限ビット一覧:
| 定数名 | ビット値 | 説明 |
|---|---|---|
TRACKABLE_LINKS_VIEW | 1 | リンク・QRコード閲覧 |
TRACKABLE_LINKS_EDIT | 2 | リンク・QRコード作成・編集 |
TRACKABLE_LINKS_ANALYTICS | 4 | アクセス統計閲覧 |
TRACKABLE_LINKS_DELETE | 8 | QRコード(全件)・プロジェクト削除 |
STAFF_VIEW | 16 | スタッフ一覧閲覧 |
STAFF_MANAGE | 32 | スタッフ編集・BAN |
SYSTEM_MANAGE | 64 | システム設定・全体管理 |
EQUIPMENT_VIEW | 128 | 備品一覧・詳細閲覧 |
EQUIPMENT_EDIT | 256 | 備品登録・編集 |
EQUIPMENT_DELETE | 512 | 備品削除 |
EQUIPMENT_INTEGRATION | 1024 | 備品連携(import/export) |
EQUIPMENT_RESET | 2048 | 備品リセット |
EQUIPMENT_MANAGE_ORGANIZATION | 4096 | 団体管理 |
EQUIPMENT_MANAGE_LOCATION | 8192 | 場所管理 |
EQUIPMENT_MANAGE_TYPE | 16384 | 備品種別管理 |
EQUIPMENT_BULK_OPERATION | 32768 | 備品一括操作 |
FORM_VIEW | 65536 | フォーム一覧閲覧 |
FORM_SUBMISSION_APPROVE | 131072 | フォーム回答承認/却下 |
EVENT_VIEW | 262144 | イベント一覧閲覧 |
EVENT_EDIT | 524288 | イベント作成・編集・削除 |
複数の権限はビットORで合成される。ALL_PERMISSIONS(全ビットOR)を持つユーザーはすべての操作が可能。
BASE_STAFF_PERMISSIONS = TRACKABLE_LINKS_VIEW | TRACKABLE_LINKS_ANALYTICS = 5(全スタッフの最低権限)。
注:
TRACKABLE_LINKS_EDITを持つユーザーは自身が作成したQRコード(creator_id一致)のみ削除可。TRACKABLE_LINKS_DELETEを持つユーザーは全QRコード・全プロジェクトの削除が可能。
2.3 line-auth-worker
役割: LINE OAuth2認証・ゲスト登録
主要エンドポイント:
| Method | Path | 説明 |
|---|---|---|
| POST | /guest/authenticate | ゲスト認証(LINE ID直接、Service Binding用) |
| POST | /guest/login | ゲストログイン(認証コード経由) |
認証フロー:
- LINE User IDまたは認証コードを受け取り
- 認証コードの場合、LINE APIでアクセストークン取得
- LINEプロフィール取得
- BANチェック
- Guestsテーブルにユーザー作成(新規の場合はGroupも作成)
- JWT発行(2日間有効)
JWT Payload(ゲスト):
interface GuestJwtPayload {
sub: string; // Guest ID (guest-xxxxxxx)
userType: 'guest';
lineUserId: string;
groupId: string;
exp: number;
iat: number;
}
2.4 unified-auth-worker
役割: JWT検証・BAN管理・レート制限
主要エンドポイント:
| Method | Path | 説明 |
|---|---|---|
| POST | /token/verify | トークン検証 |
| POST | /admin/ban | ユーザーBAN |
| POST | /admin/ban-user-global | グローバルBAN |
| POST | /admin/ban-user-by-type | タイプ別BAN |
| POST | /admin/unban | BAN解除 |
| POST | /admin/unban-advanced | BAN ID指定解除 |
| GET | /admin/user-info/:discordUserId | ユーザー情報取得 |
| GET | /admin/bans | BAN一覧取得 |
| POST | /rate-limit/login-api/check | レートリミットチェック |
| POST | /rate-limit/login-api/record-violation | 違反記録 |
トークン検証フロー:
Authorization: Bearer xxxヘッダーからトークン取得- JWT署名検証(HMAC-SHA256)
- 有効期限チェック
- BANチェック(BannedUsersテーブル)
- 検証結果を返却
3. 認証フロー詳細
3.1 スタッフ認証フロー(Discord)
3.2 ゲスト認証フロー(LINE)
4. セキュリティ仕様
4.1 Cookie設定
CookieはDomain=.soshosai.comで設定され、全サブドメインで共有される。
スタッフJWT Cookie:
Set-Cookie: soshosai_staff_jwt=eyJhbGc...;
Domain=.soshosai.com; ← 全サブドメインで共有
Path=/;
HttpOnly;
Secure;
SameSite=Lax;
Max-Age=259200 (3日間)
ゲストJWT Cookie:
Set-Cookie: soshosai_customer_jwt=eyJhbGc...;
Domain=.soshosai.com; ← 全サブドメインで共有
Path=/;
HttpOnly;
Secure;
SameSite=Lax;
Max-Age=172800 (2日間)
Cookie共有の仕組み:
| 設定 | 共有範囲 |
|---|---|
Domain=.soshosai.com | 全サブドメイン(room, fwd, docs, apf, app等) |
Domain=room.soshosai.com | room.soshosai.comのみ |
注意: スタッフCookie(
soshosai_staff_jwt)とゲストCookie(soshosai_customer_jwt)は別名のため混同しない。各サービスは必要なCookieのみを使用する。
4.2 JWT仕様
- アルゴリズム: HS256 (HMAC-SHA256)
- シークレット: 環境変数
JWT_SECRET(Secrets Store経由) - 有効期限: スタッフ3日、ゲスト2日
4.3 レート制限
| 種別 | 制限 | 対象 |
|---|---|---|
| auth | 厳格 | /login, /auth, /callback |
| read | 緩和 | GET/HEAD/OPTIONS |
| write | 標準 | POST/PUT/PATCH/DELETE |
| api_me | 非常に緩和 | /api/me |
4.4 Service Binding
auth-workersは直接公開せず、Service Binding経由でのみアクセス可能。
login-api → discord-auth-worker (DISCORD_AUTH_SERVICE)
login-api → line-auth-worker (LINE_AUTH_SERVICE)
login-api → unified-auth-worker (UNIFIED_AUTH_SERVICE)
API → unified-auth-worker (JWT検証)
5. BAN管理
5.1 BANタイプ
| タイプ | 説明 |
|---|---|
staff | スタッフとしてのBAN |
guest | ゲストとしてのBAN |
global | 全サービスからのBAN |
5.2 BANテーブル
CREATE TABLE BannedUsers (
ban_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
user_type TEXT NOT NULL, -- 'staff', 'guest', 'global'
reason TEXT,
banned_by TEXT NOT NULL,
banned_at TEXT NOT NULL,
expires_at TEXT -- NULL = 永久BAN
);
5.3 BAN判定タイミング
- ログイン時: discord-auth-worker / line-auth-worker でチェック
- API呼び出し時: unified-auth-worker の
/token/verifyでチェック
6. 環境変数
6.1 login-api
| 変数名 | 説明 |
|---|---|
JWT_SECRET | JWT署名シークレット |
DISCORD_CLIENT_ID | Discord OAuth Client ID |
DISCORD_CLIENT_SECRET | Discord OAuth Client Secret |
CHANNEL_ID | LINE Channel ID |
CHANNEL_SECRET | LINE Channel Secret |
LOGIN_API_URL | 開発環境URL(設定時は開発モード) |
6.2 discord-auth-worker
| 変数名 | 説明 |
|---|---|
JWT_SECRET | JWT署名シークレット(Secrets Store) |
AUTH_DISCORD_TOKEN | Discord Bot Token(Secrets Store) |
REQUIRED_GUILD_ID | 必須ギルドID(Secrets Store) |
REQUIRED_ROLE_ID | スタッフ必須ロールID(Secrets Store) |
ADMIN_ROLE_ID | 管理者ロールID |
DB | D1 Database Binding |
6.3 line-auth-worker
| 変数名 | 説明 |
|---|---|
JWT_SECRET | JWT署名シークレット(Secrets Store) |
LINE_CLIENT_ID | LINE Channel ID |
LINE_CLIENT_SECRET | LINE Channel Secret |
LINE_REDIRECT_URI | コールバックURL |
DB | D1 Database Binding |
6.4 unified-auth-worker
| 変数名 | 説明 |
|---|---|
JWT_SECRET | JWT署名シークレット(Secrets Store) |
RATE_LIMITER | レートリミッターBinding |
DB | D1 Database Binding |
7. 開発・デプロイ
7.1 ローカル開発
# login-api
cd packages/api/login-api
wrangler dev
# auth-workers(個別)
cd packages/api/auth-workers/discord-auth-worker
wrangler dev
cd packages/api/auth-workers/line-auth-worker
wrangler dev
cd packages/api/auth-workers/unified-auth-worker
wrangler dev
7.2 デプロイ順序
Service Bindingの依存関係上、以下の順序でデプロイ:
- unified-auth-worker(依存なし)
- discord-auth-worker(依存なし)
- line-auth-worker(依存なし)
- login-api(上記3つに依存)
# 一括デプロイスクリプト例
cd packages/api/auth-workers/unified-auth-worker && wrangler deploy
cd packages/api/auth-workers/discord-auth-worker && wrangler deploy
cd packages/api/auth-workers/line-auth-worker && wrangler deploy
cd packages/api/login-api && wrangler deploy