131 lines
3.0 KiB
TypeScript
131 lines
3.0 KiB
TypeScript
'use client';
|
|
|
|
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
|
|
import { User } from 'oidc-client-ts';
|
|
import { userManager } from '@/lib/oidc-config';
|
|
|
|
interface AuthContextType {
|
|
user: User | null;
|
|
isLoading: boolean;
|
|
isAuthenticated: boolean;
|
|
login: () => Promise<void>;
|
|
logout: () => Promise<void>;
|
|
error: string | null;
|
|
}
|
|
|
|
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
|
|
export const useAuth = () => {
|
|
const context = useContext(AuthContext);
|
|
if (context === undefined) {
|
|
throw new Error('useAuth必须在AuthProvider内部使用');
|
|
}
|
|
return context;
|
|
};
|
|
|
|
interface AuthProviderProps {
|
|
children: ReactNode;
|
|
}
|
|
|
|
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
|
const [user, setUser] = useState<User | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const isAuthenticated = !!user && !user.expired;
|
|
|
|
useEffect(() => {
|
|
if (!userManager) return;
|
|
|
|
const initAuth = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
const currentUser = await userManager.getUser();
|
|
setUser(currentUser);
|
|
} catch (err) {
|
|
console.error('初始化认证失败:', err);
|
|
setError('认证初始化失败');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
initAuth();
|
|
|
|
// 监听用户状态变化
|
|
const handleUserLoaded = (user: User) => {
|
|
setUser(user);
|
|
setError(null);
|
|
};
|
|
|
|
const handleUserUnloaded = () => {
|
|
setUser(null);
|
|
};
|
|
|
|
const handleAccessTokenExpired = () => {
|
|
setUser(null);
|
|
setError('访问令牌已过期');
|
|
};
|
|
|
|
const handleSilentRenewError = (error: Error) => {
|
|
console.error('静默续约失败:', error);
|
|
setError('令牌续约失败');
|
|
};
|
|
|
|
userManager.events.addUserLoaded(handleUserLoaded);
|
|
userManager.events.addUserUnloaded(handleUserUnloaded);
|
|
userManager.events.addAccessTokenExpired(handleAccessTokenExpired);
|
|
userManager.events.addSilentRenewError(handleSilentRenewError);
|
|
|
|
return () => {
|
|
if (userManager) {
|
|
userManager.events.removeUserLoaded(handleUserLoaded);
|
|
userManager.events.removeUserUnloaded(handleUserUnloaded);
|
|
userManager.events.removeAccessTokenExpired(handleAccessTokenExpired);
|
|
userManager.events.removeSilentRenewError(handleSilentRenewError);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
const login = async () => {
|
|
if (!userManager) {
|
|
setError('用户管理器未初始化');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setError(null);
|
|
await userManager.signinRedirect();
|
|
} catch (err) {
|
|
console.error('登录失败:', err);
|
|
setError('登录失败');
|
|
}
|
|
};
|
|
|
|
const logout = async () => {
|
|
if (!userManager) {
|
|
setError('用户管理器未初始化');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setError(null);
|
|
await userManager.signoutRedirect();
|
|
} catch (err) {
|
|
console.error('登出失败:', err);
|
|
setError('登出失败');
|
|
}
|
|
};
|
|
|
|
const value: AuthContextType = {
|
|
user,
|
|
isLoading,
|
|
isAuthenticated,
|
|
login,
|
|
logout,
|
|
error,
|
|
};
|
|
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
};
|