React集成oidc-react从API加载配置导致登录报错求助
React中动态加载配置实现OIDC登录失败问题
我花了四天时间尝试在React中实现OIDC登录,用的是oidc-react库,但一直没成功。这个库默认假设配置是硬编码的,但我需要从/settings接口加载配置。
加载配置的代码
import { Context, createContext, useContext, useEffect, useState } from "react"; export class AppSettings { buildName: string = ''; authority: string = ''; truthPortalApi: string = ''; environment: string = ''; } export const AppSettingsContext: Context<AppSettings> = createContext<AppSettings>(null!) export const AppSettingsProvider: React.FC<{ children: JSX.Element }> = ({ children }) => { const [settings, setSettings] = useState<AppSettings>(new AppSettings()) useEffect(() => { fetch(`${window.location.origin}/settings`) .then(response => response.json()) .then(data => setSettings(data)) }, []); return ( <AppSettingsContext.Provider value={ settings }> {children} </AppSettingsContext.Provider> ) } export function useAppSettings(): AppSettings { return useContext<AppSettings>(AppSettingsContext); }
尝试使用配置的代码
let url: string = `${window.location.protocol}//${window.location.host}/`; function createUserManager(appSettings: AppSettings): UserManager { const authSettings: UserManagerSettings = { authority: appSettings.authority, client_id: 'client_app', redirect_uri: `${url}signin-callback`, // 猜测值,不清楚此处应配置什么 silent_redirect_uri: `${url}silent-callback.html`, // 猜测值,不清楚此处应配置什么 post_logout_redirect_uri: `${url}`, response_type: 'code', scope: 'openid profile offline_access app', loadUserInfo: true }; return new UserManager(authSettings); } export const LocalAuthProvider: React.FC<{ children: JSX.Element[] }> = ({ children }) => { let appSettings: AppSettings = useAppSettings(); const [userManager, setUserManager] = useState<UserManager>(() => (appSettings && appSettings.authority) ? createUserManager(appSettings) : null!); useEffect(() => () => { if (appSettings && appSettings.authority) { console.warn(`LocalAuthProvider setUserManager authority='${appSettings.authority}'`); setUserManager(createUserManager(appSettings)); } }, [appSettings]); return ( <AuthProvider userManager={userManager} > {children} </AuthProvider > ); }
报错信息
Uncaught (in promise) Error: No authority or metadataUrl configured on settings
显然问题在于配置加载完成前就返回了AuthProvider,但直接返回{children}也不可行,因为子组件调用useAuth时会报错。
解决方案
核心问题是配置加载完成前,AuthProvider就接收了未初始化的userManager,导致权限配置缺失报错。可以通过以下步骤修复:
1. 更新AppSettingsProvider,增加加载状态
给上下文补充加载状态和错误信息,明确标识配置是否就绪:
import { Context, createContext, useContext, useEffect, useState } from "react"; export class AppSettings { buildName: string = ''; authority: string = ''; truthPortalApi: string = ''; environment: string = ''; } // 扩展上下文结构,包含加载状态与错误信息 interface AppSettingsContextValue { settings: AppSettings; isLoaded: boolean; loadError?: Error; } export const AppSettingsContext: Context<AppSettingsContextValue> = createContext<AppSettingsContextValue>({ settings: new AppSettings(), isLoaded: false }) export const AppSettingsProvider: React.FC<{ children: JSX.Element }> = ({ children }) => { const [settings, setSettings] = useState<AppSettings>(new AppSettings()) const [isLoaded, setIsLoaded] = useState(false) const [loadError, setLoadError] = useState<Error>() useEffect(() => { fetch(`${window.location.origin}/settings`) .then(response => { if (!response.ok) throw new Error('配置加载失败') return response.json() }) .then(data => { setSettings(data) setIsLoaded(true) }) .catch(err => { setLoadError(err) setIsLoaded(true) // 加载失败也标记为完成,避免无限等待 }) }, []); return ( <AppSettingsContext.Provider value={{ settings, isLoaded, loadError }}> {children} </AppSettingsContext.Provider> ) } export function useAppSettings(): AppSettingsContextValue { return useContext(AppSettingsContext); }
2. 更新LocalAuthProvider,等待配置加载完成
在配置就绪前渲染加载状态,避免传递无效的userManager:
import { useState, useEffect } from 'react'; import { AuthProvider, UserManager, UserManagerSettings } from 'oidc-react'; import { useAppSettings, AppSettings } from './AppSettingsProvider'; const url: string = `${window.location.protocol}//${window.location.host}/`; function createUserManager(appSettings: AppSettings): UserManager { const authSettings: UserManagerSettings = { authority: appSettings.authority, client_id: 'client_app', redirect_uri: `${url}signin-callback`, // 需与OIDC服务端配置的回调地址完全一致 silent_redirect_uri: `${url}silent-callback.html`, // 需与OIDC服务端配置的静默回调地址完全一致 post_logout_redirect_uri: `${url}`, response_type: 'code', scope: 'openid profile offline_access app', loadUserInfo: true }; return new UserManager(authSettings); } export const LocalAuthProvider: React.FC<{ children: JSX.Element[] }> = ({ children }) => { const { settings, isLoaded, loadError } = useAppSettings(); const [userManager, setUserManager] = useState<UserManager | null>(null); useEffect(() => { if (isLoaded && settings.authority) { setUserManager(createUserManager(settings)); } }, [isLoaded, settings]); // 配置未加载完成时,显示加载状态 if (!isLoaded) { return <div>加载中...</div>; // 可替换为项目全局加载组件 } // 配置加载失败时,显示错误信息 if (loadError) { return <div>配置加载失败:{loadError.message}</div>; } // 配置缺失关键参数时,提示异常 if (!settings.authority) { return <div>配置错误:未找到OIDC授权地址</div>; } // 确保userManager有效后再渲染AuthProvider return ( <AuthProvider userManager={userManager!} > {children} </AuthProvider> ); }
额外说明
redirect_uri和silent_redirect_uri必须与OIDC服务端配置的地址完全一致(包括协议、域名、路径),否则会被服务端拒绝回调- 可根据项目需求自定义加载状态和错误提示的UI(比如骨架屏、全局弹窗)
内容的提问来源于stack exchange,提问作者Richard Barraclough
相关产品推荐
相关产品推荐

