사용자가 Fabric 포털을 통해 Microsoft Entra ID 로그인할 수 있도록 Fabric 앱에 Fabric SSO(Single Sign-On)를 설정합니다. 이 문서에서는 핸드오프 흐름을 설명하고 배포된 앱에 필요한 구성 및 SDK 통합을 사용하도록 설정하는 방법을 보여 줍니다.
사전 요구 사항
- 인증을 사용하도록 설정된 Fabric 앱 프로젝트입니다. 인증 구성을 참조하세요.
- 배포된 Fabric 앱 항목입니다. Fabric으로 배포를 참조하세요.
Fabric SSO 작동 방식
Fabric SSO(Single Sign-On)는 애플리케이션과 Fabric 포털 간에 보안 postMessage 기반 핸드오프를 사용합니다. 리디렉션 또는 콜백 페이지는 없습니다.
- 앱이 팝업에서 Fabric 포털을 열고
postMessage수신기를 등록합니다. - 사용자는 Fabric 포털 내의 Microsoft Entra ID 통해 인증합니다.
- Fabric 확장은
window.opener.postMessage()통해 핸드오프 코드를 앱으로 다시 보냅니다. - SDK는 Rayfin 세션 토큰에 대한 핸드오프 코드를 교환하고 세션을 만듭니다.
- Fabric 팝업이 자동으로 닫힙니다.
이 흐름은 PKCE(코드 교환용 증명 키), 상태 논스 및 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 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');
}
로그인 또는 로그아웃이 완료되면 세션 변경 내용을 구독하여 UI를 업데이트합니다.
client.auth.onSessionChange((session) => {
console.log('Session changed:', session?.isAuthenticated ? 'signed in' : 'signed out');
});
GitHub Copilot 로그인 및 등록 생성
VS Code에서
| 목표 | 예제 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 후크 추가 | 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은(는) bare 출처이며rayfin/rayfin.yml의allowedRedirectUris에 있는 항목 중 하나와 일치합니다. - 가져오기는
@microsoft/rayfin-auth-provider-fabric에서 옵니다(지원 중단된 콜백 헬퍼가 아님).
Fabric iframe 내에 포함된 모드 사용
앱이 Fabric iframe 내에 로드되는 경우(예: 사용자가 Fabric 포털에서 열 때) 팝업 흐름 대신 포함된 모드를 사용합니다.
- 임베디드 모드는 부모 프레임에서
postMessage통해 세션을 획득합니다. - 팝업을 열지 않으며 사용자 제스처가 필요하지 않으므로 페이지 로드 시 호출해도 안전합니다.
- SDK는 URL에서
?fabricEmbedded=true포함된 모드를 자동으로 검색합니다. 옵션에서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>;
4단계 인증 폭포를 구현합니다.
- 이미 인증된 경우 기존 세션을 반환합니다.
- 새로 고침 토큰을 통해 무중단 갱신을 시도합니다.
- 포함된 모드 -Fabric iframe 내에서 실행 하는 경우 부모 프레임에
postMessage통해 세션을 가져옵니다. - 팝업 창(팝업 방식)에서 Fabric 포털을 열고
postMessage전달을 기다립니다.
1~3단계는 페이지 로드에서 안전하게 호출할 수 있습니다. 4단계는 팝업을 열고 사용자 제스처 처리기 내에서 실행해야 합니다.
FabricAuthOptions
| Property | Type | 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(선택 사항) |
임베디드 모드로 강제 설정합니다. URL의 ?fabricEmbedded=true에서 자동으로 감지됩니다. |
도우미 함수
| Function | Description |
|---|---|
initEmbeddedAuth(auth, options) |
페이지 로드로부터 안전한 임베디드 인증입니다. Fabric iframe 내에서 실행되는 경우 세션을 반환하거나, 그렇지 않으면 null 반환합니다. |
initiateFabricLogin(auth, options) |
하위 수준 팝업 흐름입니다. PKCE 매개 변수가 있는 팝업에서 Fabric 포털을 열고 postMessage 전달을 수신 대기합니다. |
isEmbeddedMode(options) |
앱이 포함된 모드(Fabric iframe)에서 실행 중인 경우 true 반환합니다. |
보안 기능
- PKCE S256 – 모든 흐름은 암호화 코드 검증 도구를 생성하고 권한 부여 코드 가로채기를 방지하기 위한 과제를 생성합니다.
- State nonce – 임의 nonce는 전달 응답을 원래 탭에 연결하여 사이트 간 요청 위조를 방지합니다.
-
postMessage원본 유효성 검사 – SDK는 들어오는 메시지의 유효성을event.origin검사하고 예기치 않은 원본의 메시지를 거부합니다. - 자동 정리 – PKCE 상태는 5분 후에 만료되고 다음 흐름에서 가비지 수집됩니다.
- 흐름 시간 제한 – 전달 메시지를 받지 못하면 5분 후에 팝업 흐름 시간이 초과됩니다.
인증 문제 해결
팝업 차단됨
브라우저에서 Fabric 포털 창을 차단했습니다.
ensureSignedInWithFabric()가 동기식 사용자 제스처 처리기(예: 버튼 onClick)에서 호출되도록 하세요. 사용자 상호 작용 전에 페이지 로드 또는 비동기 체인 내에서 호출하지 마세요.
세션이 유지되지 않음
RayfinClient이(가) 올바른 baseUrl 및 publishableKey로 구성되었는지 확인합니다. 콜백 탭과 원래 탭은 BroadcastChannel 및 localStorage이 작동하려면 동일 출처를 공유해야 합니다.
긴 지연 후 인증 실패
로그인 흐름은 5분 후에 만료됩니다. 로그인 프로세스를 시작하지만 해당 시간 내에 완료하지 않으면 흐름이 실패합니다. 팝업을 닫고 로그인 단추를 다시 선택하여 새 흐름을 시작합니다.