@microsoft/rayfin-auth-provider-fabric 1.34.0-beta.0 → 1.34.0
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/dist/brokerUrl.d.ts +8 -6
- package/dist/brokerUrl.js +19 -20
- package/dist/initiateFabricLogin.d.ts +7 -4
- package/dist/initiateFabricLogin.js +51 -18
- package/package.json +4 -4
package/dist/brokerUrl.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { FabricAuthOptions } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
* Constructs the broker launch URL
|
|
3
|
+
* Constructs the broker launch URL for the chromeless SecureItemEmbed view.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Uses the SecureItemEmbed path so the popup renders without Fabric portal
|
|
6
|
+
* chrome (navigation, ribbon). Preserves any existing query parameters on
|
|
7
|
+
* `fabricPortalUrl` and adds artifact identification + extensionPath.
|
|
8
|
+
*
|
|
9
|
+
* PKCE parameters are no longer included in the URL — they are delivered to
|
|
10
|
+
* the extension via the postMessage challenge flow (`brokeredAuth.challenge`).
|
|
7
11
|
*
|
|
8
12
|
* @param options - Auth options (workspace/project IDs, portal URL).
|
|
9
|
-
* @param codeChallenge - PKCE S256 code challenge.
|
|
10
|
-
* @param state - CSRF/correlation state nonce.
|
|
11
13
|
* @returns Fully constructed broker URL.
|
|
12
14
|
*/
|
|
13
|
-
export declare function buildBrokerUrl(options: FabricAuthOptions
|
|
15
|
+
export declare function buildBrokerUrl(options: FabricAuthOptions): string;
|
|
14
16
|
//# sourceMappingURL=brokerUrl.d.ts.map
|
package/dist/brokerUrl.js
CHANGED
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Constructs the broker launch URL
|
|
2
|
+
* Constructs the broker launch URL for the chromeless SecureItemEmbed view.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Uses the SecureItemEmbed path so the popup renders without Fabric portal
|
|
5
|
+
* chrome (navigation, ribbon). Preserves any existing query parameters on
|
|
6
|
+
* `fabricPortalUrl` and adds artifact identification + extensionPath.
|
|
7
|
+
*
|
|
8
|
+
* PKCE parameters are no longer included in the URL — they are delivered to
|
|
9
|
+
* the extension via the postMessage challenge flow (`brokeredAuth.challenge`).
|
|
6
10
|
*
|
|
7
11
|
* @param options - Auth options (workspace/project IDs, portal URL).
|
|
8
|
-
* @param codeChallenge - PKCE S256 code challenge.
|
|
9
|
-
* @param state - CSRF/correlation state nonce.
|
|
10
12
|
* @returns Fully constructed broker URL.
|
|
11
13
|
*/
|
|
12
|
-
export function buildBrokerUrl(options
|
|
14
|
+
export function buildBrokerUrl(options) {
|
|
13
15
|
const portalUrl = new URL(options.fabricPortalUrl);
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// AppBackend versions that detect broker mode via parseBrokerParams.
|
|
16
|
+
// Use the chromeless SecureItemEmbed path instead of the full portal
|
|
17
|
+
// deep-link so the popup renders without Fabric navigation chrome.
|
|
17
18
|
const basePath = portalUrl.pathname.replace(/\/$/, '');
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
portalUrl.searchParams.set('
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
portalUrl.searchParams.set('
|
|
27
|
-
portalUrl.searchParams.set('code_challenge_method', 'S256');
|
|
28
|
-
portalUrl.searchParams.set('state', state);
|
|
19
|
+
portalUrl.pathname = `${basePath}/secureItemEmbed`;
|
|
20
|
+
// Artifact identification for SecureItemEmbed
|
|
21
|
+
portalUrl.searchParams.set('workspaceId', options.workspaceId);
|
|
22
|
+
portalUrl.searchParams.set('itemType', 'AppBackend');
|
|
23
|
+
portalUrl.searchParams.set('itemId', options.projectId);
|
|
24
|
+
// Route the extension to the dedicated BrokeredAuthPage.
|
|
25
|
+
// PKCE parameters are delivered via postMessage (brokeredAuth.challenge),
|
|
26
|
+
// not through the URL, so only the path is needed here.
|
|
27
|
+
portalUrl.searchParams.set('extensionPath', '/brokeredauth');
|
|
29
28
|
return portalUrl.toString();
|
|
30
29
|
}
|
|
31
30
|
//# sourceMappingURL=brokerUrl.js.map
|
|
@@ -5,14 +5,17 @@ import type { FabricAuthOptions } from './types.js';
|
|
|
5
5
|
*
|
|
6
6
|
* **Must be called inside a user click handler** to avoid popup blockers.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* `
|
|
10
|
-
*
|
|
8
|
+
* Opens the popup synchronously in the gesture (Safari/iOS block `window.open`
|
|
9
|
+
* after an `await`) and delivers the PKCE challenge via the
|
|
10
|
+
* `brokeredAuth.challenge` postMessage. On `brokeredAuth.handoff`, exchanges
|
|
11
|
+
* the code for tokens and creates a session. `code_verifier` stays in closure.
|
|
11
12
|
*
|
|
12
13
|
* @param auth - Auth instance for token exchange.
|
|
13
14
|
* @param options - Broker auth options (`returnOrigin` required).
|
|
14
15
|
* @returns Resolves when the session is established.
|
|
15
|
-
* @throws `AuthError` - On missing options, blocked popup,
|
|
16
|
+
* @throws `AuthError` - On missing options, blocked popup, PKCE preparation or
|
|
17
|
+
* delivery failure (`PKCE_CHALLENGE_FAILED`, `CHALLENGE_DELIVERY_FAILED`), or
|
|
18
|
+
* auth failure.
|
|
16
19
|
*/
|
|
17
20
|
export declare function initiateFabricLogin(auth: Auth, options: FabricAuthOptions): Promise<void>;
|
|
18
21
|
//# sourceMappingURL=initiateFabricLogin.d.ts.map
|
|
@@ -8,14 +8,17 @@ import { buildBrokerUrl } from './brokerUrl.js';
|
|
|
8
8
|
*
|
|
9
9
|
* **Must be called inside a user click handler** to avoid popup blockers.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
11
|
+
* Opens the popup synchronously in the gesture (Safari/iOS block `window.open`
|
|
12
|
+
* after an `await`) and delivers the PKCE challenge via the
|
|
13
|
+
* `brokeredAuth.challenge` postMessage. On `brokeredAuth.handoff`, exchanges
|
|
14
|
+
* the code for tokens and creates a session. `code_verifier` stays in closure.
|
|
14
15
|
*
|
|
15
16
|
* @param auth - Auth instance for token exchange.
|
|
16
17
|
* @param options - Broker auth options (`returnOrigin` required).
|
|
17
18
|
* @returns Resolves when the session is established.
|
|
18
|
-
* @throws `AuthError` - On missing options, blocked popup,
|
|
19
|
+
* @throws `AuthError` - On missing options, blocked popup, PKCE preparation or
|
|
20
|
+
* delivery failure (`PKCE_CHALLENGE_FAILED`, `CHALLENGE_DELIVERY_FAILED`), or
|
|
21
|
+
* auth failure.
|
|
19
22
|
*/
|
|
20
23
|
export async function initiateFabricLogin(auth, options) {
|
|
21
24
|
assertBrowser('initiateFabricLogin');
|
|
@@ -32,12 +35,17 @@ export async function initiateFabricLogin(auth, options) {
|
|
|
32
35
|
if (!options.fabricPortalUrl) {
|
|
33
36
|
throw new AuthError('fabricPortalUrl is required for Fabric authentication.', 'MISSING_FABRIC_PORTAL_URL');
|
|
34
37
|
}
|
|
35
|
-
//
|
|
38
|
+
// Generate verifier + state synchronously so the popup can open in the user
|
|
39
|
+
// gesture below. The S256 challenge is computed async (off the click path)
|
|
40
|
+
// and delivered later via the `brokeredAuth.challenge` postMessage —
|
|
41
|
+
// awaiting before `window.open` would trip Safari/iOS popup blocking.
|
|
36
42
|
const codeVerifier = generateCodeVerifier();
|
|
37
|
-
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
38
43
|
const state = generateState();
|
|
44
|
+
const codeChallengePromise = generateCodeChallenge(codeVerifier);
|
|
45
|
+
// Mark rejection handled for paths that skip handleReady; real handling is there.
|
|
46
|
+
codeChallengePromise.catch(() => { });
|
|
39
47
|
const expectedBrokerOrigin = new URL(options.fabricPortalUrl).origin;
|
|
40
|
-
const brokerUrl = buildBrokerUrl(options
|
|
48
|
+
const brokerUrl = buildBrokerUrl(options);
|
|
41
49
|
return new Promise((resolve, reject) => {
|
|
42
50
|
let fabricWindow = null;
|
|
43
51
|
let channel = null;
|
|
@@ -118,7 +126,7 @@ export async function initiateFabricLogin(auth, options) {
|
|
|
118
126
|
* cross-origin iframe inside the popup — `postMessage` sent to the
|
|
119
127
|
* popup's top-level window does not propagate to child iframes.
|
|
120
128
|
*/
|
|
121
|
-
function handleReady(event) {
|
|
129
|
+
async function handleReady(event) {
|
|
122
130
|
if (readyHandled)
|
|
123
131
|
return;
|
|
124
132
|
if (!event.data || typeof event.data !== 'object')
|
|
@@ -132,6 +140,18 @@ export async function initiateFabricLogin(auth, options) {
|
|
|
132
140
|
}
|
|
133
141
|
readyHandled = true;
|
|
134
142
|
window.removeEventListener('message', handleReady);
|
|
143
|
+
// Challenge computed off the click path; await it here (almost always
|
|
144
|
+
// already resolved by the time the broker signals ready).
|
|
145
|
+
let codeChallenge;
|
|
146
|
+
try {
|
|
147
|
+
codeChallenge = await codeChallengePromise;
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
console.warn('[FabricAuth] Failed to compute PKCE code challenge', err);
|
|
151
|
+
cleanup();
|
|
152
|
+
reject(new AuthError('Failed to prepare Fabric authentication.', 'PKCE_CHALLENGE_FAILED'));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
135
155
|
const challengePayload = {
|
|
136
156
|
type: 'brokeredAuth.challenge',
|
|
137
157
|
returnOrigin: options.returnOrigin,
|
|
@@ -139,16 +159,29 @@ export async function initiateFabricLogin(auth, options) {
|
|
|
139
159
|
codeChallengeMethod: 'S256',
|
|
140
160
|
state,
|
|
141
161
|
};
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
162
|
+
// Deliver the challenge. Guard postMessage: an opaque/sandboxed sender
|
|
163
|
+
// reports event.origin === 'null' (an invalid targetOrigin that throws),
|
|
164
|
+
// and this listener is async so a throw is swallowed by the DOM rather
|
|
165
|
+
// than rejecting the outer promise — the flow would hang until timeout.
|
|
166
|
+
try {
|
|
167
|
+
// Normalize 'null'/empty origin to '*'; valid origins pass through.
|
|
168
|
+
const targetOrigin = event.origin && event.origin !== 'null' ? event.origin : '*';
|
|
169
|
+
// Reply to the window that sent the ready signal (the extension iframe).
|
|
170
|
+
const source = event.source;
|
|
171
|
+
if (source) {
|
|
172
|
+
source.postMessage(challengePayload, targetOrigin);
|
|
173
|
+
console.debug(`[FabricAuth] Sent brokeredAuth.challenge to event.source (origin=${targetOrigin})`);
|
|
174
|
+
}
|
|
175
|
+
else if (fabricWindow && !fabricWindow.closed) {
|
|
176
|
+
// Fallback when event.source is unavailable: post to the popup.
|
|
177
|
+
fabricWindow.postMessage(challengePayload, expectedBrokerOrigin);
|
|
178
|
+
console.debug('[FabricAuth] Sent brokeredAuth.challenge to fabricWindow (event.source unavailable)');
|
|
179
|
+
}
|
|
147
180
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
181
|
+
catch (err) {
|
|
182
|
+
console.warn('[FabricAuth] Failed to deliver PKCE challenge', err);
|
|
183
|
+
cleanup();
|
|
184
|
+
reject(new AuthError('Failed to deliver Fabric authentication challenge.', 'CHALLENGE_DELIVERY_FAILED'));
|
|
152
185
|
}
|
|
153
186
|
}
|
|
154
187
|
// Listen for brokeredAuth.ready from the popup (postMessage PKCE).
|
|
@@ -164,7 +197,7 @@ export async function initiateFabricLogin(auth, options) {
|
|
|
164
197
|
catch {
|
|
165
198
|
console.debug('[FabricAuth] BroadcastChannel not available — relying on postMessage only');
|
|
166
199
|
}
|
|
167
|
-
// Open broker
|
|
200
|
+
// Open the chromeless SecureItemEmbed broker URL in a centered popup.
|
|
168
201
|
const popupWidth = 600;
|
|
169
202
|
const popupHeight = 700;
|
|
170
203
|
const left = Math.round(window.screenX + (window.outerWidth - popupWidth) / 2);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@microsoft/rayfin-auth-provider-fabric",
|
|
3
|
-
"version": "1.34.0
|
|
3
|
+
"version": "1.34.0",
|
|
4
4
|
"description": "Fabric brokered authentication provider for Rayfin SDK",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
],
|
|
14
14
|
"type": "module",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@microsoft/rayfin-auth": "1.34.0
|
|
17
|
-
"@microsoft/fabric-embedded-host": "1.34.0
|
|
18
|
-
"@microsoft/rayfin-lib": "1.34.0
|
|
16
|
+
"@microsoft/rayfin-auth": "1.34.0",
|
|
17
|
+
"@microsoft/fabric-embedded-host": "1.34.0",
|
|
18
|
+
"@microsoft/rayfin-lib": "1.34.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"typescript": "^5.8.3",
|