認証と権限 Auth & permissions
管理者ポータルは Supabase Auth を使った JWT ベース認証と、ロール/権限キーによる画面側フィルタリングで構成されています。
The admin portal uses Supabase Auth for JWT-based login, plus a role / permission-key system that filters the UI client-side.
ログインフロー Login flow
sequenceDiagram
participant U as Admin
participant FE as Admin Portal
participant SB as Supabase Auth
participant DB as PostgreSQL
U->>FE: email + password
FE->>SB: signInWithPassword()
SB-->>FE: session (JWT)
FE->>DB: SELECT admins WHERE user_id = auth.uid()
DB-->>FE: admin row + role_id
FE->>DB: SELECT role_permissions WHERE role_id = ?
DB-->>FE: permission_keys[]
FE-->>U: Dashboard (nav filtered by permission_keys)
Note over FE,DB: Subsequent requests include Authorization: Bearer {jwt}
関連コンポーネント Components involved
src/auth/AuthContext.tsx— セッション管理、signIn/signOut、現在の管理者情報、権限キーの配列。onAuthStateChangeでセッション変化を監視。Holds the session, exposessignIn/signOut, the current admin, and permission keys. Listens viaonAuthStateChange.src/auth/RequireAuth.tsx— ルートラッパ。未認証なら/loginへリダイレクト。Route wrapper that redirects to/loginwhen unauthenticated.src/lib/supabase.ts— 公開クライアントsupabaseと、service_roleキーを持つ管理専用クライアントsupabaseAdminを初期化。Initializes both the publicsupabaseclient and a privilegedsupabaseAdminclient that carries theservice_rolekey.
service_role キーは強力です。配布物に埋め込まず、本ポータルでも UI ビルドに含めない運用が前提です。
The service_role key is powerful and must never be bundled into an unprivileged client. Treat it as secret even within the portal build pipeline.
ロール Roles
ロールは roles テーブルで定義され、各ロールに対する権限キーは role_permissions で管理されます。
Roles live in the roles table, with permission keys per role stored in role_permissions.
| Role | 想定担当Intended owner | アクセス範囲Scope |
|---|---|---|
| Super Admin | システム管理者Platform admin | 全権限All permissions |
| Ops Manager | 運用マネージャーOps manager | 駐車場・ユーザー・売上・サポートParking, users, revenue, support |
| Content Manager | コンテンツ担当Content team | 記事・広告・ゲーミ・カスタマイズArticles, ads, gamification, theming |
| Support Agent | CS担当Customer support | サポートチケット・誤情報報告Support tickets + error reports |
権限キーマトリクス Permission key matrix
30以上の権限キーが定義されており、Roles 画面でチェックボックスで一括管理できます。
There are 30+ permission keys, manageable as a checkbox matrix on the Roles screen.
| カテゴリCategory | キーKeys |
|---|---|
| Dashboard | dashboard.view |
| Parking | parking.view · parking.edit · tags.view · tags.edit · reviews.view · reviews.moderate |
| Users | users.view · users.edit |
| Ops | plans.view · plans.edit · support.view · support.respond · notifications.view · notifications.send · sales.view |
| Owners | owners.view · owners.edit |
| Content | articles.view · articles.edit · articles.publish · ads.view · ads.edit |
| Gamification | gamification.view · gamification.edit |
| Admins | admins.view · admins.edit · roles.view · roles.edit |
| System | settings.view · settings.edit |
アクセス制御の実装レイヤ Enforcement layers
- クライアント:
AuthContextが権限キーを保持、サイドバー / ボタンをhasPermission('parking.edit')で表示制御。 - Client:
AuthContextexposes permission keys; the sidebar and action buttons gate onhasPermission('parking.edit'). - RLS ポリシー: Supabase の Row Level Security を各テーブルに設定し、テーブル単位でアクセスを制御。
- RLS policies: Supabase Row Level Security on each table restricts access server-side.
- service_role: 管理者アカウント作成や削除など、強い操作のみ
supabaseAdmin(service_role) で実行。 - service_role: Only privileged flows (create / delete admin) go through
supabaseAdminwith the service_role key.
管理者アカウント作成フロー Admin account creation flow
sequenceDiagram participant SA as Super Admin participant FE as Admins page participant SBA as supabaseAdmin (service_role) participant Auth as Supabase Auth participant DB as admins table SA->>FE: Fill form + submit FE->>FE: Generate 12-char random password FE->>SBA: admin.createUser(email, password, email_confirm: true) SBA->>Auth: POST /auth/v1/admin/users Auth-->>SBA: user SBA->>DB: INSERT into admins (user_id, name, email, role_id) DB-->>SBA: row SBA-->>FE: created admin FE-->>SA: Display initial password (one-time, copy button)
初期パスワードは画面上でのみ表示され、閉じた後は再表示できません。
リセット時は resetAdminPassword() が新しいパスワードを生成 → Auth 更新 → 画面表示 の流れになります。
The initial password is shown once and never again. A reset flow regenerates a password, updates Auth, and surfaces the new value via resetAdminPassword().