@ axa-fr/ react-oidc
创建于 OpenID 连接( OIDC) 的响应组件和钩子 @axa-fr/oidc-client。提供方处理浏览器认证回调、会话恢复和令牌更新;您的组件使用钩子签名、阅读用户信息并呼叫受保护的API.
库支持授权代码流与PKCE,多命名配置,可选服务-工人符号隔离,PAR,和DPoP. 它是一个浏览器侧认证库,而不是服务器侧会话或授权系统.
快开始
npm install @axa-fr/react-oidc
使用 PKCE 授权代码流程与身份提供者注册公共浏览器客户端 。 注册确切的回调 URL 和登录后 URL,并允许应用程序的出处通过 CORS 。 从未在浏览器应用程序中嵌入客户端秘密 .
将下面的发行者和客户端ID替换为您自己的设置 。 此示例假设客户端委托的 React 应用程序带有一个名为 HTML 的元素 root。您的网络服务器必须在 /authentication/callback 以及 /。 。 。 .
import { useState } from 'react';
import { createRoot } from 'react-dom/client';
import { OidcProvider, useOidc, type OidcConfiguration } from '@axa-fr/react-oidc';
const configuration: OidcConfiguration = {
client_id: 'your-public-client',
authority: 'https://issuer.example.com',
redirect_uri: `${window.location.origin}/authentication/callback`,
scope: 'openid profile',
};
function Account(): React.JSX.Element {
const { login, logout, isAuthenticated } = useOidc();
const [hasError, setHasError] = useState(false);
const changeSession = async (): Promise<void> => {
setHasError(false);
try {
if (isAuthenticated) {
await logout('/');
} else {
await login('/');
}
} catch {
setHasError(true);
}
};
return (
<main>
<p>{isAuthenticated ? 'Signed in' : 'Not signed in'}</p>
<button type="button" onClick={changeSession}>
{isAuthenticated ? 'Sign out' : 'Sign in'}
</button>
{hasError && <p role="alert">Authentication failed. Please try again.</p>}
</main>
);
}
const root = document.getElementById('root');
if (!root) throw new Error('Missing root element');
createRoot(root).render(
<OidcProvider configuration={configuration}>
<Account />
</OidcProvider>,
);
OidcProvider 处理回调路由; 不调用 loginCallbackAsync() {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}你在这个反动设定里. login('/') 在认证后选择应用程序目的地,而不是注册的回调 URL.
此示例使用浏览器存储. 对于可选符号隔离,请遵循 服务-工人设置。 。 。 .
flowchart LR
UI["React components and hooks"] --> Provider["OidcProvider"]
Provider --> Client["@axa-fr/oidc-client"]
Client --> IdP["OIDC identity provider"]
Client --> SW["Optional service worker"]
SW --> API["Trusted API"]
Client --> Fetch["OIDC fetch without worker"]
Fetch --> API
保护组件
OidcSecure 在没有经认证的会话时开始登录, 并在认证后才使其子 :
import { OidcSecure } from '@axa-fr/react-oidc';
export function PrivatePage(): React.JSX.Element {
return (
<OidcSecure callbackPath="/account">
<h1>Account</h1>
</OidcSecure>
);
}
把它放在下面 放在下面 OidcProvider。您可以保护整个应用程序、路由元素或较小的组件。 可选道具是 callbackPath,授权 extras,以及 configurationName。 。 。 .
排序较高的组件等同:
import { withOidcSecure } from '@axa-fr/react-oidc';
function AccountDetails(): React.JSX.Element {
return <h1>Account details</h1>;
}
export const ProtectedAccountDetails = withOidcSecure(AccountDetails, '/account');
其签名是: withOidcSecure(Component, callbackPath?, extras?, configurationName?)。 。 。 .
这些组件打开 UI; 您的 API 必须独立验证符并强制授权 .
调用受保护的 API
useOidcFetch() 返回附加访问符(或服务-工作占位符)并与更新相融合的抓取包。 只用于信任的API URL,而不是任意的用户提供目的地.
import { useState } from 'react';
import { useOidcFetch } from '@axa-fr/react-oidc';
export function ProfileRequest(): React.JSX.Element {
const { fetch: oidcFetch } = useOidcFetch();
const [status, setStatus] = useState('Ready');
const loadProfile = async (): Promise<void> => {
setStatus('Loading…');
try {
const response = await oidcFetch('https://api.example.com/profile');
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}`);
}
setStatus('Profile request succeeded');
} catch {
setStatus('Could not load the profile');
}
};
return (
<>
<button type="button" onClick={loadProfile}>
Load profile
</button>
<p role="status">{status}</p>
</>
);
}
保护API的消费者 OidcSecure,或等待到 useOidc().isAuthenticated 这是真的。 检查 response.ok:普通的HTTP出错反应不会自动拒绝.
| API 密码 | 参数/结果 |
|---|---|
useOidcFetch(fetch?, configurationName?, demonstratingProofOfPossession?) | 回返 { fetch }; 浏览器获取和配置默认值 'default'。 。 。 . |
withOidcFetch(fetch?, configurationName?, demonstratingProofOfPossession?)(Component) | 输入 a fetch 插入一个组件. |
譬如说, withOidcFetch()(ProfileComponent) 通过道具而不是钩子提供同样的包装.
用户信息和符号钩
用户信息
useOidcUser() 读取提供者的用户-信息端点并显示装入状态。 导出的enum 是 OidcUserStatus数字 :
import { OidcUserStatus, useOidcUser } from '@axa-fr/react-oidc';
export function UserGreeting(): React.JSX.Element {
const { oidcUser, oidcUserLoadingState } = useOidcUser();
switch (oidcUserLoadingState) {
case OidcUserStatus.Loading:
return <p>Loading profile…</p>;
case OidcUserStatus.Unauthenticated:
return <p>Please sign in.</p>;
case OidcUserStatus.LoadingError:
return <p>Could not load your profile.</p>;
default:
return <p>Hello, {oidcUser?.name ?? 'there'}.</p>;
}
}
钩子也回来了 reloadOidcUser()启用 useOidcUser<MyUserInfo>(configurationName?, demonstratingProofOfPossession?) 习惯索赔 MyUserInfo 扩展 OidcUserInfo。 。 。 .
钩入引用
| 钩子 | 返回值 |
|---|---|
useOidc(configurationName?) | isAuthenticated, (中文(简体) ), login, (中文(简体) ), logout, (中文(简体) ), renewTokens。 。 。 . |
useOidcUser<T>(configurationName?, demonstratingProofOfPossession?) | oidcUser, (中文(简体) ), oidcUserLoadingState, (中文(简体) ), reloadOidcUser。 。 。 . |
useOidcAccessToken(configurationName?) | accessToken, (中文(简体) ), accessTokenPayload,并在启用时, generateDemonstrationOfProofOfPossessionAsync。 。 。 . |
useOidcIdToken(configurationName?) | idToken, (中文(简体) ), idTokenPayload。 。 。 . |
login(callbackPath?, extras?, silentLoginOnly?, scope?), (中文(简体) ), logout(callbackPath?, extras?),以及 renewTokens(extras?) 返回承诺.
偏爱 useOidcFetch 超过手动建筑授权头。 当工人代币隐藏被启用时, accessToken 是一个占位符,而不是真正的象征。 不渲染或登录原始令牌,并将用户的诉求视为个人数据。 ID符向客户端识别用户;它不是对API访问符的替换.
服务工作者
确保您的应用程序的静态资产目录存在( S)public (d) 安装工人资产:
node ./node_modules/@axa-fr/react-oidc/bin/copy-service-worker-files.mjs public
保留生成 OidcServiceWorker.js 与软件包更新同步。 例如,把这个合并到您的应用程序中 package.json数字 :
{
"scripts": {
"postinstall": "node ./node_modules/@axa-fr/react-oidc/bin/copy-service-worker-files.mjs public"
}
}
然后说:
- 配置
public/OidcTrustedDomains.js与您的供应商和 API 目的地. - 添加
service_worker_relative_url: '/OidcServiceWorker.js'到 OIDC 配置 . - 设定
service_worker_only: true如果登录不能返回浏览器符号存储. - 为HTTPS(或本地主机)的工人服务,范围涵盖申请.
跟着 核心服务人员指南 对于一个可信任的域例,所有选项,符号曝光选择,以及倒置行为.
多塔布登录 : 何时 allowMultiTabLogin: true 设置在一个可信任域条目中,使用 useOidcFetch() 或者说 withOidcFetch() 保护API的电话。 他们的分页占位符向工人指明会议届次。 普通取取或默认 Axios 请求不能提供该标记,并可能导致 HTTP 401.
工人可以从JavaScript应用程序中隐藏访问权限并刷新令牌,但它确实 没有 防止 XSS 或停止通过您的应用程序输入代码 。 它没有隐藏所有的身份信息。 保持正常的XSS防御并避免广泛的可信任域规则.
配置、更新、PAR和DPoP
OidcProvider 接受相同的建议 OidcConfiguration 作为香草客户端. 在配置对象外保留提供者道具,如自定义组件和路由回调.
- 配置 : 见 核心配置参考文献 用于所需的字段、存储、发现、超时、登录和会话监测.
- 更新 : 默认启用自动更新 。 对于
TokenAutomaticRenewMode.AutomaticOnlyWhenFetchExecuted,使用时useOidcFetch编号 :withOidcFetch因此,请求触发续约。 见 更新行为和严格更新。 。 。 . - 静态登录 : 添加单独的
silent_redirect_uri如果需要的话。 提供者处理其无声记录路线. 提供者和浏览器饼干政策可以防止iframe登录. - 帕罗: 设定
par: 'auto'或者说'required'内configuration; 默认为'disabled'。 。 。 .auto当一个端点被公示时使用 PAR; 缺少所需的端点在导航前失效。 一旦被选中, PAR 错误永远不会默默地降级. 见 完整的PAR语义、错误和CORS要求。 。 。 . - 掌声: 启用
demonstrating_proof_of_possession和使用情况useOidcFetch(undefined, 'default', true)或者说withOidcFetch(undefined, 'default', true)。对于用户信息,使用useOidcUser('default', true)。见 DPoP和工人特定环境。身份提供者和资源服务器都必须支持它.
自定义组件和提供者选项
用自己的组件替换内置状态屏幕。 这些是 提供者道具字段 configuration数字 :
const Loading = (): React.JSX.Element => <p>Restoring your session…</p>;
const AuthenticationError = (): React.JSX.Element => (
<p role="alert">Sign-in failed. Please return to the sign-in page and try again.</p>
);
<OidcProvider
configuration={configuration}
loadingComponent={Loading}
authenticatingErrorComponent={AuthenticationError}
>
<App />
</OidcProvider>;
| 提供者道具 | 目的 |
|---|---|
configuration, (中文(简体) ), configurationName | 客户端配置及其名称( E)'default' (默认情况下). |
loadingComponent | 会话恢复/加载屏幕. |
loadingTimeoutComponent | 装入监视器过期时显示屏幕 . |
authenticatingComponent | 开始登录时显示屏幕 . |
authenticatingErrorComponent | 登录或召回失败屏幕 . |
callbackSuccessComponent | 屏幕在成功召回后显示 . |
sessionLostComponent | 会话损失屏幕. |
serviceWorkerNotSupportedComponent | 需要只使用工人模式时不可用- 工人屏幕 . |
onSessionLost | 处理会话损失而不是显示内置会话损失流 . |
onLogoutFromAnotherTab, (中文(简体) ), onLogoutFromSameTab | 对登录事件的反应 . |
onEvent(configurationName, eventName, data) | 观察客户端事件;避免记录完整的有效载荷. |
withCustomHistory | 返回历史适配器 replaceState(url, stateHistory?)。 。 。 . |
navigateAfterCallback(callbackPath) | Async召回以进行后登录导航;此导航优先于历史适配器. |
getFetch | 为客户端提供自定义获取执行 . |
location | 自定义 ILOidcLocation 适配器. |
自定义状态组件收到 configurationName设置 configuration.loading_timeout_ms 以更改默认的 30秒装入监视器,或非正值来禁用它。 见 OidcProviderProps 全部类型.
运行和下一个.js
客户端路由器
默认导航使用浏览器历史 API 和发送 popstate保留 OidcProvider 安装在召回的 URL 上。 保护路由元素 OidcSecure;不需要特定的路由器包.
对于路由器特定集成,请提供 navigateAfterCallback 进行和等待路由器的导航,或 withCustomHistory 替换默认历史适配器。 这些选项属于 OidcProvider。 。 。 .
库保留了hash-route回调匹配的后遗症集成,但OAuth重定向URI不能包含碎片. 优先使用基于路径的回调URL进行新部署,即使其余应用程序使用散列路由器. 现有的散列召回设置取决于提供者的特定行为. 交互和无声回调 URL 必须不同 .
下一个.js
将这个浏览器库保存在仅限客户端的边界后: 不访问 window 或在服务器上渲染时初始化 OIDC 客户端。 在App路由器, 'use client' 单是不会禁用预发;使用适当的仅客户端安装或动态导入策略.
仓库的 下一个.js 演示 使用该 页面路由器 和定制历史适配器。 对于只使用初始客户端的提供者 next/router,后登录导航适配器可以看起来像这样:
import { useRouter } from 'next/router';
import { OidcProvider } from '@axa-fr/react-oidc';
function ClientAuth({ children }: React.PropsWithChildren): React.JSX.Element {
const router = useRouter();
return (
<OidcProvider
configuration={configuration}
navigateAfterCallback={async (path: string): Promise<void> => {
await router.replace(path);
window.dispatchEvent(new Event('popstate'));
}}
>
{children}
</OidcProvider>
);
}
给 configuration 是您的浏览器侧 OIDC 配置 。 不复制 next/router 输入 App 路由器应用程序; 适应该路由器的API和导航生命周期。 此软件包不提供服务器侧路由保护或服务器会话.
命名的配置
使用名称来区分身份提供者或具有不同范围的会议. 给每个配置不同的回调 URL, 对于工人模式, 给一个匹配的可信任域条目 .
<OidcProvider configuration={accountConfiguration}>
<OidcProvider configuration={paymentsConfiguration} configurationName="payments">
<App />
</OidcProvider>
</OidcProvider>
明确选择名称; 嵌入不会改变钩子的默认名称 :
const { login, isAuthenticated } = useOidc('payments');
const { fetch: paymentsFetch } = useOidcFetch(undefined, 'payments');
使用 <OidcSecure configurationName="payments"> 被保护的UI。 Token和用户钩也接受一个配置名称.
错误和缺少提供者
OidcError, (中文(简体) ), OidcErrorCode, (中文(简体) ), isOidcError, (中文(简体) ), OidcStateError, (中文(简体) ), OidcStateErrorCode, (中文(简体) ), isOidcStateError,而PAR错误分类/守护符则从此套件中再导出. 使用稳定的代码和相,而不是字符串匹配,用于错误处理.
见 核心错误和事件指南 用于更新错误、缺失/匹配状态、缺失非ces、可重试性和恢复。 提供者的自定义错误/会话损失屏幕和 onEvent 回调是React集成点. 在钩外严格更新时,请使用 OidcClient.getOrThrow(name).renewTokensOrThrowAsync()。 。 。 .
这个 onEvent(configurationName, eventName, data) 回调收到核心客户端的 符号同步事件A级 syncTokensAsync_end 事件可以报告会话损失或延期;它不能保证恢复或完成的持久性。 这些事件也发生在复用有效令牌的检查中,因此避免将每对始发/终发作为应用程序加载周期处理.
打开 syncTokensAsync_error,供应商打电话 onSessionLost() 当供应。 否则,它会选择会话损失屏幕,使用 sessionLostComponent 如果配置的话。 同样的处理方式已经适用于 refreshTokensAsync_error,包括已解决的更新失败。 读取错误 onEvent 不抑制此处理或抓取同步例外。 保持回调轻量级,不丢出;只记录所选错误代码,相位和状态,绝不是完整的有效载荷或例外.
当没有客户端被初始化的名字, useOidc, (中文(简体) ), useOidcUser, (中文(简体) ), useOidcAccessToken,以及 useOidcIdToken 警告每个名称一次,并返回未经认证/无效默认值。 这在测试和故事书中有用,但并没有初始化认证.
OidcClient.get(name) 返回时 null 失踪客户; getOrThrow(name) 明显失败. OidcSecure 和通过决议提出的请求 useOidcFetch 需要初始化并保持故障快取。 在使用前挂载匹配的提供者 .
实例和进一步阅读
- 反应演示 ——路线,钩子,工人选项,以及被命名的配置.
- 下一个.js 演示 ——页路由器集成.
- 核心客户指南和 API。 。 。 .
- 服务人员协议。 。 。 .
- 成套服务人员指南。 。 。 .
- 财务问题和部署指导。 。 。 .
演示使用寄存器的 pnpm 工作空间. 跟踪它们的README进行设置,而不是单独安装每个软件包.
用于源开发, 使用根 贡献命令 和已死的锁文件。 保持Vitest和覆盖版本对齐;MSW 保留 满足Vitest的嘲弄同伴依赖.