为Fabric应用设置Fabric单一登录(SSO),以便用户可以通过Fabric门户使用Microsoft Entra ID登录。 本文介绍切换流,并演示如何为已部署的应用启用所需的配置和 SDK 集成。
先决条件
- 启用了身份验证的Fabric应用项目。 请参阅 “配置身份验证”。
- 已部署的 Fabric 应用项。 请参阅 部署到 Fabric。
Fabric SSO 的工作原理
Fabric 单点登录 (SSO) 使用应用程序与 Fabric 门户之间基于 postMessage 的安全交接机制。 没有重定向或回调页面:
- 您的应用会在弹出窗口中打开 Fabric 门户,并注册一个
postMessage监听器。 - 用户在Fabric门户中通过Microsoft Entra ID进行身份验证。
- Fabric扩展通过
window.opener.postMessage()将移交代码发送回应用。 - SDK 交换 Rayfin 会话令牌的交接代码并创建会话。
- Fabric弹出窗口会自动关闭。
该流程通过 PKCE(代码交换证明密钥)、state 随机数和 postMessage 来源验证来保护,以防止授权码拦截和跨站点请求伪造。
启用Fabric身份验证
将Fabric身份验证配置添加到 rayfin/rayfin.yml 文件:
services:
auth:
enabled: true
allowedRedirectUris:
- http://localhost:5173
fabric:
enabled: true
对于已部署的应用程序,请重新部署以推送更新的设置:
npx rayfin up
对于已部署的应用,npx rayfin up 会将您已部署的应用回调 URL 添加到 allowedRedirectUris。
安装Fabric身份验证提供程序(可选)
使用 npm create @microsoft/rayfin@latest 创建的项目已包含 @microsoft/rayfin-auth-provider-fabric。 仅当你要为尚未包含该软件包的项目添加 Fabric 身份验证时,才需要手动安装该软件包:
npm install @microsoft/rayfin-auth-provider-fabric
将登录和注册添加到应用
Fabric SSO 对登录和注册使用单个 API:ensureSignedInWithFabric()。 当用户首次登录时,Fabric会根据用户的Microsoft Entra ID标识自动为其预配 Rayfin 会话-没有单独的注册呼叫。 同一代码路径也适用于回访用户。
可以使用 VS Code 中的GitHub Copilot手动添加此代码或生成此代码。
手动添加登录
在用户手势处理程序中调用 ensureSignedInWithFabric()(例如,在按钮选择事件中):
import { RayfinClient } from '@microsoft/rayfin-client';
import { ensureSignedInWithFabric } from '@microsoft/rayfin-auth-provider-fabric';
const client = new RayfinClient({
baseUrl: import.meta.env.VITE_RAYFIN_API_URL,
publishableKey: import.meta.env.VITE_RAYFIN_PUBLISHABLE_KEY,
});
const fabricOptions = {
workspaceId: import.meta.env.VITE_FABRIC_WORKSPACE_ID,
projectId: import.meta.env.VITE_FABRIC_ITEM_ID,
fabricPortalUrl: import.meta.env.VITE_FABRIC_PORTAL_URL,
returnOrigin: window.location.origin,
};
async function handleSignIn() {
// Signs in existing users and provisions new users on first sign-in.
const session = await ensureSignedInWithFabric(client.auth, fabricOptions);
if (session.isAuthenticated && session.user) {
console.log('Signed in as:', session.user.email);
}
}
必须从同步用户手势处理程序调用该函数,以避免弹出阻止程序。 在页面加载时调用它,或者在用户交互之前的异步调用链中调用它,都会触发浏览器的弹窗保护机制。
returnOrigin 必须是裸源(方案和主机,无路径),例如 https://app.contoso.com。 SDK 使用它来验证传入 postMessage 事件。
手动添加注销
调用 client.auth.signOut() 以结束会话并清除缓存的令牌:
async function handleSignOut() {
await client.auth.signOut();
console.log('Signed out');
}
订阅会话变更,以便在登录或注销完成时更新用户界面:
client.auth.onSessionChange((session) => {
console.log('Session changed:', session?.isAuthenticated ? 'signed in' : 'signed out');
});
使用 GitHub Copilot 生成登录和注册
如果在 VS Code 中使用 GitHub Copilot,请在 Fabric Apps 项目中打开Copilot 对话助手,并使用此类提示来搭建身份验证代码的基架。 Copilot 遵循 Fabric VS Code 扩展中附带的 Rayfin 技能里的模式。
| 目标 | 示例 Copilot 提示词 |
|---|---|
| 添加登录按钮 | Add a Sign in with Fabric button to my React app using ensureSignedInWithFabric from @microsoft/rayfin-auth-provider-fabric. Read workspaceId, projectId, and fabricPortalUrl from VITE_* env vars and set returnOrigin to window.location.origin. |
| 添加注销按钮 | Add a Sign out button that calls client.auth.signOut() and updates the UI when the session ends. |
| 添加一个支持身份验证的 React Hook | Create a useFabricAuth React hook that exposes session, signIn, signOut, and isAuthenticated, using ensureSignedInWithFabric and client.auth.onSessionChange. |
| 支持嵌入式模式 | Update my app's entry point to call initEmbeddedAuth on page load so users signed in through the Fabric portal don't have to click Sign in again. |
| 限制路由 | Wrap the /dashboard route so it calls ensureSignedInWithFabric before rendering and redirects unauthenticated users to a sign-in page. |
Copilot生成代码后,请查看编辑并确保:
- 调用
ensureSignedInWithFabric()在用户手势处理程序(例如onClick)内运行,而不是在页面加载时运行。 -
returnOrigin是一个裸源,并且与rayfin/rayfin.yml中的allowedRedirectUris条目之一匹配。 - 从
@microsoft/rayfin-auth-provider-fabric导入(而不是从已弃用的回调辅助函数导入)。
在 Fabric iframe 中使用嵌入式模式
当应用在 Fabric iframe 内加载(例如,当用户从 Fabric 门户打开应用时),请使用嵌入模式而不是弹出窗口流:
- 嵌入模式通过
postMessage从父框架获取会话。 - 它不会打开弹出窗口,不需要用户手势,因此可以安全地调用页面加载。
- SDK 从
?fabricEmbedded=trueURL 中自动检测嵌入模式。 还可以通过在选项中设置fabricEmbedded: true来强制它。
在应用启动中提前调用 initEmbeddedAuth() :
import { initEmbeddedAuth } from '@microsoft/rayfin-auth-provider-fabric';
import { client } from './lib/rayfin';
const session = await initEmbeddedAuth(client.auth, {
workspaceId: import.meta.env.VITE_FABRIC_WORKSPACE_ID,
projectId: import.meta.env.VITE_FABRIC_ITEM_ID,
fabricPortalUrl: import.meta.env.VITE_FABRIC_PORTAL_URL,
returnOrigin: window.location.origin,
});
if (session) {
console.log('Signed in via embedded mode:', session.user?.email);
}
initEmbeddedAuth() 会在应用未以嵌入模式运行时返回 null,因此可以安全地无条件调用。
ensureSignedInWithFabric() 还会在回退到弹出窗口流之前自动尝试嵌入模式。
在 React 中使用Fabric身份验证
创建集成登录、注册和注销的自定义挂钩:
import { useState, useEffect, useCallback } from 'react';
import { ensureSignedInWithFabric } from '@microsoft/rayfin-auth-provider-fabric';
import { client } from './lib/rayfin';
const fabricOptions = {
workspaceId: import.meta.env.VITE_FABRIC_WORKSPACE_ID,
projectId: import.meta.env.VITE_FABRIC_ITEM_ID,
fabricPortalUrl: import.meta.env.VITE_FABRIC_PORTAL_URL,
returnOrigin: window.location.origin,
};
export function useFabricAuth() {
const [session, setSession] = useState(client.auth.getSession());
useEffect(() => client.auth.onSessionChange(setSession), []);
// Signs in existing users and provisions new users on first sign-in.
const signIn = useCallback(async () => {
const result = await ensureSignedInWithFabric(client.auth, fabricOptions);
setSession(result);
return result;
}, []);
const signOut = useCallback(async () => {
await client.auth.signOut();
}, []);
return {
session,
signIn,
signOut,
isAuthenticated: session?.isAuthenticated ?? false,
};
}
在组件中使用挂钩:
function App() {
const { isAuthenticated, signIn, signOut } = useFabricAuth();
if (!isAuthenticated) {
return <button onClick={signIn}>Sign in with Fabric</button>;
}
return (
<>
<Dashboard />
<button onClick={signOut}>Sign out</button>
</>
);
}
API 参考
ensureSignedInWithFabric
function ensureSignedInWithFabric(
auth: Auth,
options: FabricAuthOptions
): Promise<OpaqueSession>;
实现四步身份验证瀑布:
- 如果已通过身份验证,则返回现有会话。
- 尝试通过刷新令牌进行静默刷新。
- 嵌入模式 - 如果在 Fabric iframe 中运行,则通过
postMessage获取会话到父帧。 - 在弹出窗口中打开 Fabric 门户(弹出流程),并等待
postMessage切换完成。
步骤 1 到 3 可以在页面加载时安全地调用。 步骤 4 将打开弹出窗口,必须在用户手势处理程序中运行。
FabricAuthOptions
| 财产 | 类型 | Description |
|---|---|---|
workspaceId |
string |
Fabric工作区 ID。 |
projectId |
string |
Fabric应用项 ID。 |
fabricPortalUrl |
string |
Fabric 门户基本 URL(例如,https://app.fabric.microsoft.com)。 |
returnOrigin |
string |
应用的交付来源 postMessage (例如, window.location.origin)。 必须是裸源(方案和主机,无路径)。 |
fabricEmbedded |
boolean(可选) |
强制嵌入模式。 从 ?fabricEmbedded=true URL 中自动检测到。 |
帮助函数
| Function | Description |
|---|---|
initEmbeddedAuth(auth, options) |
页面加载安全的嵌入式身份验证。如果在 Fabric iframe 中运行,则返回会话;否则返回 null。 |
initiateFabricLogin(auth, options) |
低级别弹出窗口流。 在弹出窗口中使用 PKCE 参数打开 Fabric 门户,并监听 postMessage 交接。 |
isEmbeddedMode(options) |
如果应用在嵌入式模式下运行(Fabric iframe),则返回 true。 |
安全功能
- PKCE S256 – 每个流都会生成加密代码验证器和质询,以防止授权代码截获。
- State 随机数 – 随机生成的随机数会将交接响应与发起该请求的选项卡关联起来,从而防止跨站点请求伪造。
-
postMessage源验证 – SDK 对event.origin传入消息进行验证,并拒绝来自意外源的消息。 - 自动清理 – PKCE 状态会在 5 分钟后过期,并在下一次流程执行时由垃圾回收机制清理。
- 流程超时 – 如果在 5 分钟内未收到交接消息,弹出窗口流程将超时。
排查身份验证问题
弹出窗口被阻止
浏览器阻止了Fabric门户窗口。 确保 ensureSignedInWithFabric() 从同步用户手势处理程序(例如按钮 onClick)调用。 在用户交互之前,请勿在页面加载或异步链中调用它。
会话未持久化
确认 RayfinClient 已配置正确的 baseUrl 和 publishableKey。 回调选项卡和原始选项卡必须共享相同的源,才能BroadcastChannellocalStorage正常工作。
身份验证在长时间延迟后失败
登录流将在 5 分钟后过期。 如果你开始登录流程,但未在该时限内完成,整个流程将失败。 关闭弹出窗口,再次选择登录按钮以启动新流。