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

認証システム仕様書

1. システム概要

1.1 目的

本システムは、蒼翔祭(学祭)関連サービス全体で使用される統合認証基盤である。

  • スタッフ認証: Discord OAuth2によるログイン
  • ゲスト認証: LINE OAuth2によるログイン
  • JWT管理: トークン発行・検証・BAN管理

1.2 アーキテクチャ構成

1.3 JWT利用サービス一覧

スタッフJWT(soshosai_staff_jwt:

ドメインサービス名用途
room.soshosai.comSho-Room学祭委員管理画面
fwd.soshosai.comtrackable-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.comsoshosai_staff_jwtスタッフ専用
fwd.soshosai.comsoshosai_staff_jwtスタッフ専用
docs.soshosai.comsoshosai_staff_jwtスタッフ専用
apf.soshosai.comsoshosai_staff_jwtスタッフ専用
app.soshosai.comsoshosai_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設定を担当。

主要エンドポイント:

MethodPath説明
GET/discord-loginDiscord OAuth2認証開始
GET/auth/discord-callbackDiscord OAuthコールバック
GET/line-loginLINE OAuth2認証開始
GET/login-callbackLINE OAuthコールバック
GET/api/me現在のユーザー情報取得
GET/api/tokenJWT取得(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認証・ロール検証・スタッフ権限計算

主要エンドポイント:

MethodPath説明
POST/staff/authenticate-discordスタッフ認証(Discord ID直接)
POST/token/verifyJWTトークン検証・BANチェック
GET/internal/discord/members/:discordUserIdDiscordメンバー情報取得
GET/internal/discord/guild/rolesギルドロール一覧取得
GET/authority/info/:discordId権限情報取得
POST/authority/refresh/:discordId権限更新

認証フロー:

  1. Discord User IDを受け取り
  2. BANチェック(BannedUsersテーブル)
  3. Discord APIでギルドメンバー情報取得
  4. 必須ロール(REQUIRED_ROLE_ID)の確認
  5. ロールから権限レベル計算(admin/staff)
  6. Staffsテーブルにユーザー作成/更新
  7. 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_VIEW1リンク・QRコード閲覧
TRACKABLE_LINKS_EDIT2リンク・QRコード作成・編集
TRACKABLE_LINKS_ANALYTICS4アクセス統計閲覧
TRACKABLE_LINKS_DELETE8QRコード(全件)・プロジェクト削除
STAFF_VIEW16スタッフ一覧閲覧
STAFF_MANAGE32スタッフ編集・BAN
SYSTEM_MANAGE64システム設定・全体管理
EQUIPMENT_VIEW128備品一覧・詳細閲覧
EQUIPMENT_EDIT256備品登録・編集
EQUIPMENT_DELETE512備品削除
EQUIPMENT_INTEGRATION1024備品連携(import/export)
EQUIPMENT_RESET2048備品リセット
EQUIPMENT_MANAGE_ORGANIZATION4096団体管理
EQUIPMENT_MANAGE_LOCATION8192場所管理
EQUIPMENT_MANAGE_TYPE16384備品種別管理
EQUIPMENT_BULK_OPERATION32768備品一括操作
FORM_VIEW65536フォーム一覧閲覧
FORM_SUBMISSION_APPROVE131072フォーム回答承認/却下
EVENT_VIEW262144イベント一覧閲覧
EVENT_EDIT524288イベント作成・編集・削除

複数の権限はビット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認証・ゲスト登録

主要エンドポイント:

MethodPath説明
POST/guest/authenticateゲスト認証(LINE ID直接、Service Binding用)
POST/guest/loginゲストログイン(認証コード経由)

認証フロー:

  1. LINE User IDまたは認証コードを受け取り
  2. 認証コードの場合、LINE APIでアクセストークン取得
  3. LINEプロフィール取得
  4. BANチェック
  5. Guestsテーブルにユーザー作成(新規の場合はGroupも作成)
  6. 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管理・レート制限

主要エンドポイント:

MethodPath説明
POST/token/verifyトークン検証
POST/admin/banユーザーBAN
POST/admin/ban-user-globalグローバルBAN
POST/admin/ban-user-by-typeタイプ別BAN
POST/admin/unbanBAN解除
POST/admin/unban-advancedBAN ID指定解除
GET/admin/user-info/:discordUserIdユーザー情報取得
GET/admin/bansBAN一覧取得
POST/rate-limit/login-api/checkレートリミットチェック
POST/rate-limit/login-api/record-violation違反記録

トークン検証フロー:

  1. Authorization: Bearer xxx ヘッダーからトークン取得
  2. JWT署名検証(HMAC-SHA256)
  3. 有効期限チェック
  4. BANチェック(BannedUsersテーブル)
  5. 検証結果を返却

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.comroom.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判定タイミング

  1. ログイン時: discord-auth-worker / line-auth-worker でチェック
  2. API呼び出し時: unified-auth-worker の /token/verify でチェック

6. 環境変数

6.1 login-api

変数名説明
JWT_SECRETJWT署名シークレット
DISCORD_CLIENT_IDDiscord OAuth Client ID
DISCORD_CLIENT_SECRETDiscord OAuth Client Secret
CHANNEL_IDLINE Channel ID
CHANNEL_SECRETLINE Channel Secret
LOGIN_API_URL開発環境URL(設定時は開発モード)

6.2 discord-auth-worker

変数名説明
JWT_SECRETJWT署名シークレット(Secrets Store)
AUTH_DISCORD_TOKENDiscord Bot Token(Secrets Store)
REQUIRED_GUILD_ID必須ギルドID(Secrets Store)
REQUIRED_ROLE_IDスタッフ必須ロールID(Secrets Store)
ADMIN_ROLE_ID管理者ロールID
DBD1 Database Binding

6.3 line-auth-worker

変数名説明
JWT_SECRETJWT署名シークレット(Secrets Store)
LINE_CLIENT_IDLINE Channel ID
LINE_CLIENT_SECRETLINE Channel Secret
LINE_REDIRECT_URIコールバックURL
DBD1 Database Binding

6.4 unified-auth-worker

変数名説明
JWT_SECRETJWT署名シークレット(Secrets Store)
RATE_LIMITERレートリミッターBinding
DBD1 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の依存関係上、以下の順序でデプロイ:

  1. unified-auth-worker(依存なし)
  2. discord-auth-worker(依存なし)
  3. line-auth-worker(依存なし)
  4. 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