認証コンテキスト
:LiTarget: 用途
ユーザー認証状態をアプリ全体で共有するReact Context。
:LiSparkle: 特徴
- ログイン/ログアウト
- ユーザー情報共有
- 権限チェック
:LiCode: 実コード(SCALE Base より自動抽出)
:LiInfo:
lib/auth-context.tsxの中身そのもの。コピペ即可。
'use client';
import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react';
export interface User {
id: string;
name: string;
email: string;
altEmail?: string;
role: 'admin' | 'manager' | 'member';
departmentId: string;
departmentName: string;
avatar: string;
allowedSystems: string[];
}
const SESSION_KEY = 'sb_user';
interface AuthContextType {
user: User | null;
isLoading: boolean;
login: (email: string, password: string) => Promise<boolean>;
logout: () => void;
}
const AuthContext = createContext<AuthContextType | null>(null);
// Demo users for initial setup
const DEMO_USERS: (User & { password: string })[] = [
{
id: '1',
name: '大串', // 2026-05-06 ce-2026-05-06-03: タスクシート担当者表記「大串」と統一(フル表記は法人代表者名等の特別な場面のみ)
email: 'y-ogushi@scale-group.co.jp',
password: 'scale2023',
role: 'admin',
departmentId: 'all',
departmentName: '経営',
avatar: '大',
allowedSystems: ['command', 'assistant', 'pilot', 'tasks', 'docs', 'admin', 'services', 'system-mgmt', 'design', 'writing', 'x', 'seo', 'hp', 'fs', 'scale-lead', 'pm', 'pipeline', 'accounting', 'hr', 'ai-ops', 'automation', 'datalake', 'goals', 'partners', 'brand', 'rnd', 'insight', 'cmo', 'build'],
},
{
id: '2',
name: 'ハヤテ',
email: 'marketing@scale-group.co.jp',
password: 'marketing',
role: 'manager',
departmentId: 'marketing',
departmentName: 'マーケティング',
avatar: 'ハ',
allowedSystems: ['assistant', 'seo', 'x', 'tasks', 'sites', 'goals'],
},
{
id: '3',
name: 'センリ',
email: 'sales@scale-group.co.jp',
password: 'sales',
role: 'manager',
departmentId: 'sales',
departmentName: '営業',
avatar: 'セ',
allowedSystems: ['assistant', 'fs', 'scale-lead', 'tasks', 'sites', 'goals'],
},
{
id: '4',
name: '細川',
email: 'hosokawa@scale-group.co.jp',
altEmail: 's.hosokawa03044@gmail.com',
password: 'scale2023',
role: 'admin',
departmentId: 'all',
departmentName: 'PM',
avatar: '細',
allowedSystems: ['command', 'assistant', 'pilot', 'tasks', 'docs', 'admin', 'services', 'system-mgmt', 'design', 'writing', 'x', 'seo', 'hp', 'fs', 'scale-lead', 'pm', 'pipeline', 'hr', 'ai-ops', 'automation', 'datalake', 'goals', 'partners', 'brand', 'rnd', 'insight', 'cmo'],
},
];
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// Migrate legacy key if present
try {
const legacy = localStorage.getItem('scale-base-user');
if (legacy && !sessionStorage.getItem(SESSION_KEY)) {
sessionStorage.setItem(SESSION_KEY, legacy);
}
} catch { /* ignore */ }
const stored = sessionStorage.getItem(SESSION_KEY);
if (stored) {
try {
const parsed = JSON.parse(stored);
const master = DEMO_USERS.find(u => u.email === parsed.email || u.name === parsed.name);
if (master) {
parsed.allowedSystems = master.allowedSystems;
parsed.altEmail = master.altEmail;
parsed.role = master.role;
sessionStorage.setItem(SESSION_KEY, JSON.stringify(parsed));
}
setUser(parsed);
} catch { /* ignore */ }
}
setIsLoading(false);
}, []);
const login = useCallback(async (identifier: string, password: string): Promise<boolean> => {
// Accept email, altEmail, or name (case-insensitive)
const input = identifier.trim().toLowerCase();
const found = DEMO_USERS.find(u =>
u.password === password && (
u.email.toLowerCase() === input ||
(u.altEmail && u.altEmail.toLowerCase() === input) ||
u.name.toLowerCase() === input
)
);
if (!found) return false;
const { password: _, ...userData } = found;
setUser(userData);
sessionStorage.setItem(SESSION_KEY, JSON.stringify(userData));
return true;
}, []);
const logout = useCallback(() => {
setUser(null);
try { sessionStorage.removeItem(SESSION_KEY); } catch { /* ignore */ }
try { localStorage.removeItem('scale-base-user'); } catch { /* ignore */ }
}, []);
return (
<AuthContext.Provider value={{ user, isLoading, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
return ctx;
}
:LiFolder: ソースファイルのパス
/Users/oogushiyuuki/Library/CloudStorage/GoogleDrive-y-ogushi@scale-group.co.jp/マイドライブ/AI/scale-base/lib/auth-context.tsx
:LiHandPointer: 使い方
対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。
:LiAlertCircle: 注意事項
- 依存パッケージを忘れず追加