@oxyhq/services 12.1.0 → 12.1.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/lib/commonjs/ui/context/OxyContext.js +7 -8
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/inSessionTokenRefresh.js +66 -90
- package/lib/commonjs/ui/context/inSessionTokenRefresh.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +7 -8
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/inSessionTokenRefresh.js +67 -91
- package/lib/module/ui/context/inSessionTokenRefresh.js.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts +27 -31
- package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts +27 -31
- package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ui/context/OxyContext.tsx +7 -8
- package/src/ui/context/inSessionTokenRefresh.ts +75 -90
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
*
|
|
16
16
|
* 1. {@link createInSessionRefreshHandler} — an `AuthRefreshHandler` installed
|
|
17
17
|
* on the owner client. It re-mints a fresh access token WITHOUT a page
|
|
18
|
-
* reload by
|
|
19
|
-
*
|
|
20
|
-
* (`createLinkedClient`) inherits the fix for
|
|
21
|
-
* back to the owner's `refreshAccessToken`.
|
|
18
|
+
* reload by composing the SAME silent-restore primitives cold boot uses
|
|
19
|
+
* ({@link mintSessionViaPerApexIframe}, {@link selectActiveRefreshAccount}) —
|
|
20
|
+
* not a copy. The linked client (`createLinkedClient`) inherits the fix for
|
|
21
|
+
* free: its refresh delegates back to the owner's `refreshAccessToken`.
|
|
22
22
|
*
|
|
23
23
|
* 2. {@link startTokenRefreshScheduler} — a proactive scheduler that refreshes
|
|
24
24
|
* ~{@link TOKEN_REFRESH_LEAD_MS} before expiry (and on web tab-focus / native
|
|
@@ -42,40 +42,35 @@ export declare const TOKEN_REFRESH_LEAD_MS = 60000;
|
|
|
42
42
|
/**
|
|
43
43
|
* Build the in-session `AuthRefreshHandler` for the owner client.
|
|
44
44
|
*
|
|
45
|
-
* Arms
|
|
46
|
-
*
|
|
47
|
-
* it
|
|
45
|
+
* Arms run in an explicit order; the first to plant a token wins. Each arm
|
|
46
|
+
* resolves to the planted token (read back via `getAccessToken()` — the
|
|
47
|
+
* underlying SDK call plants it internally) or `null`. A throw in one arm is
|
|
48
|
+
* logged and treated as `null` so the chain continues.
|
|
48
49
|
*
|
|
49
50
|
* NATIVE (Expo): shared cross-app identity key re-mint
|
|
50
51
|
* (`signInWithSharedIdentity` → challenge→sign→verify plants the tokens).
|
|
51
52
|
* The ONLY silent native arm (mirrors cold boot's `shared-key-signin`); the
|
|
52
|
-
* `/auth/silent` web iframe is NEVER attempted on native.
|
|
53
|
+
* `/auth/silent` web iframe is NEVER attempted on native. Resolves to `null`
|
|
53
54
|
* when the device holds no shared identity (e.g. a password-only native
|
|
54
55
|
* sign-in) so a genuinely dead session reconciles to logged-out rather than
|
|
55
56
|
* staying a zombie.
|
|
56
57
|
*
|
|
57
58
|
* WEB, in order:
|
|
58
|
-
* 1.
|
|
59
|
-
* (
|
|
60
|
-
*
|
|
61
|
-
* `fedcm_session` cookie on `auth.<apex>` and mints a fresh Oxy token. No
|
|
62
|
-
* top-level navigation → works in a backgrounded tab. On a `*.oxy.so`
|
|
63
|
-
* app the per-apex host IS the central host, so this also covers
|
|
64
|
-
* same-apex.
|
|
59
|
+
* 1. Per-apex `/auth/silent` iframe — {@link mintSessionViaPerApexIframe}
|
|
60
|
+
* (shared verbatim with cold boot). The durable cross-apex path; also
|
|
61
|
+
* covers `*.oxy.so` (the per-apex host IS the central host there).
|
|
65
62
|
* 2. FedCM silent re-auth (Chrome) — `silentSignInWithFedCM`.
|
|
66
|
-
* 3. Same-apex refresh cookie — `refreshAllSessions
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* bearer.
|
|
63
|
+
* 3. Same-apex refresh cookie — `refreshAllSessions` + the shared
|
|
64
|
+
* {@link selectActiveRefreshAccount}; plant the active account's rotated
|
|
65
|
+
* token. On a cross-apex RP it returns `{accounts:[]}` (clean no-op).
|
|
66
|
+
* Unlike the cold-boot cookie restore this does NOT rebuild multi-session
|
|
67
|
+
* state — an in-session refresh only needs a fresh bearer.
|
|
72
68
|
*
|
|
73
|
-
* NO RECURSION:
|
|
69
|
+
* NO RECURSION: no arm issues a request through the authed client's
|
|
74
70
|
* `refreshAccessToken` path. The iframe/FedCM transports are postMessage /
|
|
75
71
|
* credential APIs; the follow-up `/session/user` fetch inside `silentSignIn`
|
|
76
72
|
* runs against the just-planted FULL-TTL token (≫ the preflight lead), so it
|
|
77
|
-
* never re-enters the refresh path; `refreshAllSessions` uses a raw `fetch
|
|
78
|
-
* `credentials:'include'`.
|
|
73
|
+
* never re-enters the refresh path; `refreshAllSessions` uses a raw `fetch`.
|
|
79
74
|
*/
|
|
80
75
|
export declare function createInSessionRefreshHandler(oxyServices: OxyServices): AuthRefreshHandler;
|
|
81
76
|
/**
|
|
@@ -92,14 +87,15 @@ export interface TokenRefreshSchedulerHandle {
|
|
|
92
87
|
* current access token's `exp`, calling `httpService.refreshAccessToken`
|
|
93
88
|
* ('preflight') — which runs the installed handler and is deduped + cooldown-
|
|
94
89
|
* guarded. After every attempt it reschedules from the (possibly rotated) token.
|
|
95
|
-
* It also reschedules whenever the token changes (`onTokensChanged`
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* expired on return).
|
|
90
|
+
* It also reschedules whenever the token changes (`onTokensChanged` — so a
|
|
91
|
+
* sign-out that clears the token cancels the timer) and, on web tab-focus /
|
|
92
|
+
* native app-foreground, refreshes immediately if already inside the lead window
|
|
93
|
+
* (a long-hidden tab throttles timers, so the token can be expired on return).
|
|
99
94
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* path
|
|
95
|
+
* The `exp` is derived directly from the JWT via `getAccessTokenExpiry()`. No-ops
|
|
96
|
+
* cleanly when there is no token, an opaque/no-`exp` token, or the host lacks
|
|
97
|
+
* `getAccessTokenExpiry` (older stubs) — the reactive 401 path stays the only
|
|
98
|
+
* refresh trigger in those cases.
|
|
103
99
|
*/
|
|
104
100
|
export declare function startTokenRefreshScheduler(oxyServices: OxyServices): TokenRefreshSchedulerHandle;
|
|
105
101
|
//# sourceMappingURL=inSessionTokenRefresh.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inSessionTokenRefresh.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/inSessionTokenRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAEnB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"inSessionTokenRefresh.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/inSessionTokenRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAEnB,MAAM,aAAa,CAAC;AAyBrB;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAQ5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,WAAW,GAAG,kBAAkB,CAsD1F;AAED;;;GAGG;AACH,MAAM,WAAW,2BAA2B;IAC1C,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,WAAW,GAAG,2BAA2B,CA4FhG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"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"}
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
*
|
|
16
16
|
* 1. {@link createInSessionRefreshHandler} — an `AuthRefreshHandler` installed
|
|
17
17
|
* on the owner client. It re-mints a fresh access token WITHOUT a page
|
|
18
|
-
* reload by
|
|
19
|
-
*
|
|
20
|
-
* (`createLinkedClient`) inherits the fix for
|
|
21
|
-
* back to the owner's `refreshAccessToken`.
|
|
18
|
+
* reload by composing the SAME silent-restore primitives cold boot uses
|
|
19
|
+
* ({@link mintSessionViaPerApexIframe}, {@link selectActiveRefreshAccount}) —
|
|
20
|
+
* not a copy. The linked client (`createLinkedClient`) inherits the fix for
|
|
21
|
+
* free: its refresh delegates back to the owner's `refreshAccessToken`.
|
|
22
22
|
*
|
|
23
23
|
* 2. {@link startTokenRefreshScheduler} — a proactive scheduler that refreshes
|
|
24
24
|
* ~{@link TOKEN_REFRESH_LEAD_MS} before expiry (and on web tab-focus / native
|
|
@@ -42,40 +42,35 @@ export declare const TOKEN_REFRESH_LEAD_MS = 60000;
|
|
|
42
42
|
/**
|
|
43
43
|
* Build the in-session `AuthRefreshHandler` for the owner client.
|
|
44
44
|
*
|
|
45
|
-
* Arms
|
|
46
|
-
*
|
|
47
|
-
* it
|
|
45
|
+
* Arms run in an explicit order; the first to plant a token wins. Each arm
|
|
46
|
+
* resolves to the planted token (read back via `getAccessToken()` — the
|
|
47
|
+
* underlying SDK call plants it internally) or `null`. A throw in one arm is
|
|
48
|
+
* logged and treated as `null` so the chain continues.
|
|
48
49
|
*
|
|
49
50
|
* NATIVE (Expo): shared cross-app identity key re-mint
|
|
50
51
|
* (`signInWithSharedIdentity` → challenge→sign→verify plants the tokens).
|
|
51
52
|
* The ONLY silent native arm (mirrors cold boot's `shared-key-signin`); the
|
|
52
|
-
* `/auth/silent` web iframe is NEVER attempted on native.
|
|
53
|
+
* `/auth/silent` web iframe is NEVER attempted on native. Resolves to `null`
|
|
53
54
|
* when the device holds no shared identity (e.g. a password-only native
|
|
54
55
|
* sign-in) so a genuinely dead session reconciles to logged-out rather than
|
|
55
56
|
* staying a zombie.
|
|
56
57
|
*
|
|
57
58
|
* WEB, in order:
|
|
58
|
-
* 1.
|
|
59
|
-
* (
|
|
60
|
-
*
|
|
61
|
-
* `fedcm_session` cookie on `auth.<apex>` and mints a fresh Oxy token. No
|
|
62
|
-
* top-level navigation → works in a backgrounded tab. On a `*.oxy.so`
|
|
63
|
-
* app the per-apex host IS the central host, so this also covers
|
|
64
|
-
* same-apex.
|
|
59
|
+
* 1. Per-apex `/auth/silent` iframe — {@link mintSessionViaPerApexIframe}
|
|
60
|
+
* (shared verbatim with cold boot). The durable cross-apex path; also
|
|
61
|
+
* covers `*.oxy.so` (the per-apex host IS the central host there).
|
|
65
62
|
* 2. FedCM silent re-auth (Chrome) — `silentSignInWithFedCM`.
|
|
66
|
-
* 3. Same-apex refresh cookie — `refreshAllSessions
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* bearer.
|
|
63
|
+
* 3. Same-apex refresh cookie — `refreshAllSessions` + the shared
|
|
64
|
+
* {@link selectActiveRefreshAccount}; plant the active account's rotated
|
|
65
|
+
* token. On a cross-apex RP it returns `{accounts:[]}` (clean no-op).
|
|
66
|
+
* Unlike the cold-boot cookie restore this does NOT rebuild multi-session
|
|
67
|
+
* state — an in-session refresh only needs a fresh bearer.
|
|
72
68
|
*
|
|
73
|
-
* NO RECURSION:
|
|
69
|
+
* NO RECURSION: no arm issues a request through the authed client's
|
|
74
70
|
* `refreshAccessToken` path. The iframe/FedCM transports are postMessage /
|
|
75
71
|
* credential APIs; the follow-up `/session/user` fetch inside `silentSignIn`
|
|
76
72
|
* runs against the just-planted FULL-TTL token (≫ the preflight lead), so it
|
|
77
|
-
* never re-enters the refresh path; `refreshAllSessions` uses a raw `fetch
|
|
78
|
-
* `credentials:'include'`.
|
|
73
|
+
* never re-enters the refresh path; `refreshAllSessions` uses a raw `fetch`.
|
|
79
74
|
*/
|
|
80
75
|
export declare function createInSessionRefreshHandler(oxyServices: OxyServices): AuthRefreshHandler;
|
|
81
76
|
/**
|
|
@@ -92,14 +87,15 @@ export interface TokenRefreshSchedulerHandle {
|
|
|
92
87
|
* current access token's `exp`, calling `httpService.refreshAccessToken`
|
|
93
88
|
* ('preflight') — which runs the installed handler and is deduped + cooldown-
|
|
94
89
|
* guarded. After every attempt it reschedules from the (possibly rotated) token.
|
|
95
|
-
* It also reschedules whenever the token changes (`onTokensChanged`
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* expired on return).
|
|
90
|
+
* It also reschedules whenever the token changes (`onTokensChanged` — so a
|
|
91
|
+
* sign-out that clears the token cancels the timer) and, on web tab-focus /
|
|
92
|
+
* native app-foreground, refreshes immediately if already inside the lead window
|
|
93
|
+
* (a long-hidden tab throttles timers, so the token can be expired on return).
|
|
99
94
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* path
|
|
95
|
+
* The `exp` is derived directly from the JWT via `getAccessTokenExpiry()`. No-ops
|
|
96
|
+
* cleanly when there is no token, an opaque/no-`exp` token, or the host lacks
|
|
97
|
+
* `getAccessTokenExpiry` (older stubs) — the reactive 401 path stays the only
|
|
98
|
+
* refresh trigger in those cases.
|
|
103
99
|
*/
|
|
104
100
|
export declare function startTokenRefreshScheduler(oxyServices: OxyServices): TokenRefreshSchedulerHandle;
|
|
105
101
|
//# sourceMappingURL=inSessionTokenRefresh.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inSessionTokenRefresh.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/inSessionTokenRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAEnB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"inSessionTokenRefresh.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/inSessionTokenRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAEnB,MAAM,aAAa,CAAC;AAyBrB;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAQ5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,WAAW,GAAG,kBAAkB,CAsD1F;AAED;;;GAGG;AACH,MAAM,WAAW,2BAA2B;IAC1C,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,WAAW,GAAG,2BAA2B,CA4FhG"}
|
package/package.json
CHANGED
|
@@ -1418,14 +1418,13 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
|
|
|
1418
1418
|
// step that prevents the re-bounce loop: when it finds a session,
|
|
1419
1419
|
// the terminal `sso-bounce` never fires.
|
|
1420
1420
|
//
|
|
1421
|
-
// The
|
|
1422
|
-
//
|
|
1423
|
-
//
|
|
1424
|
-
//
|
|
1425
|
-
//
|
|
1426
|
-
//
|
|
1427
|
-
//
|
|
1428
|
-
// `isWebBrowser()` gates it off, so native never runs an iframe.
|
|
1421
|
+
// The per-apex iframe mint itself lives in
|
|
1422
|
+
// `mintSessionViaPerApexIframe` (shared verbatim with the in-session
|
|
1423
|
+
// refresh handler so the two paths can never drift): it points the
|
|
1424
|
+
// iframe at `autoDetectAuthWebUrl()` and skips when there is no
|
|
1425
|
+
// per-apex IdP (localhost/IP/single-label/off-browser). Web only; on
|
|
1426
|
+
// native `isWebBrowser()` gates it off, so native never runs an
|
|
1427
|
+
// iframe.
|
|
1429
1428
|
//
|
|
1430
1429
|
// FIX-B: additionally skipped when `stored-session` already won.
|
|
1431
1430
|
// FIX-D: bounded by `SILENT_IFRAME_TIMEOUT` (plus `iframe.onerror`
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
*
|
|
16
16
|
* 1. {@link createInSessionRefreshHandler} — an `AuthRefreshHandler` installed
|
|
17
17
|
* on the owner client. It re-mints a fresh access token WITHOUT a page
|
|
18
|
-
* reload by
|
|
19
|
-
*
|
|
20
|
-
* (`createLinkedClient`) inherits the fix for
|
|
21
|
-
* back to the owner's `refreshAccessToken`.
|
|
18
|
+
* reload by composing the SAME silent-restore primitives cold boot uses
|
|
19
|
+
* ({@link mintSessionViaPerApexIframe}, {@link selectActiveRefreshAccount}) —
|
|
20
|
+
* not a copy. The linked client (`createLinkedClient`) inherits the fix for
|
|
21
|
+
* free: its refresh delegates back to the owner's `refreshAccessToken`.
|
|
22
22
|
*
|
|
23
23
|
* 2. {@link startTokenRefreshScheduler} — a proactive scheduler that refreshes
|
|
24
24
|
* ~{@link TOKEN_REFRESH_LEAD_MS} before expiry (and on web tab-focus / native
|
|
@@ -35,10 +35,11 @@ import type {
|
|
|
35
35
|
AuthRefreshHandler,
|
|
36
36
|
AuthRefreshReason,
|
|
37
37
|
} from '@oxyhq/core';
|
|
38
|
-
import {
|
|
38
|
+
import { logger as loggerUtil } from '@oxyhq/core';
|
|
39
39
|
import { AppState, type AppStateStatus } from 'react-native';
|
|
40
40
|
import { isWebBrowser } from '../hooks/useWebSSO';
|
|
41
41
|
import { readActiveAuthuser } from '../utils/activeAuthuser';
|
|
42
|
+
import { mintSessionViaPerApexIframe, selectActiveRefreshAccount } from './silentSessionRestore';
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* Per-arm fail-fast budget (ms) for the web first-party `/auth/silent` iframe
|
|
@@ -67,114 +68,97 @@ const COOKIE_REFRESH_TIMEOUT = 4000;
|
|
|
67
68
|
*/
|
|
68
69
|
export const TOKEN_REFRESH_LEAD_MS = 60_000;
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
error,
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
71
|
+
/**
|
|
72
|
+
* A single refresh arm: attempts one silent-restore mechanism and resolves to
|
|
73
|
+
* the freshly planted access token, or `null` to fall through to the next arm.
|
|
74
|
+
*/
|
|
75
|
+
type RefreshArm = () => Promise<string | null>;
|
|
79
76
|
|
|
80
77
|
/**
|
|
81
78
|
* Build the in-session `AuthRefreshHandler` for the owner client.
|
|
82
79
|
*
|
|
83
|
-
* Arms
|
|
84
|
-
*
|
|
85
|
-
* it
|
|
80
|
+
* Arms run in an explicit order; the first to plant a token wins. Each arm
|
|
81
|
+
* resolves to the planted token (read back via `getAccessToken()` — the
|
|
82
|
+
* underlying SDK call plants it internally) or `null`. A throw in one arm is
|
|
83
|
+
* logged and treated as `null` so the chain continues.
|
|
86
84
|
*
|
|
87
85
|
* NATIVE (Expo): shared cross-app identity key re-mint
|
|
88
86
|
* (`signInWithSharedIdentity` → challenge→sign→verify plants the tokens).
|
|
89
87
|
* The ONLY silent native arm (mirrors cold boot's `shared-key-signin`); the
|
|
90
|
-
* `/auth/silent` web iframe is NEVER attempted on native.
|
|
88
|
+
* `/auth/silent` web iframe is NEVER attempted on native. Resolves to `null`
|
|
91
89
|
* when the device holds no shared identity (e.g. a password-only native
|
|
92
90
|
* sign-in) so a genuinely dead session reconciles to logged-out rather than
|
|
93
91
|
* staying a zombie.
|
|
94
92
|
*
|
|
95
93
|
* WEB, in order:
|
|
96
|
-
* 1.
|
|
97
|
-
* (
|
|
98
|
-
*
|
|
99
|
-
* `fedcm_session` cookie on `auth.<apex>` and mints a fresh Oxy token. No
|
|
100
|
-
* top-level navigation → works in a backgrounded tab. On a `*.oxy.so`
|
|
101
|
-
* app the per-apex host IS the central host, so this also covers
|
|
102
|
-
* same-apex.
|
|
94
|
+
* 1. Per-apex `/auth/silent` iframe — {@link mintSessionViaPerApexIframe}
|
|
95
|
+
* (shared verbatim with cold boot). The durable cross-apex path; also
|
|
96
|
+
* covers `*.oxy.so` (the per-apex host IS the central host there).
|
|
103
97
|
* 2. FedCM silent re-auth (Chrome) — `silentSignInWithFedCM`.
|
|
104
|
-
* 3. Same-apex refresh cookie — `refreshAllSessions
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* bearer.
|
|
98
|
+
* 3. Same-apex refresh cookie — `refreshAllSessions` + the shared
|
|
99
|
+
* {@link selectActiveRefreshAccount}; plant the active account's rotated
|
|
100
|
+
* token. On a cross-apex RP it returns `{accounts:[]}` (clean no-op).
|
|
101
|
+
* Unlike the cold-boot cookie restore this does NOT rebuild multi-session
|
|
102
|
+
* state — an in-session refresh only needs a fresh bearer.
|
|
110
103
|
*
|
|
111
|
-
* NO RECURSION:
|
|
104
|
+
* NO RECURSION: no arm issues a request through the authed client's
|
|
112
105
|
* `refreshAccessToken` path. The iframe/FedCM transports are postMessage /
|
|
113
106
|
* credential APIs; the follow-up `/session/user` fetch inside `silentSignIn`
|
|
114
107
|
* runs against the just-planted FULL-TTL token (≫ the preflight lead), so it
|
|
115
|
-
* never re-enters the refresh path; `refreshAllSessions` uses a raw `fetch
|
|
116
|
-
* `credentials:'include'`.
|
|
108
|
+
* never re-enters the refresh path; `refreshAllSessions` uses a raw `fetch`.
|
|
117
109
|
*/
|
|
118
110
|
export function createInSessionRefreshHandler(oxyServices: OxyServices): AuthRefreshHandler {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
debugRefresh('Native shared-key in-session refresh failed', reason, error);
|
|
111
|
+
const runArm = async (label: string, reason: AuthRefreshReason, arm: RefreshArm): Promise<string | null> => {
|
|
112
|
+
try {
|
|
113
|
+
return await arm();
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (__DEV__) {
|
|
116
|
+
loggerUtil.debug(
|
|
117
|
+
`In-session refresh arm "${label}" failed (falling through)`,
|
|
118
|
+
{ component: 'inSessionTokenRefresh', method: 'authRefreshHandler', reason },
|
|
119
|
+
error,
|
|
120
|
+
);
|
|
130
121
|
}
|
|
131
122
|
return null;
|
|
132
123
|
}
|
|
124
|
+
};
|
|
133
125
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return oxyServices.getAccessToken();
|
|
144
|
-
}
|
|
126
|
+
const webArms: Array<[string, RefreshArm]> = [
|
|
127
|
+
['silent-iframe', async () =>
|
|
128
|
+
(await mintSessionViaPerApexIframe(oxyServices, SILENT_IFRAME_REFRESH_TIMEOUT))
|
|
129
|
+
? oxyServices.getAccessToken()
|
|
130
|
+
: null,
|
|
131
|
+
],
|
|
132
|
+
['fedcm-silent', async () => {
|
|
133
|
+
if (oxyServices.isFedCMSupported?.() !== true) {
|
|
134
|
+
return null;
|
|
145
135
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (oxyServices.isFedCMSupported?.() === true) {
|
|
153
|
-
const session = await oxyServices.silentSignInWithFedCM?.();
|
|
154
|
-
if (session) {
|
|
155
|
-
return oxyServices.getAccessToken();
|
|
156
|
-
}
|
|
136
|
+
return (await oxyServices.silentSignInWithFedCM?.()) ? oxyServices.getAccessToken() : null;
|
|
137
|
+
}],
|
|
138
|
+
['refresh-cookie', async () => {
|
|
139
|
+
const snapshot = await oxyServices.refreshAllSessions({ timeout: COOKIE_REFRESH_TIMEOUT });
|
|
140
|
+
if (snapshot.accounts.length === 0) {
|
|
141
|
+
return null;
|
|
157
142
|
}
|
|
158
|
-
|
|
159
|
-
|
|
143
|
+
const active = selectActiveRefreshAccount(snapshot.accounts, readActiveAuthuser());
|
|
144
|
+
oxyServices.httpService.setTokens(active.accessToken);
|
|
145
|
+
return oxyServices.getAccessToken();
|
|
146
|
+
}],
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
return async (reason: AuthRefreshReason): Promise<string | null> => {
|
|
150
|
+
if (!isWebBrowser()) {
|
|
151
|
+
return runArm('native-shared-key', reason, async () =>
|
|
152
|
+
(await oxyServices.signInWithSharedIdentity?.()) ? oxyServices.getAccessToken() : null,
|
|
153
|
+
);
|
|
160
154
|
}
|
|
161
155
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const persistedAuthuser = readActiveAuthuser();
|
|
167
|
-
const active =
|
|
168
|
-
(persistedAuthuser !== null
|
|
169
|
-
? snapshot.accounts.find((account) => account.authuser === persistedAuthuser)
|
|
170
|
-
: undefined) ?? snapshot.accounts[0];
|
|
171
|
-
oxyServices.httpService.setTokens(active.accessToken);
|
|
172
|
-
return oxyServices.getAccessToken();
|
|
156
|
+
for (const [label, arm] of webArms) {
|
|
157
|
+
const token = await runArm(label, reason, arm);
|
|
158
|
+
if (token) {
|
|
159
|
+
return token;
|
|
173
160
|
}
|
|
174
|
-
} catch (error) {
|
|
175
|
-
debugRefresh('Refresh-cookie in-session refresh failed', reason, error);
|
|
176
161
|
}
|
|
177
|
-
|
|
178
162
|
return null;
|
|
179
163
|
};
|
|
180
164
|
}
|
|
@@ -194,14 +178,15 @@ export interface TokenRefreshSchedulerHandle {
|
|
|
194
178
|
* current access token's `exp`, calling `httpService.refreshAccessToken`
|
|
195
179
|
* ('preflight') — which runs the installed handler and is deduped + cooldown-
|
|
196
180
|
* guarded. After every attempt it reschedules from the (possibly rotated) token.
|
|
197
|
-
* It also reschedules whenever the token changes (`onTokensChanged`
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* expired on return).
|
|
181
|
+
* It also reschedules whenever the token changes (`onTokensChanged` — so a
|
|
182
|
+
* sign-out that clears the token cancels the timer) and, on web tab-focus /
|
|
183
|
+
* native app-foreground, refreshes immediately if already inside the lead window
|
|
184
|
+
* (a long-hidden tab throttles timers, so the token can be expired on return).
|
|
201
185
|
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* path
|
|
186
|
+
* The `exp` is derived directly from the JWT via `getAccessTokenExpiry()`. No-ops
|
|
187
|
+
* cleanly when there is no token, an opaque/no-`exp` token, or the host lacks
|
|
188
|
+
* `getAccessTokenExpiry` (older stubs) — the reactive 401 path stays the only
|
|
189
|
+
* refresh trigger in those cases.
|
|
205
190
|
*/
|
|
206
191
|
export function startTokenRefreshScheduler(oxyServices: OxyServices): TokenRefreshSchedulerHandle {
|
|
207
192
|
let disposed = false;
|