@microsoft/rayfin-guide 1.1.0 → 1.33.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/docs/app-backend/deploy.md +256 -0
- package/assets/docs/app-backend/index.md +126 -0
- package/assets/docs/app-backend/pricing.md +66 -0
- package/assets/docs/auth/fabric.md +328 -0
- package/assets/docs/auth/index.md +33 -0
- package/assets/docs/auth/overview.md +130 -0
- package/assets/docs/cli/ai-files.md +146 -0
- package/assets/docs/cli/env-interpolation.md +187 -0
- package/assets/docs/cli/env-migration.md +135 -0
- package/assets/docs/cli/environment-variables.md +173 -0
- package/assets/docs/cli/index.md +84 -0
- package/assets/docs/cli/installation.md +107 -0
- package/assets/docs/cli/quickstart.md +88 -0
- package/assets/docs/data/graphql.md +267 -0
- package/assets/docs/data/index.md +20 -0
- package/assets/docs/data/overview.md +270 -0
- package/assets/docs/data/permissions.md +172 -0
- package/assets/docs/data/validation.md +165 -0
- package/assets/docs/getting-started/create-app-with-cli.md +118 -0
- package/assets/docs/getting-started/create-rayfin-item.md +73 -0
- package/assets/docs/getting-started/index.md +201 -0
- package/assets/docs/getting-started/project-structure.md +290 -0
- package/assets/docs/hosting/index.md +183 -0
- package/assets/docs/index.md +92 -45
- package/assets/docs/known-limitations.md +50 -0
- package/assets/docs/preview/local-dev-docker.md +124 -0
- package/package.json +1 -1
- package/assets/docs/quickstart.md +0 -81
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 2
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Fabric Auth
|
|
6
|
+
|
|
7
|
+
Fabric authentication lets users sign in to your Rayfin application with their existing Microsoft Entra identity through the Fabric Portal.
|
|
8
|
+
No separate login form is needed — users authenticate once in Fabric and your app inherits that session automatically.
|
|
9
|
+
|
|
10
|
+
The SDK supports two modes:
|
|
11
|
+
|
|
12
|
+
- **Popup flow** — your app opens the Fabric Portal in a new browser tab, the user authenticates, and the tab closes automatically.
|
|
13
|
+
- **Embedded flow** — your app runs inside a Fabric iframe and inherits the session via `postMessage` with no popup, redirect, or user interaction.
|
|
14
|
+
|
|
15
|
+
Both flows are secured with PKCE S256, `postMessage` origin validation, and state nonces.
|
|
16
|
+
|
|
17
|
+
## How it works
|
|
18
|
+
|
|
19
|
+
### Popup flow
|
|
20
|
+
|
|
21
|
+
1. Your app opens the Fabric Portal in a new browser tab and registers a `postMessage` listener.
|
|
22
|
+
2. The user authenticates through Entra ID inside the Fabric Portal.
|
|
23
|
+
3. The Fabric extension sends the handoff code back to your app via `window.top.opener.postMessage()`.
|
|
24
|
+
4. The SDK exchanges the handoff code for Rayfin session tokens and creates a session.
|
|
25
|
+
5. The Fabric tab is closed automatically.
|
|
26
|
+
|
|
27
|
+
No callback page or redirect is needed.
|
|
28
|
+
|
|
29
|
+
### Embedded flow (Fabric iframe)
|
|
30
|
+
|
|
31
|
+
1. The Fabric Shell loads your app inside an iframe with `?fabricEmbedded=true` in the URL.
|
|
32
|
+
2. On startup your app detects embedded mode and calls `initEmbeddedAuth()`.
|
|
33
|
+
3. The SDK generates PKCE parameters in memory and sends `auth.requestHandoff` to the parent frame via `postMessage`.
|
|
34
|
+
4. The Fabric Extension Host responds with a handoff code.
|
|
35
|
+
5. The SDK exchanges the handoff code for Rayfin session tokens and creates a session.
|
|
36
|
+
|
|
37
|
+
No popup, redirect, or user click is needed.
|
|
38
|
+
|
|
39
|
+
## Enable Fabric auth
|
|
40
|
+
|
|
41
|
+
Add the `fabric` section and your app's origin to `rayfin/rayfin.yml`:
|
|
42
|
+
|
|
43
|
+
```yaml
|
|
44
|
+
services:
|
|
45
|
+
auth:
|
|
46
|
+
enabled: true
|
|
47
|
+
allowedRedirectUris:
|
|
48
|
+
- http://localhost:5173
|
|
49
|
+
fabric:
|
|
50
|
+
enabled: true
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The `allowedRedirectUris` list must include your app's bare origin (e.g. `http://localhost:5173`).
|
|
54
|
+
The Fabric brokered auth flow uses the origin as the `postMessage` target for the handoff code.
|
|
55
|
+
|
|
56
|
+
After changing `rayfin.yml`, redeploy the backend:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx rayfin up
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
For a remote deployment, re-deploy to push the updated settings:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx rayfin up
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Install the provider package
|
|
69
|
+
|
|
70
|
+
The Fabric auth provider is a separate companion package:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npm install @microsoft/rayfin-auth-provider-fabric
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Client-side usage
|
|
77
|
+
|
|
78
|
+
### Popup flow: sign in with a button click
|
|
79
|
+
|
|
80
|
+
Call `ensureSignedInWithFabric` from a user-gesture handler (for example, a button click).
|
|
81
|
+
Step 4 of the waterfall calls `window.open()`, so a user gesture is required to avoid popup blockers.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { RayfinClient } from '@microsoft/rayfin-client';
|
|
85
|
+
import { ensureSignedInWithFabric } from '@microsoft/rayfin-auth-provider-fabric';
|
|
86
|
+
|
|
87
|
+
const client = new RayfinClient({
|
|
88
|
+
baseUrl: 'http://localhost:5168',
|
|
89
|
+
publishableKey: 'pk-commonSampleAppKey',
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
async function handleSignIn() {
|
|
93
|
+
const session = await ensureSignedInWithFabric(client.auth, {
|
|
94
|
+
workspaceId: '<your-fabric-workspace-id>',
|
|
95
|
+
projectId: '<your-rayfin-item-id>',
|
|
96
|
+
fabricPortalUrl: 'https://app.fabric.microsoft.com',
|
|
97
|
+
returnOrigin: window.location.origin,
|
|
98
|
+
});
|
|
99
|
+
console.log('Signed in:', session.user);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Embedded flow: automatic authentication on startup
|
|
104
|
+
|
|
105
|
+
Call `initEmbeddedAuth()` once at app startup (for example, in a React `useEffect` or initialization routine).
|
|
106
|
+
It is safe to call on every page load — it returns `null` immediately when not in embedded mode.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { RayfinClient } from '@microsoft/rayfin-client';
|
|
110
|
+
import { initEmbeddedAuth } from '@microsoft/rayfin-auth-provider-fabric';
|
|
111
|
+
|
|
112
|
+
const client = new RayfinClient({
|
|
113
|
+
baseUrl: import.meta.env.VITE_RAYFIN_API_URL,
|
|
114
|
+
publishableKey: import.meta.env.VITE_RAYFIN_PUBLISHABLE_KEY,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const fabricOptions = {
|
|
118
|
+
workspaceId: import.meta.env.VITE_FABRIC_WORKSPACE_ID,
|
|
119
|
+
projectId: import.meta.env.VITE_FABRIC_ITEM_ID,
|
|
120
|
+
fabricPortalUrl: import.meta.env.VITE_FABRIC_PORTAL_URL,
|
|
121
|
+
returnOrigin: window.location.origin,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Safe to call on every page load — no-op when not embedded.
|
|
125
|
+
const session = await initEmbeddedAuth(client.auth, fabricOptions);
|
|
126
|
+
if (session) {
|
|
127
|
+
console.log('Embedded session established:', session.user);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Import `@microsoft/rayfin-auth-provider-fabric` statically in your app entry module rather than only via dynamic `import()`.
|
|
132
|
+
The package captures the `?fabricEmbedded=true` URL flag into `sessionStorage` as a side effect at module load, and that must happen on the initial page load before any client-side navigation strips the query string (for example, a post-logout redirect to `/login`).
|
|
133
|
+
Apps that resume from a stored refresh token never enter the embedded auth path on first load, so a dynamic-only import would miss the URL flag and fall back to the popup on the next sign-in.
|
|
134
|
+
|
|
135
|
+
### Detecting embedded mode
|
|
136
|
+
|
|
137
|
+
The SDK detects embedded mode when any of these conditions is true:
|
|
138
|
+
|
|
139
|
+
- The `fabricEmbedded` option is set to `true` in `FabricAuthOptions`.
|
|
140
|
+
- The URL contains the `?fabricEmbedded=true` query parameter.
|
|
141
|
+
- A previous call already stored the flag in `sessionStorage`.
|
|
142
|
+
|
|
143
|
+
You can check this manually:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { isEmbeddedMode } from '@microsoft/rayfin-auth-provider-fabric';
|
|
147
|
+
|
|
148
|
+
const embedded = isEmbeddedMode({
|
|
149
|
+
workspaceId: '...',
|
|
150
|
+
projectId: '...',
|
|
151
|
+
fabricPortalUrl: '...',
|
|
152
|
+
returnOrigin: window.location.origin,
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Supporting both flows
|
|
157
|
+
|
|
158
|
+
Most apps should support both embedded mode (iframe) and the popup flow (standalone browser).
|
|
159
|
+
`ensureSignedInWithFabric()` handles this automatically — it tries embedded auth first, then falls back to the popup:
|
|
160
|
+
|
|
161
|
+
1. Return existing session if already authenticated.
|
|
162
|
+
2. Attempt refresh via refresh token.
|
|
163
|
+
3. If embedded mode is detected, use `postMessage` handoff (no popup).
|
|
164
|
+
4. Otherwise, open the Fabric Portal in a new tab and wait for the handoff.
|
|
165
|
+
|
|
166
|
+
For page-load initialization (no user gesture), use `initEmbeddedAuth()` instead.
|
|
167
|
+
It skips step 4 and returns `null` when no embedded session is available.
|
|
168
|
+
|
|
169
|
+
### React hook example
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import { useState, useCallback } from 'react';
|
|
173
|
+
import { ensureSignedInWithFabric } from '@microsoft/rayfin-auth-provider-fabric';
|
|
174
|
+
import { client } from './lib/rayfin';
|
|
175
|
+
|
|
176
|
+
const fabricOptions = {
|
|
177
|
+
workspaceId: import.meta.env.VITE_FABRIC_WORKSPACE_ID,
|
|
178
|
+
projectId: import.meta.env.VITE_FABRIC_ITEM_ID,
|
|
179
|
+
fabricPortalUrl: import.meta.env.VITE_FABRIC_PORTAL_URL,
|
|
180
|
+
returnOrigin: window.location.origin,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export function useFabricAuth() {
|
|
184
|
+
const [session, setSession] = useState(client.auth.getSession());
|
|
185
|
+
|
|
186
|
+
const signIn = useCallback(async () => {
|
|
187
|
+
const result = await ensureSignedInWithFabric(
|
|
188
|
+
client.auth,
|
|
189
|
+
fabricOptions
|
|
190
|
+
);
|
|
191
|
+
setSession(result);
|
|
192
|
+
return result;
|
|
193
|
+
}, []);
|
|
194
|
+
|
|
195
|
+
return { session, signIn, isAuthenticated: session?.isAuthenticated ?? false };
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## API reference
|
|
200
|
+
|
|
201
|
+
### ensureSignedInWithFabric
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
function ensureSignedInWithFabric(
|
|
205
|
+
auth: Auth,
|
|
206
|
+
options: FabricAuthOptions
|
|
207
|
+
): Promise<OpaqueSession>;
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The primary API.
|
|
211
|
+
Implements a four-step waterfall where the first successful step short-circuits the rest:
|
|
212
|
+
|
|
213
|
+
1. **Already authenticated** — returns the existing session.
|
|
214
|
+
2. **Refresh token** — attempts to refresh the session silently.
|
|
215
|
+
3. **Embedded mode** — if running inside a Fabric iframe (`fabricEmbedded=true`), uses `postMessage` to acquire a session without a popup.
|
|
216
|
+
4. **Open Fabric broker** — opens the Fabric Portal in a new tab, listens for the handoff code via `postMessage`, exchanges the code for tokens, and creates a session.
|
|
217
|
+
|
|
218
|
+
Steps 1–3 are safe to call on page load.
|
|
219
|
+
Step 4 opens a new browser tab and must run inside a user-gesture handler.
|
|
220
|
+
|
|
221
|
+
### initEmbeddedAuth
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
function initEmbeddedAuth(
|
|
225
|
+
auth: Auth,
|
|
226
|
+
options: FabricAuthOptions
|
|
227
|
+
): Promise<OpaqueSession | null>;
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Call once at app startup.
|
|
231
|
+
Returns the authenticated session when running in embedded mode, or `null` when not embedded.
|
|
232
|
+
Never opens a popup or new tab — safe for page-load use.
|
|
233
|
+
|
|
234
|
+
**Waterfall:**
|
|
235
|
+
|
|
236
|
+
1. Return existing session if authenticated.
|
|
237
|
+
2. Attempt refresh via refresh token.
|
|
238
|
+
3. Request handoff from the parent frame via `postMessage` and exchange for tokens.
|
|
239
|
+
|
|
240
|
+
### initiateFabricLogin
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
function initiateFabricLogin(
|
|
244
|
+
auth: Auth,
|
|
245
|
+
options: FabricAuthOptions
|
|
246
|
+
): Promise<void>;
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Low-level function that opens the Fabric Portal and listens for the postMessage handoff.
|
|
250
|
+
Called internally by `ensureSignedInWithFabric` in step 4.
|
|
251
|
+
Most apps should use `ensureSignedInWithFabric` instead.
|
|
252
|
+
|
|
253
|
+
### FabricAuthOptions
|
|
254
|
+
|
|
255
|
+
| Property | Type | Description |
|
|
256
|
+
| --- | --- | --- |
|
|
257
|
+
| `workspaceId` | `string` | The Fabric workspace ID. |
|
|
258
|
+
| `projectId` | `string` | The Rayfin item ID (AppBackend artifact ID). |
|
|
259
|
+
| `fabricPortalUrl` | `string` | The Fabric Portal base URL (for example, `https://app.fabric.microsoft.com`). |
|
|
260
|
+
| `returnOrigin` | `string` | Your app's origin (for example, `window.location.origin`). Used as the `postMessage` target origin. |
|
|
261
|
+
| `fabricEmbedded` | `boolean` (optional) | When `true`, force embedded mode. The SDK also auto-detects embedded mode when `?fabricEmbedded=true` is in the URL. |
|
|
262
|
+
|
|
263
|
+
## Security
|
|
264
|
+
|
|
265
|
+
- **PKCE S256** — Every flow generates a cryptographic code verifier and challenge to prevent authorization code interception.
|
|
266
|
+
- **State nonce** — A random nonce ties the postMessage response to the originating flow, preventing CSRF.
|
|
267
|
+
- **In-closure code verifier** — The PKCE `code_verifier` is held in memory (closure) and never persisted to localStorage.
|
|
268
|
+
- **Origin validation** — The SDK validates `event.origin` on incoming messages against `fabricPortalUrl`.
|
|
269
|
+
The Fabric extension uses an explicit `targetOrigin` (not `"*"`) when sending the handoff code.
|
|
270
|
+
- **Flow timeout** — The flow times out after 5 minutes if no postMessage is received.
|
|
271
|
+
- **Session isolation** — In embedded mode, the iframe's `localStorage` stores the session tokens, isolated from the parent frame by the browser's same-origin policy.
|
|
272
|
+
|
|
273
|
+
## Environment variables
|
|
274
|
+
|
|
275
|
+
Fabric auth requires three Vite environment variables so your frontend can build the `FabricAuthOptions` at runtime.
|
|
276
|
+
`npx rayfin up` writes the underlying `RAYFIN_PUBLIC_*` values to `rayfin/.env`, and `rayfin env --framework vite` (run automatically by the scaffolded `predev` / `prebuild` hooks) maps them to Vite-compatible names in `.env.local`.
|
|
277
|
+
|
|
278
|
+
| Source variable (`rayfin/.env`) | Vite variable (`.env.local`) | Description | Example |
|
|
279
|
+
| --- | --- | --- | --- |
|
|
280
|
+
| `RAYFIN_PUBLIC_ITEM_ID` | `VITE_FABRIC_ITEM_ID` | The Fabric item ID (Fabric data app artifact ID). Maps to `projectId`. | `21b98705-08d5-448c-ab32-d88a3d00af41` |
|
|
281
|
+
| `RAYFIN_PUBLIC_WORKSPACE_ID` | `VITE_FABRIC_WORKSPACE_ID` | The Fabric workspace ID. Maps to `workspaceId`. | `b80c0e39-468a-4742-8f0a-458dc6b1c918` |
|
|
282
|
+
| `RAYFIN_PUBLIC_PORTAL_URL` | `VITE_FABRIC_PORTAL_URL` | The Fabric Portal base URL. Maps to `fabricPortalUrl`. | `https://app.fabric.microsoft.com/` |
|
|
283
|
+
|
|
284
|
+
For local development, add these to `rayfin/.env`:
|
|
285
|
+
|
|
286
|
+
```text
|
|
287
|
+
RAYFIN_PUBLIC_ITEM_ID=<your-rayfin-item-id>
|
|
288
|
+
RAYFIN_PUBLIC_WORKSPACE_ID=<your-fabric-workspace-id>
|
|
289
|
+
RAYFIN_PUBLIC_PORTAL_URL=https://app.fabric.microsoft.com/
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Then read them in your application code:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
const fabricOptions = {
|
|
296
|
+
workspaceId: import.meta.env.VITE_FABRIC_WORKSPACE_ID,
|
|
297
|
+
projectId: import.meta.env.VITE_FABRIC_ITEM_ID,
|
|
298
|
+
fabricPortalUrl: import.meta.env.VITE_FABRIC_PORTAL_URL,
|
|
299
|
+
returnOrigin: window.location.origin,
|
|
300
|
+
};
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Deployment values
|
|
304
|
+
|
|
305
|
+
After running `npx rayfin up`, the CLI records deployment metadata in `rayfin/.deployments.json` and merges the corresponding `RAYFIN_PUBLIC_*` variables into `rayfin/.env`:
|
|
306
|
+
|
|
307
|
+
```text
|
|
308
|
+
RAYFIN_PUBLIC_ITEM_ID=<guid>
|
|
309
|
+
RAYFIN_PUBLIC_WORKSPACE_ID=<guid>
|
|
310
|
+
RAYFIN_PUBLIC_PORTAL_URL=https://app.fabric.microsoft.com/
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Run `rayfin env --framework vite` (or `npm run dev`, which triggers it via the scaffolded `predev` hook) to generate `.env.local` with the Vite-compatible names. Use `RAYFIN_PUBLIC_ITEM_ID` as the `projectId` and `RAYFIN_PUBLIC_WORKSPACE_ID` as the `workspaceId` in your `FabricAuthOptions`.
|
|
314
|
+
|
|
315
|
+
## Troubleshooting
|
|
316
|
+
|
|
317
|
+
- **Popup blocked** — Call `ensureSignedInWithFabric` from a synchronous user-gesture handler (for example, a button `onClick`).
|
|
318
|
+
Calling it on page load or inside an `async` chain before the user clicks triggers popup blockers.
|
|
319
|
+
- **Session not persisting** — Confirm the `RayfinClient` is configured with the correct `baseUrl` and `publishableKey`.
|
|
320
|
+
- **Timeout after 5 minutes** — The handoff code was not received.
|
|
321
|
+
Check that `returnOrigin` matches your app's actual origin and that the Fabric extension is sending to the correct origin.
|
|
322
|
+
- **Origin mismatch** — The `fabricPortalUrl` origin must match the origin of the Fabric Portal tab.
|
|
323
|
+
Verify you are using the correct URL for your environment (production, PPE, or dev).
|
|
324
|
+
- **`initEmbeddedAuth` returns `null`** — Ensure the URL contains `?fabricEmbedded=true` or set `fabricEmbedded: true` in the options.
|
|
325
|
+
- **Embedded handoff timeout** — The parent frame did not respond.
|
|
326
|
+
Verify that `returnOrigin` matches the iframe's actual origin.
|
|
327
|
+
- **State mismatch error** — The response state did not match the request.
|
|
328
|
+
This can indicate a replay attack or a stale response from a previous flow.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 20
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Auth
|
|
6
|
+
|
|
7
|
+
Rayfin Auth gives you managed user authentication, session handling, and token management so you can focus on your application instead of building identity infrastructure.
|
|
8
|
+
One call to `signIn()` replaces hundreds of lines of auth plumbing, and every subsequent data call is automatically authenticated.
|
|
9
|
+
|
|
10
|
+
## Why use Rayfin Auth
|
|
11
|
+
|
|
12
|
+
- **Zero auth infrastructure** — User management, sessions, and token handling work out of the box with no external identity service to deploy or configure.
|
|
13
|
+
- **Auth and data are pre-integrated** — After sign-in, every `client.data.*` call automatically carries the authenticated context.
|
|
14
|
+
No manual header management or token passing between modules.
|
|
15
|
+
- **Automatic per-user data isolation** — JWT claims drive row-level security policies defined declaratively in your data model decorators.
|
|
16
|
+
Each user sees only the data they own without writing RLS SQL.
|
|
17
|
+
- **Works in every deployment mode** — The same auth code runs identically in local Docker development, self-hosted environments, and managed Fabric hosting.
|
|
18
|
+
No code changes required when you move between environments.
|
|
19
|
+
|
|
20
|
+
## Auth methods
|
|
21
|
+
|
|
22
|
+
Rayfin supports multiple authentication methods that you can enable independently:
|
|
23
|
+
|
|
24
|
+
- **Email and password** — Traditional sign-up and sign-in with managed credentials.
|
|
25
|
+
Enabled by default in new projects.
|
|
26
|
+
- **Magic link** — Passwordless sign-in via email links.
|
|
27
|
+
- **Fabric brokered auth** — Single sign-on through the Microsoft Fabric Portal using the user's Entra identity.
|
|
28
|
+
See [Fabric Brokered Auth](./fabric.md) for setup and usage.
|
|
29
|
+
|
|
30
|
+
## Next steps
|
|
31
|
+
|
|
32
|
+
- [How to configure auth](./overview.md)
|
|
33
|
+
- [Fabric brokered auth](./fabric.md)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 1
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Configure Rayfin Auth
|
|
6
|
+
|
|
7
|
+
This guide explains how to configure Rayfin authentication in your application.
|
|
8
|
+
`signUp`, `signIn`, `signOut`, and `getSession` cover most flows and return opaque session data managed by Rayfin.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Token management** - Access tokens never exposed to application code.
|
|
13
|
+
- **Session Management** - Automatic state tracking with localStorage, sessionStorage, or custom storage.
|
|
14
|
+
- **Event System** - React-friendly session change notifications.
|
|
15
|
+
- **Server-side Compatible** - Works in Node.js with custom storage.
|
|
16
|
+
|
|
17
|
+
## Auth client API surface
|
|
18
|
+
|
|
19
|
+
The auth client exposes the following methods for sign-up, sign-in, session management, and lifecycle events:
|
|
20
|
+
|
|
21
|
+
| Method | Description |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `signUp({ email, password })` | Register a new user. |
|
|
24
|
+
| `signIn({ email, password })` | Authenticate an existing user. |
|
|
25
|
+
| `signOut()` | End the current session. |
|
|
26
|
+
| `getSession()` | Return the current session (opaque; check `isAuthenticated` or `user`). |
|
|
27
|
+
| `onSessionChange(callback)` | Subscribe to session state changes; returns an unsubscribe function. |
|
|
28
|
+
|
|
29
|
+
> **Important**
|
|
30
|
+
> The method name is `onSessionChange`, not `onAuthStateChange`.
|
|
31
|
+
> `onAuthStateChange` does not exist on Rayfin's auth client.
|
|
32
|
+
|
|
33
|
+
Session objects are opaque.
|
|
34
|
+
Gate UI logic on the `isAuthenticated` flag or the presence of a `user` property rather than inspecting internal session fields.
|
|
35
|
+
|
|
36
|
+
Restart the backend (`npx rayfin up`) whenever you enable or disable auth in `rayfin.yml` so the correct endpoints are exposed.
|
|
37
|
+
|
|
38
|
+
## Configure auth settings
|
|
39
|
+
|
|
40
|
+
The `rayfin.yml` can be used to configure your authentication service.
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
services:
|
|
46
|
+
auth:
|
|
47
|
+
enabled: true
|
|
48
|
+
allowedRedirectUris:
|
|
49
|
+
- http://localhost:5173
|
|
50
|
+
customClaims:
|
|
51
|
+
tenant: default
|
|
52
|
+
app_version: 1.0.0
|
|
53
|
+
scopes:
|
|
54
|
+
- read:data
|
|
55
|
+
- write:data
|
|
56
|
+
password:
|
|
57
|
+
enabled: true
|
|
58
|
+
fabric:
|
|
59
|
+
enabled: false
|
|
60
|
+
email:
|
|
61
|
+
enabled: false
|
|
62
|
+
provider: smtp
|
|
63
|
+
senderName: Rayfin Platform
|
|
64
|
+
verificationTokenExpirationHours: 24
|
|
65
|
+
passwordResetTokenExpirationMinutes: 30
|
|
66
|
+
smtp:
|
|
67
|
+
host: localhost
|
|
68
|
+
port: 1025
|
|
69
|
+
senderEmail: noreply@rayfin.local
|
|
70
|
+
username: ""
|
|
71
|
+
password: ""
|
|
72
|
+
useSsl: false
|
|
73
|
+
useStartTls: false
|
|
74
|
+
webPort: 1080
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- Set `services.auth.enabled` to `true` to expose Rayfin managed auth endpoints.
|
|
78
|
+
- Define any custom claims that your frontend will read from the session (tenant, roles, release channels, and so on).
|
|
79
|
+
- Toggle `email.enabled` to `true` plus SMTP metadata if you want verification or password reset flows.
|
|
80
|
+
- Set `fabric.enabled` to `true` to enable Fabric brokered authentication (Entra SSO through the Fabric Portal).
|
|
81
|
+
See [Fabric Brokered Auth](./fabric.md) for the full setup and client-side integration guide.
|
|
82
|
+
- Restart `npx rayfin up` whenever this file changes so configuration is reloaded.
|
|
83
|
+
|
|
84
|
+
## Initialize auth
|
|
85
|
+
|
|
86
|
+
1. Import RayfinClient and instantiate it with your backend base URL plus the publishable key so API calls route to the correct Rayfin environment.
|
|
87
|
+
2. Call `client.auth.signIn({ email, password })` to authenticate the current user; Rayfin manages session cookies and tokens internally.
|
|
88
|
+
3. Retrieve the active session via `client.auth.getSession()`; checking `session.isAuthenticated` or `session.user` lets you gate UI logic.
|
|
89
|
+
4. Once signed in, the same client instance automatically attaches the auth context to data calls, so you can call `client.data.Todo.getAll()` (or any other entity) without re-supplying credentials.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { RayfinClient } from '@microsoft/rayfin-client';
|
|
93
|
+
|
|
94
|
+
const client = new RayfinClient({
|
|
95
|
+
baseUrl: 'http://localhost:5168',
|
|
96
|
+
publishableKey: 'pk-commonSampleAppKey',
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await client.auth.signIn({ email, password });
|
|
100
|
+
const session = client.auth.getSession();
|
|
101
|
+
|
|
102
|
+
// Data API automatically authenticated
|
|
103
|
+
await client.data.Todo.getAll();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Using custom hook for React
|
|
107
|
+
|
|
108
|
+
That hook keeps React in sync with the Rayfin auth session.
|
|
109
|
+
|
|
110
|
+
- Grab the current session once on mount via `auth.getSession()` and store it in component state so the UI can react to updates.
|
|
111
|
+
- Register `auth.onSessionChange(setSession)` inside `useEffect` to subscribe to Rayfin’s session events; anytime the backend refreshes or invalidates the session, your state updates automatically.
|
|
112
|
+
- Because everything funnels through one hook, any component can read `useAuth()` to gate routes, show user info, or trigger login/logout flows with minimal boilerplate.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { useState, useEffect } from 'react';
|
|
116
|
+
import { auth } from './lib/rayfin';
|
|
117
|
+
|
|
118
|
+
export function useAuth() {
|
|
119
|
+
const [session, setSession] = useState(auth.getSession());
|
|
120
|
+
|
|
121
|
+
useEffect(() => auth.onSessionChange(setSession), []);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
...session,
|
|
125
|
+
isAuthenticated: session?.isAuthenticated ?? false,
|
|
126
|
+
signIn: auth.signIn.bind(auth),
|
|
127
|
+
signOut: auth.signOut.bind(auth),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
```
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 5
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Agent files (`rayfin init ai-files`)
|
|
6
|
+
|
|
7
|
+
A "Rayfin agent file" is a piece of context that AI coding agents — Copilot CLI, GitHub Copilot in VS Code, Claude Code, Cursor, Gemini CLI, Codex, and friends — read on every task to know how to work with your project.
|
|
8
|
+
|
|
9
|
+
The Rayfin CLI installs three of them in your project and keeps them in sync as the platform evolves:
|
|
10
|
+
|
|
11
|
+
| File | Purpose | Who reads it |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| `AGENTS.md` | Universal cross-agent instructions for your project. Plain English; lives at the project root. | Almost every modern coding agent. |
|
|
14
|
+
| `.mcp.json` | Wires up the Rayfin MCP doc-lookup server so the agent can search Rayfin docs at task time. The CLI manages just the `mcpServers.rayfin` key — your other servers are preserved. | GitHub Copilot, VS Code Copilot, Claude Code, Cursor (some flavors). |
|
|
15
|
+
| `.agents/skills/rayfin/SKILL.md` | A "Skill" that names Rayfin-specific rules and anti-patterns (decorators, permission rules, MSSQL constraints, deployment flow). | GitHub Copilot CLI, VS Code Copilot, Codex. |
|
|
16
|
+
|
|
17
|
+
If you are new to coding agents, the short version is: **these three files together teach any agent how to work in a Rayfin project.** You drop them in once, they update with `rayfin-cli`, and you stop having to paste the same Rayfin context into chat over and over.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
When you scaffold a project with `npm create @microsoft/rayfin@latest` or `rayfin init`, the CLI installs the agent files automatically as part of the post-scaffold pipeline. You usually do not need to run anything by hand.
|
|
22
|
+
|
|
23
|
+
To install them in an existing project (or refresh them after upgrading the CLI):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx rayfin init ai-files install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
You will get an interactive checkbox picker the first time. Pass `--non-interactive` (or `--yes`) to accept defaults, or `--enable`/`--disable` to script it.
|
|
30
|
+
|
|
31
|
+
To see what is installed and whether anything has drifted:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx rayfin init ai-files status
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
To preview what `install` would do without touching disk:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx rayfin init ai-files install -n --json
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## What about my other agent config?
|
|
44
|
+
|
|
45
|
+
The CLI is a careful neighbor:
|
|
46
|
+
|
|
47
|
+
- `.mcp.json` is merged at the key level. Your other `mcpServers.<name>` entries are preserved on every install.
|
|
48
|
+
- `AGENTS.md` is **one-time install**. If you (or your template) already wrote one, the CLI never overwrites it — even with `--force`. Add Rayfin-specific rules to it freely.
|
|
49
|
+
- `.agents/skills/rayfin/SKILL.md` carries a `rayfin-managed: true` frontmatter sigil. If you remove that sigil, the CLI stops managing the file. That is the documented opt-out gesture for skills you want to fully customize.
|
|
50
|
+
|
|
51
|
+
If you have hand-edited a managed file and do not want to lose your changes, the CLI flags it as `user-modified` and preserves your version with a warning. Choose either path:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Keep your version, stop managing this item
|
|
55
|
+
npx rayfin init ai-files install --disable skill:rayfin
|
|
56
|
+
|
|
57
|
+
# Throw away your edits, accept the bundled version
|
|
58
|
+
npx rayfin init ai-files install --force
|
|
59
|
+
|
|
60
|
+
# Reset just one item (overwrite mcp; keep your edits to skill)
|
|
61
|
+
npx rayfin init ai-files install --force mcp:rayfin
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Command reference
|
|
65
|
+
|
|
66
|
+
### `rayfin init ai-files install`
|
|
67
|
+
|
|
68
|
+
Installs or refreshes the Rayfin agent files. **Idempotent** — re-running auto-reconciles the project to the bundled content for your current CLI version. Re-running on an up-to-date project is a no-op (no disk writes).
|
|
69
|
+
|
|
70
|
+
| Flag | Behavior |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| `--enable <id>` (repeatable) | Install/keep a specific item by its namespaced id (e.g. `--enable skill:rayfin`). Unknown ids are rejected with a clear error. |
|
|
73
|
+
| `--disable <id>` (repeatable) | Stop managing a specific item — record the choice but do not delete the on-disk file. Also accepts orphan ids (items the lockfile remembers but the current CLI no longer ships). |
|
|
74
|
+
| `--remove-files` | Modifier for `--disable`. Also removes the on-disk file. Cannot be passed alone. |
|
|
75
|
+
| `--force [ids...]` | Overwrite items that have been hand-edited (`user-modified`), restore items that were deleted (`missing`), or rebuild after a malformed `.mcp.json`. Pass with no args to apply to every managed item. Pass one or more namespaced ids (e.g. `--force mcp:rayfin`) to scope force to those items only — others use default behavior. **Never** overwrites `AGENTS.md`. |
|
|
76
|
+
| `--json` | Emit a `{status, schemaVersion, dryRun, report}` envelope to stdout instead of human progress lines. Implies non-interactive. |
|
|
77
|
+
| `-n, --dry-run` | Classify what install would do and emit the report — no disk writes. Pairs with `--json` for previewability in scripts. |
|
|
78
|
+
| `-y, --yes` / `--non-interactive` | Skip the interactive prompt and accept defaults. |
|
|
79
|
+
|
|
80
|
+
#### Exit codes
|
|
81
|
+
|
|
82
|
+
| Code | Meaning |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| `0` | Success, no warnings |
|
|
85
|
+
| `1` | Hard error (invalid args, unknown id, malformed lockfile, write failure that was not isolated) |
|
|
86
|
+
| `3` | Success, but warnings present (`user-modified` items preserved, `unreadable` sibling files, etc.). Distinct from `1` so agent consumers can tell "you should look at this" from "the command failed." |
|
|
87
|
+
|
|
88
|
+
### `rayfin init ai-files status`
|
|
89
|
+
|
|
90
|
+
Prints one line per managed item with its current state. Pass `--json` for `{status, schemaVersion, items: ItemStatus[]}` on stdout.
|
|
91
|
+
|
|
92
|
+
States:
|
|
93
|
+
|
|
94
|
+
| State | Meaning |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| `up-to-date` | On disk; sha matches lockfile and bundled content. Nothing to do. |
|
|
97
|
+
| `update-available` | On disk; sha matches lockfile but the CLI now ships different bundled content. Run `install` to refresh. |
|
|
98
|
+
| `user-modified` | On disk but the file hash does not match what the CLI last wrote. The CLI assumes you edited it intentionally. `install --force <id>` overwrites just that item; `install --disable <id>` keeps your version and stops managing. |
|
|
99
|
+
| `missing` | The CLI installed it once, but the file is gone now. `install --force <id>` re-installs just that item. |
|
|
100
|
+
| `not-installed` | The CLI knows about this item but it is not installed in the project yet. Plain `install` installs it. |
|
|
101
|
+
| `disabled` | You opted out of managing this item (either via `install --disable <id>` or by removing the `rayfin-managed: true` sigil from a skill frontmatter). `install --enable <id>` re-enables. |
|
|
102
|
+
| `orphaned` | The lockfile remembers an item the current CLI no longer ships. `install` cleans it up if it has not been touched, or warns if you have edited it. |
|
|
103
|
+
| `unreadable` | The on-disk file exists but is malformed (e.g. invalid JSON in `.mcp.json`). Repair it by hand, or `install --force <id>` to rebuild just that item. |
|
|
104
|
+
|
|
105
|
+
## How conflicts are resolved
|
|
106
|
+
|
|
107
|
+
The CLI tracks what it wrote in a project-tracked lockfile at `rayfin/.lockfile.json` so re-runs know what they last installed and don't re-do work. Commit the lockfile so your team shares the same baseline.
|
|
108
|
+
|
|
109
|
+
When you re-run `install`, the CLI compares the on-disk content against what it last wrote and against the bundled content for your current CLI version. That gives one of the eight states above. The conflict policy is:
|
|
110
|
+
|
|
111
|
+
| State | Default | With `--force` |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| `not-installed` | install | install |
|
|
114
|
+
| `up-to-date` | no-op | no-op |
|
|
115
|
+
| `update-available` | rewrite | rewrite |
|
|
116
|
+
| `user-modified` | warn, preserve user content | overwrite with bundled |
|
|
117
|
+
| `missing` | warn | re-install |
|
|
118
|
+
| `disabled` (lockfile flag) | skip | skip — `--force` alone will not re-enable; pass `--enable <id>` |
|
|
119
|
+
| `disabled` (sigil-removed skill) | skip | skip — pass `--enable <id> --force` to re-stamp the sigil |
|
|
120
|
+
| `unreadable` | warn | overwrite (rebuilds from scratch) |
|
|
121
|
+
| `orphaned` (clean) | delete (lockfile + disk) | delete |
|
|
122
|
+
| `orphaned` (dirty) | warn, preserve | delete |
|
|
123
|
+
| `orphaned` + `--disable <id>` | mark disabled, preserve file (does NOT enter cleanup) | mark disabled, preserve file |
|
|
124
|
+
|
|
125
|
+
`--force` accepts an optional list of ids: `install --force mcp:rayfin` overwrites only `mcp:rayfin` and leaves the other items on default behavior. Useful when you want to refresh one file without losing intentional edits to others.
|
|
126
|
+
|
|
127
|
+
When `install` warns about a `user-modified`, `missing`, or `unreadable` item, the warning recommends the per-item form (e.g. `rayfin init ai-files install --force skill:rayfin`). Following the warning's instruction will only touch that one item — unrelated user-modified items in the same project are not affected.
|
|
128
|
+
|
|
129
|
+
## Drift nudge in `rayfin up`
|
|
130
|
+
|
|
131
|
+
On every `rayfin up` startup, the CLI prints a one-line nudge if any managed item is out of date, modified, missing, unreadable, or newly-shipped. Refresh with `rayfin init ai-files install`. Upgrading `@microsoft/rayfin-cli` to a version that ships identical content does not produce a nudge — the check is content-based, not version-based.
|
|
132
|
+
|
|
133
|
+
## Scripting and CI
|
|
134
|
+
|
|
135
|
+
For agent or CI consumers, the structured `--json` output and exit code 3 (warning-only success) are first-class:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Check if anything would change without writing
|
|
139
|
+
npx rayfin init ai-files install -n --json
|
|
140
|
+
|
|
141
|
+
# Idempotent install in CI; warnings exit 3, hard errors exit 1
|
|
142
|
+
npx rayfin init ai-files install --yes --json
|
|
143
|
+
|
|
144
|
+
# Inspect current state without writes
|
|
145
|
+
npx rayfin init ai-files status --json
|
|
146
|
+
```
|