You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 23:47:26