@atlasauth/js 0.7.0 → 0.8.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/CHANGELOG.md +27 -0
- package/dist/environment.d.ts +79 -0
- package/dist/environment.js +74 -0
- package/dist/environment.js.map +1 -0
- package/dist/frontchannel-logout.d.ts +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/instance-settings.d.ts +9 -0
- package/dist/instance-settings.js +2 -1
- package/dist/instance-settings.js.map +1 -1
- package/dist/sign-out-guard.d.ts +114 -0
- package/dist/sign-out-guard.js +197 -0
- package/dist/sign-out-guard.js.map +1 -0
- package/dist/sign-out.d.ts +28 -0
- package/dist/sign-out.js +47 -1
- package/dist/sign-out.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @atlasauth/js changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
- **New: `SignOutGuard`** — the safer sign-out every framework provider now drives, so they all behave like
|
|
5
|
+
`@atlasauth/react`: `new SignOutGuard({ request, frontendApi?, store?, tombstones?, retryDelaysMs?,
|
|
6
|
+
frontChannelLogout? })` (`SignOutGuardOptions`). Methods: `revoke(sessionId, { frontchannel?, request? })` →
|
|
7
|
+
`SignOutAttempt` (`{ sessionId, outcome }`; finds the cookie session with `GET /v1/client` when the id is unknown,
|
|
8
|
+
tombstones it before revoking, revokes with one retry, runs front-channel logout, and re-tries a failed revoke in
|
|
9
|
+
the background after 15 s, 1 min and 5 min); `settle(attempt, { strict? })` → `SignOutResult` (throws
|
|
10
|
+
`SignOutError` under `strict`); `promoted(attempt)` → the server-promoted session id to adopt, or null;
|
|
11
|
+
`refuse(sessionId)` → whether a booted session must not be adopted (re-tries its revoke in the background and
|
|
12
|
+
removes the tombstone once it lands); `isSignedOut(id)`; `signal(attempt)` / `learn(message)` for the cross-tab
|
|
13
|
+
`{ signedOut: string[] }` message; `watch(current, onSignedOut)` (another tab signed out of this session);
|
|
14
|
+
`ready`; `dispose()`; `tombstones`. **Added types** `FapiRequest`, `SignOutGuardOptions`, `SignOutAttempt`,
|
|
15
|
+
`SignedOutSignal`.
|
|
16
|
+
- **Tombstones on platforms without `localStorage`.** `SessionTombstonesOptions.store?: TombstoneStore` (new type:
|
|
17
|
+
`{ getItem(key), setItem(key, value) }`, sync or async) keeps the tombstones in the platform's own store
|
|
18
|
+
(AsyncStorage, `expo-secure-store`, `chrome.storage`, Capacitor Preferences…). **Added**
|
|
19
|
+
`SessionTombstones.ready: Promise<void>` (settles once the store has been read; immediate for `localStorage`).
|
|
20
|
+
- **Email delivery.** `InstanceSettings.emailDelivery: EmailDelivery` (new type: `{ available, passwordReset,
|
|
21
|
+
passwordSignUp }`, read from `email_delivery` on `GET /v1/appearance`; all `true` on a server that does not send
|
|
22
|
+
it). **New helpers:** `emailDeliveryFrom(body)` (an environment / appearance body or the object itself),
|
|
23
|
+
`offeredFirstFactors(strategies, delivery)` (drops `email_code` / `email_link` while email can't be delivered),
|
|
24
|
+
and the constants `EMAIL_DELIVERY_UNKNOWN`, `EMAIL_UNAVAILABLE_MESSAGE`, `EMAIL_FIRST_FACTORS`.
|
|
25
|
+
- **New: typed client environment.** `fetchClientEnvironment(client)` → `ClientEnvironment | null` and
|
|
26
|
+
`clientEnvironmentFrom(body)` for `GET /v1/client/environment`: `{ firstFactors, strategies, emailDelivery,
|
|
27
|
+
socialProviders }`, with the new `EnvironmentSocialProvider` type (`provider`, `native`, `mode`, `label`,
|
|
28
|
+
`display_name`, `icon`, `allow_sign_in`, `allow_sign_up`, `client_id?`, `bot_username?`).
|
|
29
|
+
|
|
3
30
|
## 0.7.0
|
|
4
31
|
- **Popup OAuth completes when the opener page sends `Cross-Origin-Opener-Policy: same-origin`.** That
|
|
5
32
|
header puts the provider popup in another browsing-context group: `popup.closed` reads `true` at once and
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { FapiClient } from './fapi';
|
|
2
|
+
/**
|
|
3
|
+
* The client environment (`GET /v1/client/environment`) and the email-delivery
|
|
4
|
+
* facts both it and `GET /v1/appearance` publish.
|
|
5
|
+
*
|
|
6
|
+
* An app that cannot deliver email (a production instance with no email
|
|
7
|
+
* provider) cannot send a reset code, an email sign-in code or link, or a
|
|
8
|
+
* sign-up verification code. The server already stops offering email code / link
|
|
9
|
+
* and refuses those requests; `email_delivery` lets a sign-in UI say so up front
|
|
10
|
+
* instead: hide "Forgot password?" and email-code options, and show a "can't
|
|
11
|
+
* send email" state where a sign-up would need one.
|
|
12
|
+
*/
|
|
13
|
+
/** `email_delivery`: what works given whether the app can deliver email. */
|
|
14
|
+
export interface EmailDelivery {
|
|
15
|
+
/** The app can send email (its own provider, or the shared development sender). */
|
|
16
|
+
available: boolean;
|
|
17
|
+
/** "Forgot password?" can work: password sign-in is on and reset emails can be delivered. */
|
|
18
|
+
passwordReset: boolean;
|
|
19
|
+
/** Email + password sign-up can complete (email can be delivered, or verification is not required). */
|
|
20
|
+
passwordSignUp: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* What an older server (no `email_delivery`) is taken to mean: everything works.
|
|
24
|
+
* The server still refuses whatever it cannot deliver.
|
|
25
|
+
*/
|
|
26
|
+
export declare const EMAIL_DELIVERY_UNKNOWN: Readonly<EmailDelivery>;
|
|
27
|
+
/** The default English copy for the "can't send email" state. */
|
|
28
|
+
export declare const EMAIL_UNAVAILABLE_MESSAGE = "This app can't send email right now. Try another sign-in method or contact the app owner.";
|
|
29
|
+
/** The first factors that need the app to deliver email. */
|
|
30
|
+
export declare const EMAIL_FIRST_FACTORS: readonly string[];
|
|
31
|
+
/**
|
|
32
|
+
* Read `email_delivery` from a `/v1/client/environment` or `/v1/appearance`
|
|
33
|
+
* body — or from the `email_delivery` object itself. A missing object or field
|
|
34
|
+
* reads as available (see {@link EMAIL_DELIVERY_UNKNOWN}).
|
|
35
|
+
*/
|
|
36
|
+
export declare function emailDeliveryFrom(body: unknown): EmailDelivery;
|
|
37
|
+
/**
|
|
38
|
+
* The first factors a sign-in UI should offer: `strategies` without email code /
|
|
39
|
+
* link while the app cannot deliver email. A capability of the app, not of the
|
|
40
|
+
* user, so it never narrows the server's uniform list per identifier. With no
|
|
41
|
+
* `delivery` (not loaded yet) the list is returned unchanged.
|
|
42
|
+
*/
|
|
43
|
+
export declare function offeredFirstFactors(strategies: readonly string[], delivery: EmailDelivery | null | undefined): string[];
|
|
44
|
+
/** One entry of the environment's `social_providers`. */
|
|
45
|
+
export interface EnvironmentSocialProvider {
|
|
46
|
+
provider: string;
|
|
47
|
+
/** Whether the provider signs in natively (One Tap etc.) on this instance. */
|
|
48
|
+
native: boolean;
|
|
49
|
+
mode: 'native' | 'redirect';
|
|
50
|
+
/** The full button text, or null for the SDK's own label. */
|
|
51
|
+
label: string | null;
|
|
52
|
+
display_name: string | null;
|
|
53
|
+
icon: {
|
|
54
|
+
type: 'url' | 'svg';
|
|
55
|
+
value: string;
|
|
56
|
+
} | null;
|
|
57
|
+
allow_sign_in: boolean;
|
|
58
|
+
allow_sign_up: boolean;
|
|
59
|
+
/** Public client id, for native providers only. */
|
|
60
|
+
client_id?: string;
|
|
61
|
+
/** Telegram's bot username. */
|
|
62
|
+
bot_username?: string;
|
|
63
|
+
}
|
|
64
|
+
/** `GET /v1/client/environment`, typed. */
|
|
65
|
+
export interface ClientEnvironment {
|
|
66
|
+
/** The enabled first factors (`password`, `email_code`, `passkey`, …). */
|
|
67
|
+
firstFactors: string[];
|
|
68
|
+
/**
|
|
69
|
+
* The offered strategies, camelCase as the server sends them (`password`,
|
|
70
|
+
* `emailCode`, `emailLink`, `passkey`, `phoneCode`, …, `oauthProviders`).
|
|
71
|
+
*/
|
|
72
|
+
strategies: Record<string, unknown>;
|
|
73
|
+
emailDelivery: EmailDelivery;
|
|
74
|
+
socialProviders: EnvironmentSocialProvider[];
|
|
75
|
+
}
|
|
76
|
+
/** Read a `/v1/client/environment` body. Defensive: malformed parts read as empty. */
|
|
77
|
+
export declare function clientEnvironmentFrom(body: unknown): ClientEnvironment;
|
|
78
|
+
/** Fetch the client environment. Null when the server can't be reached or errors. */
|
|
79
|
+
export declare function fetchClientEnvironment(client: FapiClient): Promise<ClientEnvironment | null>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EMAIL_FIRST_FACTORS = exports.EMAIL_UNAVAILABLE_MESSAGE = exports.EMAIL_DELIVERY_UNKNOWN = void 0;
|
|
4
|
+
exports.emailDeliveryFrom = emailDeliveryFrom;
|
|
5
|
+
exports.offeredFirstFactors = offeredFirstFactors;
|
|
6
|
+
exports.clientEnvironmentFrom = clientEnvironmentFrom;
|
|
7
|
+
exports.fetchClientEnvironment = fetchClientEnvironment;
|
|
8
|
+
/**
|
|
9
|
+
* What an older server (no `email_delivery`) is taken to mean: everything works.
|
|
10
|
+
* The server still refuses whatever it cannot deliver.
|
|
11
|
+
*/
|
|
12
|
+
exports.EMAIL_DELIVERY_UNKNOWN = Object.freeze({
|
|
13
|
+
available: true,
|
|
14
|
+
passwordReset: true,
|
|
15
|
+
passwordSignUp: true,
|
|
16
|
+
});
|
|
17
|
+
/** The default English copy for the "can't send email" state. */
|
|
18
|
+
exports.EMAIL_UNAVAILABLE_MESSAGE = "This app can't send email right now. Try another sign-in method or contact the app owner.";
|
|
19
|
+
/** The first factors that need the app to deliver email. */
|
|
20
|
+
exports.EMAIL_FIRST_FACTORS = ['email_code', 'email_link'];
|
|
21
|
+
const isRecord = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
22
|
+
/**
|
|
23
|
+
* Read `email_delivery` from a `/v1/client/environment` or `/v1/appearance`
|
|
24
|
+
* body — or from the `email_delivery` object itself. A missing object or field
|
|
25
|
+
* reads as available (see {@link EMAIL_DELIVERY_UNKNOWN}).
|
|
26
|
+
*/
|
|
27
|
+
function emailDeliveryFrom(body) {
|
|
28
|
+
const raw = isRecord(body) && isRecord(body.email_delivery) ? body.email_delivery : body;
|
|
29
|
+
if (!isRecord(raw) || !('available' in raw || 'password_reset' in raw || 'password_sign_up' in raw)) {
|
|
30
|
+
return { ...exports.EMAIL_DELIVERY_UNKNOWN };
|
|
31
|
+
}
|
|
32
|
+
const flag = (v) => (typeof v === 'boolean' ? v : true);
|
|
33
|
+
return {
|
|
34
|
+
available: flag(raw.available),
|
|
35
|
+
passwordReset: flag(raw.password_reset),
|
|
36
|
+
passwordSignUp: flag(raw.password_sign_up),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The first factors a sign-in UI should offer: `strategies` without email code /
|
|
41
|
+
* link while the app cannot deliver email. A capability of the app, not of the
|
|
42
|
+
* user, so it never narrows the server's uniform list per identifier. With no
|
|
43
|
+
* `delivery` (not loaded yet) the list is returned unchanged.
|
|
44
|
+
*/
|
|
45
|
+
function offeredFirstFactors(strategies, delivery) {
|
|
46
|
+
if (!delivery || delivery.available)
|
|
47
|
+
return [...strategies];
|
|
48
|
+
return strategies.filter((s) => !exports.EMAIL_FIRST_FACTORS.includes(s));
|
|
49
|
+
}
|
|
50
|
+
/** Read a `/v1/client/environment` body. Defensive: malformed parts read as empty. */
|
|
51
|
+
function clientEnvironmentFrom(body) {
|
|
52
|
+
const root = isRecord(body) ? body : {};
|
|
53
|
+
return {
|
|
54
|
+
firstFactors: Array.isArray(root.first_factors)
|
|
55
|
+
? root.first_factors.filter((f) => typeof f === 'string')
|
|
56
|
+
: [],
|
|
57
|
+
strategies: isRecord(root.strategies) ? { ...root.strategies } : {},
|
|
58
|
+
emailDelivery: emailDeliveryFrom(root),
|
|
59
|
+
socialProviders: Array.isArray(root.social_providers)
|
|
60
|
+
? root.social_providers.filter((p) => isRecord(p) && typeof p.provider === 'string')
|
|
61
|
+
: [],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Fetch the client environment. Null when the server can't be reached or errors. */
|
|
65
|
+
async function fetchClientEnvironment(client) {
|
|
66
|
+
try {
|
|
67
|
+
const response = await client.get('/v1/client/environment');
|
|
68
|
+
return response.ok ? clientEnvironmentFrom(response.data) : null;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment.js","sourceRoot":"","sources":["../src/environment.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,7 +26,7 @@ export interface SignOutOptions {
|
|
|
26
26
|
* Reject with a `SignOutError` when the Frontend API could not revoke the
|
|
27
27
|
* session (after one retry). The local sign-out still happens either way.
|
|
28
28
|
* Default `false`: `signOut()` resolves and reports `revoked: false` instead.
|
|
29
|
-
* Honoured by
|
|
29
|
+
* Honoured by every framework provider.
|
|
30
30
|
*/
|
|
31
31
|
strict?: boolean;
|
|
32
32
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './config';
|
|
2
2
|
export * from './sign-out';
|
|
3
|
+
export * from './sign-out-guard';
|
|
3
4
|
export * from './token-cache';
|
|
4
5
|
export * from './tab-election';
|
|
5
6
|
export * from './attempt';
|
|
@@ -22,4 +23,5 @@ export * from './captcha';
|
|
|
22
23
|
export * from './captcha-widget';
|
|
23
24
|
export * from './captcha-controller';
|
|
24
25
|
export * from './instance-settings';
|
|
26
|
+
export * from './environment';
|
|
25
27
|
export { type AuthzClaims, type ProtectCondition, type ProtectOutcome, type ProtectReason, evaluate, hasFromClaims, protectFromClaims, ForbiddenError, ROLE_KEY_PATTERN, PERMISSION_KEY_PATTERN, isRoleKey, isPermissionKey, } from '@atlasauth/authz';
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,8 @@ exports.isPermissionKey = exports.isRoleKey = exports.PERMISSION_KEY_PATTERN = e
|
|
|
18
18
|
// Provider configuration checks (frontendApi) shared by every framework SDK.
|
|
19
19
|
__exportStar(require("./config"), exports);
|
|
20
20
|
__exportStar(require("./sign-out"), exports);
|
|
21
|
+
// The safer sign-out every framework provider drives (tombstones, background revoke).
|
|
22
|
+
__exportStar(require("./sign-out-guard"), exports);
|
|
21
23
|
__exportStar(require("./token-cache"), exports);
|
|
22
24
|
__exportStar(require("./tab-election"), exports);
|
|
23
25
|
__exportStar(require("./attempt"), exports);
|
|
@@ -49,6 +51,8 @@ __exportStar(require("./captcha-widget"), exports);
|
|
|
49
51
|
// sign-in / sign-up settings (`/v1/appearance`) they are driven by.
|
|
50
52
|
__exportStar(require("./captcha-controller"), exports);
|
|
51
53
|
__exportStar(require("./instance-settings"), exports);
|
|
54
|
+
// `GET /v1/client/environment`, typed, and the `email_delivery` facts.
|
|
55
|
+
__exportStar(require("./environment"), exports);
|
|
52
56
|
// Framework-agnostic authorization primitive — `has()`/`evaluate` for any
|
|
53
57
|
// non-React/Next consumer reading org_role/org_permissions off its session.
|
|
54
58
|
var authz_1 = require("@atlasauth/authz");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,6EAA6E;AAC7E,2CAAyB;AACzB,6CAA2B;AAC3B,gDAA8B;AAC9B,iDAA+B;AAC/B,4CAA0B;AAC1B,6CAA2B;AAC3B,qFAAqF;AACrF,kDAAgC;AAChC,yCAAuB;AACvB,2CAAyB;AACzB,mDAAiC;AACjC,4CAA0B;AAC1B,6CAA2B;AAC3B,yCAAuB;AACvB,8CAA4B;AAC5B,2CAAyB;AACzB,4DAA4D;AAC5D,6CAA2B;AAC3B,4CAA0B;AAC1B,mDAAiC;AACjC,kDAAgC;AAChC,sDAAsD;AACtD,wDAAsC;AACtC,+EAA+E;AAC/E,2EAA2E;AAC3E,4CAA0B;AAC1B,kFAAkF;AAClF,mDAAiC;AACjC,kFAAkF;AAClF,yEAAyE;AACzE,oEAAoE;AACpE,uDAAqC;AACrC,sDAAoC;AACpC,0EAA0E;AAC1E,4EAA4E;AAC5E,0CAa0B;AARxB,iGAAA,QAAQ,OAAA;AACR,sGAAA,aAAa,OAAA;AACb,0GAAA,iBAAiB,OAAA;AACjB,uGAAA,cAAc,OAAA;AACd,yGAAA,gBAAgB,OAAA;AAChB,+GAAA,sBAAsB,OAAA;AACtB,kGAAA,SAAS,OAAA;AACT,wGAAA,eAAe,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,6EAA6E;AAC7E,2CAAyB;AACzB,6CAA2B;AAC3B,sFAAsF;AACtF,mDAAiC;AACjC,gDAA8B;AAC9B,iDAA+B;AAC/B,4CAA0B;AAC1B,6CAA2B;AAC3B,qFAAqF;AACrF,kDAAgC;AAChC,yCAAuB;AACvB,2CAAyB;AACzB,mDAAiC;AACjC,4CAA0B;AAC1B,6CAA2B;AAC3B,yCAAuB;AACvB,8CAA4B;AAC5B,2CAAyB;AACzB,4DAA4D;AAC5D,6CAA2B;AAC3B,4CAA0B;AAC1B,mDAAiC;AACjC,kDAAgC;AAChC,sDAAsD;AACtD,wDAAsC;AACtC,+EAA+E;AAC/E,2EAA2E;AAC3E,4CAA0B;AAC1B,kFAAkF;AAClF,mDAAiC;AACjC,kFAAkF;AAClF,yEAAyE;AACzE,oEAAoE;AACpE,uDAAqC;AACrC,sDAAoC;AACpC,uEAAuE;AACvE,gDAA8B;AAC9B,0EAA0E;AAC1E,4EAA4E;AAC5E,0CAa0B;AARxB,iGAAA,QAAQ,OAAA;AACR,sGAAA,aAAa,OAAA;AACb,0GAAA,iBAAiB,OAAA;AACjB,uGAAA,cAAc,OAAA;AACd,yGAAA,gBAAgB,OAAA;AAChB,+GAAA,sBAAsB,OAAA;AACtB,kGAAA,SAAS,OAAA;AACT,wGAAA,eAAe,OAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CaptchaFlowKey, FlowCaptcha } from './captcha-widget';
|
|
2
|
+
import { type EmailDelivery } from './environment';
|
|
2
3
|
import type { FapiClient } from './fapi';
|
|
3
4
|
/**
|
|
4
5
|
* The instance's public sign-in / sign-up settings, read from
|
|
@@ -19,6 +20,14 @@ export interface InstanceSettings {
|
|
|
19
20
|
privacyUrl: string | null;
|
|
20
21
|
required: boolean;
|
|
21
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* Whether the app can deliver email, and what that allows: hide "Forgot
|
|
25
|
+
* password?" when `passwordReset` is false, email code / link options when
|
|
26
|
+
* `available` is false, and show a "can't send email" state instead of the
|
|
27
|
+
* password sign-up form when `passwordSignUp` is false. All true on an older
|
|
28
|
+
* server that does not say.
|
|
29
|
+
*/
|
|
30
|
+
emailDelivery: EmailDelivery;
|
|
22
31
|
}
|
|
23
32
|
/** One configurable sign-up field, exactly as `/v1/appearance` resolves it. */
|
|
24
33
|
export interface PublicSignUpField {
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.instanceSettingsFrom = instanceSettingsFrom;
|
|
4
4
|
exports.fetchInstanceSettings = fetchInstanceSettings;
|
|
5
5
|
exports.toSignUpInput = toSignUpInput;
|
|
6
|
+
const environment_1 = require("./environment");
|
|
6
7
|
const NO_LEGAL = { termsUrl: null, privacyUrl: null, required: false };
|
|
7
8
|
const isRecord = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
8
9
|
/**
|
|
@@ -40,7 +41,7 @@ function instanceSettingsFrom(body) {
|
|
|
40
41
|
required: legalRaw.required === true,
|
|
41
42
|
}
|
|
42
43
|
: NO_LEGAL;
|
|
43
|
-
return { captchaFlows, signUpFields, legal };
|
|
44
|
+
return { captchaFlows, signUpFields, legal, emailDelivery: (0, environment_1.emailDeliveryFrom)(root) };
|
|
44
45
|
}
|
|
45
46
|
/**
|
|
46
47
|
* Fetch the instance's public settings (`GET /v1/appearance`). Null when the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance-settings.js","sourceRoot":"","sources":["../src/instance-settings.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"instance-settings.js","sourceRoot":"","sources":["../src/instance-settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The whole safer sign-out, once, for every framework provider.
|
|
3
|
+
*
|
|
4
|
+
* `@atlasauth/react` grew this inline; every other provider (Vue, Svelte,
|
|
5
|
+
* Angular, Solid, Qwik, the native SDKs) drives the same steps through a
|
|
6
|
+
* {@link SignOutGuard} so the behaviour cannot drift:
|
|
7
|
+
*
|
|
8
|
+
* - `signOut()` remembers the session id BEFORE revoking (a tombstone), revokes
|
|
9
|
+
* it on the Frontend API (retried once), runs front-channel logout, and
|
|
10
|
+
* reports `{ revoked, activeSessionId? }` — or rejects with a
|
|
11
|
+
* {@link SignOutError} under `{ strict: true }`;
|
|
12
|
+
* - every boot / reload / refresh / sibling-tab broadcast asks
|
|
13
|
+
* {@link SignOutGuard.refuse} before adopting a session, so a session this
|
|
14
|
+
* browser signed out of is never re-adopted from a refresh cookie (or a
|
|
15
|
+
* stored token) the revoke did not clear;
|
|
16
|
+
* - a refused session is revoked again in the background, and its tombstone is
|
|
17
|
+
* dropped once the server confirms;
|
|
18
|
+
* - a parked session the server promoted on revoke is adopted
|
|
19
|
+
* ({@link SignOutGuard.promoted}).
|
|
20
|
+
*
|
|
21
|
+
* The provider keeps its own state and calls these at the matching points.
|
|
22
|
+
*/
|
|
23
|
+
import type { SignOutOptions } from './frontchannel-logout';
|
|
24
|
+
import { SessionTombstones, type RevokeSessionOutcome, type SignOutResult, type TombstoneStore } from './sign-out';
|
|
25
|
+
/** A credentialed Frontend API request (`path` is relative to the FAPI base URL). */
|
|
26
|
+
export type FapiRequest = (path: string, init?: RequestInit) => Promise<Response>;
|
|
27
|
+
/** Options for {@link SignOutGuard}. */
|
|
28
|
+
export interface SignOutGuardOptions {
|
|
29
|
+
/** The provider's FAPI request: cookies (web) or its bearer token (native). */
|
|
30
|
+
request: FapiRequest;
|
|
31
|
+
/** The Frontend API base URL — tombstones are kept per Frontend API origin. */
|
|
32
|
+
frontendApi?: string;
|
|
33
|
+
/** A persistent store for platforms without `localStorage` (see {@link TombstoneStore}). */
|
|
34
|
+
store?: TombstoneStore;
|
|
35
|
+
/** Bring your own tombstones (tests, or a set shared with other code). */
|
|
36
|
+
tombstones?: SessionTombstones;
|
|
37
|
+
/**
|
|
38
|
+
* Delays (ms) between background re-tries of a revoke `signOut()` could not
|
|
39
|
+
* complete. Default `[15_000, 60_000, 300_000]`; `[]` re-tries only when a
|
|
40
|
+
* refused session shows up on a later boot.
|
|
41
|
+
*/
|
|
42
|
+
retryDelaysMs?: number[];
|
|
43
|
+
/** Skip front-channel logout entirely (runtimes without a DOM). */
|
|
44
|
+
frontChannelLogout?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** What {@link SignOutGuard.revoke} did on the server. */
|
|
47
|
+
export interface SignOutAttempt {
|
|
48
|
+
/** The session that was signed out of, or null when there was none. */
|
|
49
|
+
sessionId: string | null;
|
|
50
|
+
outcome: RevokeSessionOutcome;
|
|
51
|
+
}
|
|
52
|
+
/** The cross-tab sign-out signal: session ids (never tokens) a tab signed out of. */
|
|
53
|
+
export interface SignedOutSignal {
|
|
54
|
+
signedOut?: string[];
|
|
55
|
+
}
|
|
56
|
+
export declare class SignOutGuard {
|
|
57
|
+
/** The sessions this browser (or app) signed out of. */
|
|
58
|
+
readonly tombstones: SessionTombstones;
|
|
59
|
+
private readonly request;
|
|
60
|
+
private readonly retryDelaysMs;
|
|
61
|
+
private readonly frontChannelLogout;
|
|
62
|
+
/** Sessions with a background revoke in flight or scheduled. */
|
|
63
|
+
private readonly retrying;
|
|
64
|
+
private readonly timers;
|
|
65
|
+
constructor(options: SignOutGuardOptions);
|
|
66
|
+
/** Settles once persisted tombstones are loaded — await it before the first boot. */
|
|
67
|
+
get ready(): Promise<void>;
|
|
68
|
+
/** Whether this browser signed out of `sessionId`. */
|
|
69
|
+
isSignedOut(sessionId: string | null | undefined): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Call with the session a boot / reload / refresh is about to adopt. True when
|
|
72
|
+
* it must NOT be adopted (this browser signed out of it); the revoke is then
|
|
73
|
+
* re-tried in the background and the tombstone dropped once it lands.
|
|
74
|
+
*/
|
|
75
|
+
refuse(sessionId: string | null | undefined, request?: FapiRequest): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* The server half of `signOut()`. Finds the session to end when the provider
|
|
78
|
+
* does not know it (`GET /v1/client` — the revoke works from the cookies, so a
|
|
79
|
+
* missing in-memory token never skips it), tombstones it, revokes it (retried
|
|
80
|
+
* once) and runs front-channel logout. A failed revoke is re-tried in the
|
|
81
|
+
* background. Pass `request` to revoke with a credential the provider is about
|
|
82
|
+
* to drop (a native bearer token).
|
|
83
|
+
*/
|
|
84
|
+
revoke(sessionId: string | null | undefined, options?: SignOutOptions & {
|
|
85
|
+
request?: FapiRequest;
|
|
86
|
+
}): Promise<SignOutAttempt>;
|
|
87
|
+
/** The parked session the server promoted on revoke, when it should be adopted. */
|
|
88
|
+
promoted(attempt: SignOutAttempt): string | null;
|
|
89
|
+
/**
|
|
90
|
+
* What `signOut()` resolves with — or, with `{ strict: true }` and a revoke the
|
|
91
|
+
* server never confirmed, the {@link SignOutError} it rejects with. Call after
|
|
92
|
+
* the local sign-out.
|
|
93
|
+
*/
|
|
94
|
+
settle(attempt: SignOutAttempt, options?: SignOutOptions): SignOutResult;
|
|
95
|
+
/** The cross-tab message announcing a sign-out (`{}` when there was no session). */
|
|
96
|
+
signal(attempt: SignOutAttempt): SignedOutSignal;
|
|
97
|
+
/**
|
|
98
|
+
* Learn the sessions a sibling tab signed out of from its broadcast — call it
|
|
99
|
+
* BEFORE re-checking, so the re-check cannot re-adopt one. Returns the ids.
|
|
100
|
+
*/
|
|
101
|
+
learn(message: unknown): string[];
|
|
102
|
+
/**
|
|
103
|
+
* Call `onSignedOut` when another tab signs out of the session `current()`
|
|
104
|
+
* returns (its tombstone reached the shared store). Returns the unsubscribe.
|
|
105
|
+
*/
|
|
106
|
+
watch(current: () => string | null | undefined, onSignedOut: () => void): () => void;
|
|
107
|
+
/** Cancel scheduled background re-tries (provider teardown). */
|
|
108
|
+
dispose(): void;
|
|
109
|
+
/**
|
|
110
|
+
* Revoke `sessionId` again: now when `delays` is empty, else after each delay
|
|
111
|
+
* in turn until one lands. Drops the tombstone on success. One chain per session.
|
|
112
|
+
*/
|
|
113
|
+
private retryInBackground;
|
|
114
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The whole safer sign-out, once, for every framework provider.
|
|
4
|
+
*
|
|
5
|
+
* `@atlasauth/react` grew this inline; every other provider (Vue, Svelte,
|
|
6
|
+
* Angular, Solid, Qwik, the native SDKs) drives the same steps through a
|
|
7
|
+
* {@link SignOutGuard} so the behaviour cannot drift:
|
|
8
|
+
*
|
|
9
|
+
* - `signOut()` remembers the session id BEFORE revoking (a tombstone), revokes
|
|
10
|
+
* it on the Frontend API (retried once), runs front-channel logout, and
|
|
11
|
+
* reports `{ revoked, activeSessionId? }` — or rejects with a
|
|
12
|
+
* {@link SignOutError} under `{ strict: true }`;
|
|
13
|
+
* - every boot / reload / refresh / sibling-tab broadcast asks
|
|
14
|
+
* {@link SignOutGuard.refuse} before adopting a session, so a session this
|
|
15
|
+
* browser signed out of is never re-adopted from a refresh cookie (or a
|
|
16
|
+
* stored token) the revoke did not clear;
|
|
17
|
+
* - a refused session is revoked again in the background, and its tombstone is
|
|
18
|
+
* dropped once the server confirms;
|
|
19
|
+
* - a parked session the server promoted on revoke is adopted
|
|
20
|
+
* ({@link SignOutGuard.promoted}).
|
|
21
|
+
*
|
|
22
|
+
* The provider keeps its own state and calls these at the matching points.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.SignOutGuard = void 0;
|
|
26
|
+
const frontchannel_logout_1 = require("./frontchannel-logout");
|
|
27
|
+
const sign_out_1 = require("./sign-out");
|
|
28
|
+
const DEFAULT_RETRY_DELAYS_MS = [15_000, 60_000, 300_000];
|
|
29
|
+
class SignOutGuard {
|
|
30
|
+
/** The sessions this browser (or app) signed out of. */
|
|
31
|
+
tombstones;
|
|
32
|
+
request;
|
|
33
|
+
retryDelaysMs;
|
|
34
|
+
frontChannelLogout;
|
|
35
|
+
/** Sessions with a background revoke in flight or scheduled. */
|
|
36
|
+
retrying = new Set();
|
|
37
|
+
timers = new Set();
|
|
38
|
+
constructor(options) {
|
|
39
|
+
this.request = options.request;
|
|
40
|
+
this.tombstones =
|
|
41
|
+
options.tombstones ??
|
|
42
|
+
new sign_out_1.SessionTombstones({ frontendApi: options.frontendApi, ...(options.store ? { store: options.store } : {}) });
|
|
43
|
+
this.retryDelaysMs = options.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;
|
|
44
|
+
this.frontChannelLogout = options.frontChannelLogout !== false;
|
|
45
|
+
}
|
|
46
|
+
/** Settles once persisted tombstones are loaded — await it before the first boot. */
|
|
47
|
+
get ready() {
|
|
48
|
+
return this.tombstones.ready;
|
|
49
|
+
}
|
|
50
|
+
/** Whether this browser signed out of `sessionId`. */
|
|
51
|
+
isSignedOut(sessionId) {
|
|
52
|
+
return this.tombstones.has(sessionId);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Call with the session a boot / reload / refresh is about to adopt. True when
|
|
56
|
+
* it must NOT be adopted (this browser signed out of it); the revoke is then
|
|
57
|
+
* re-tried in the background and the tombstone dropped once it lands.
|
|
58
|
+
*/
|
|
59
|
+
refuse(sessionId, request) {
|
|
60
|
+
if (!this.tombstones.has(sessionId))
|
|
61
|
+
return false;
|
|
62
|
+
this.retryInBackground(sessionId, request ?? this.request, []);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The server half of `signOut()`. Finds the session to end when the provider
|
|
67
|
+
* does not know it (`GET /v1/client` — the revoke works from the cookies, so a
|
|
68
|
+
* missing in-memory token never skips it), tombstones it, revokes it (retried
|
|
69
|
+
* once) and runs front-channel logout. A failed revoke is re-tried in the
|
|
70
|
+
* background. Pass `request` to revoke with a credential the provider is about
|
|
71
|
+
* to drop (a native bearer token).
|
|
72
|
+
*/
|
|
73
|
+
async revoke(sessionId, options = {}) {
|
|
74
|
+
const request = options.request ?? this.request;
|
|
75
|
+
let id = sessionId || null;
|
|
76
|
+
let outcome = { revoked: true, status: 200 };
|
|
77
|
+
if (!id) {
|
|
78
|
+
try {
|
|
79
|
+
const response = await request('/v1/client');
|
|
80
|
+
if (!response.ok)
|
|
81
|
+
throw new Error('boot failed');
|
|
82
|
+
const body = (await response.json());
|
|
83
|
+
id = typeof body?.session?.id === 'string' ? body.session.id : null;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
outcome = { revoked: false, status: 0 };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (id) {
|
|
90
|
+
// Remember it first: from here on nothing may re-adopt it.
|
|
91
|
+
this.tombstones.add(id);
|
|
92
|
+
outcome = await (0, sign_out_1.revokeSession)(request, id);
|
|
93
|
+
// OIDC Front-Channel Logout: let the apps signed in through this session
|
|
94
|
+
// clear theirs in this browser (hidden iframes, capped at 5 s). Best-effort.
|
|
95
|
+
if (this.frontChannelLogout && options.frontchannel !== false && typeof document !== 'undefined') {
|
|
96
|
+
await (0, frontchannel_logout_1.runFrontChannelLogoutFromResponse)(outcome.response);
|
|
97
|
+
}
|
|
98
|
+
if (!outcome.revoked)
|
|
99
|
+
this.retryInBackground(id, request, this.retryDelaysMs);
|
|
100
|
+
}
|
|
101
|
+
return { sessionId: id, outcome };
|
|
102
|
+
}
|
|
103
|
+
/** The parked session the server promoted on revoke, when it should be adopted. */
|
|
104
|
+
promoted(attempt) {
|
|
105
|
+
const id = attempt.outcome.activeSessionId;
|
|
106
|
+
return id && !this.tombstones.has(id) ? id : null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* What `signOut()` resolves with — or, with `{ strict: true }` and a revoke the
|
|
110
|
+
* server never confirmed, the {@link SignOutError} it rejects with. Call after
|
|
111
|
+
* the local sign-out.
|
|
112
|
+
*/
|
|
113
|
+
settle(attempt, options = {}) {
|
|
114
|
+
const { outcome, sessionId } = attempt;
|
|
115
|
+
if (!outcome.revoked && options.strict)
|
|
116
|
+
throw new sign_out_1.SignOutError(sessionId ?? '', outcome.status);
|
|
117
|
+
return outcome.activeSessionId
|
|
118
|
+
? { revoked: outcome.revoked, activeSessionId: outcome.activeSessionId }
|
|
119
|
+
: { revoked: outcome.revoked };
|
|
120
|
+
}
|
|
121
|
+
/** The cross-tab message announcing a sign-out (`{}` when there was no session). */
|
|
122
|
+
signal(attempt) {
|
|
123
|
+
return attempt.sessionId ? { signedOut: [attempt.sessionId] } : {};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Learn the sessions a sibling tab signed out of from its broadcast — call it
|
|
127
|
+
* BEFORE re-checking, so the re-check cannot re-adopt one. Returns the ids.
|
|
128
|
+
*/
|
|
129
|
+
learn(message) {
|
|
130
|
+
const list = message?.signedOut;
|
|
131
|
+
if (!Array.isArray(list))
|
|
132
|
+
return [];
|
|
133
|
+
const ids = list.filter((id) => typeof id === 'string' && !!id);
|
|
134
|
+
this.tombstones.add(...ids);
|
|
135
|
+
return ids;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Call `onSignedOut` when another tab signs out of the session `current()`
|
|
139
|
+
* returns (its tombstone reached the shared store). Returns the unsubscribe.
|
|
140
|
+
*/
|
|
141
|
+
watch(current, onSignedOut) {
|
|
142
|
+
return this.tombstones.subscribe((ids) => {
|
|
143
|
+
const id = current();
|
|
144
|
+
if (id && ids.includes(id))
|
|
145
|
+
onSignedOut();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/** Cancel scheduled background re-tries (provider teardown). */
|
|
149
|
+
dispose() {
|
|
150
|
+
for (const t of this.timers)
|
|
151
|
+
clearTimeout(t);
|
|
152
|
+
this.timers.clear();
|
|
153
|
+
this.retrying.clear();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Revoke `sessionId` again: now when `delays` is empty, else after each delay
|
|
157
|
+
* in turn until one lands. Drops the tombstone on success. One chain per session.
|
|
158
|
+
*/
|
|
159
|
+
retryInBackground(sessionId, request, delays) {
|
|
160
|
+
if (this.retrying.has(sessionId))
|
|
161
|
+
return;
|
|
162
|
+
this.retrying.add(sessionId);
|
|
163
|
+
const attempt = async (index) => {
|
|
164
|
+
let revoked = false;
|
|
165
|
+
try {
|
|
166
|
+
revoked = (await (0, sign_out_1.revokeSession)(request, sessionId, { retries: 0 })).revoked;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
revoked = false;
|
|
170
|
+
}
|
|
171
|
+
if (revoked) {
|
|
172
|
+
this.tombstones.remove(sessionId);
|
|
173
|
+
this.retrying.delete(sessionId);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (index >= delays.length || !this.retrying.has(sessionId)) {
|
|
177
|
+
this.retrying.delete(sessionId);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
schedule(index);
|
|
181
|
+
};
|
|
182
|
+
const schedule = (index) => {
|
|
183
|
+
const timer = setTimeout(() => {
|
|
184
|
+
this.timers.delete(timer);
|
|
185
|
+
void attempt(index + 1);
|
|
186
|
+
}, delays[index]);
|
|
187
|
+
timer.unref?.();
|
|
188
|
+
this.timers.add(timer);
|
|
189
|
+
};
|
|
190
|
+
if (delays.length === 0)
|
|
191
|
+
void attempt(0);
|
|
192
|
+
else
|
|
193
|
+
schedule(0);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
exports.SignOutGuard = SignOutGuard;
|
|
197
|
+
//# sourceMappingURL=sign-out-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-out-guard.js","sourceRoot":"","sources":["../src/sign-out-guard.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AAGH,+DAA0E;AAC1E,yCAOoB;AAqCpB,MAAM,uBAAuB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1D,MAAa,YAAY;IACvB,wDAAwD;IAC/C,UAAU,CAAoB;IACtB,OAAO,CAAc;IACrB,aAAa,CAAW;IACxB,kBAAkB,CAAU;IAC7C,gEAAgE;IAC/C,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAC7B,MAAM,GAAG,IAAI,GAAG,EAAiC,CAAC;IAEnE,YAAY,OAA4B;QACtC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,UAAU;YACb,OAAO,CAAC,UAAU;gBAClB,IAAI,4BAAiB,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAClH,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,uBAAuB,CAAC;QACtE,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,KAAK,KAAK,CAAC;IACjE,CAAC;IAED,qFAAqF;IACrF,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;IAC/B,CAAC;IAED,sDAAsD;IACtD,WAAW,CAAC,SAAoC;QAC9C,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,SAAoC,EAAE,OAAqB;QAChE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;QAClD,IAAI,CAAC,iBAAiB,CAAC,SAAmB,EAAE,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM,CACV,SAAoC,EACpC,UAAsD,EAAE;QAExD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC;QAChD,IAAI,EAAE,GAAG,SAAS,IAAI,IAAI,CAAC;QAC3B,IAAI,OAAO,GAAyB,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;QACnE,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;gBAC7C,IAAI,CAAC,QAAQ,CAAC,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;gBACjD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAiD,CAAC;gBACrF,EAAE,GAAG,OAAO,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACtE,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YAC1C,CAAC;QACH,CAAC;QACD,IAAI,EAAE,EAAE,CAAC;YACP,2DAA2D;YAC3D,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACxB,OAAO,GAAG,MAAM,IAAA,wBAAa,EAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC3C,yEAAyE;YACzE,6EAA6E;YAC7E,IAAI,IAAI,CAAC,kBAAkB,IAAI,OAAO,CAAC,YAAY,KAAK,KAAK,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;gBACjG,MAAM,IAAA,uDAAiC,EAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC5D,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,OAAO;gBAAE,IAAI,CAAC,iBAAiB,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QAChF,CAAC;QACD,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;IACpC,CAAC;IAED,mFAAmF;IACnF,QAAQ,CAAC,OAAuB;QAC9B,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC;QAC3C,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACpD,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,OAAuB,EAAE,UAA0B,EAAE;QAC1D,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;QACvC,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM;YAAE,MAAM,IAAI,uBAAY,CAAC,SAAS,IAAI,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAChG,OAAO,OAAO,CAAC,eAAe;YAC5B,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE;YACxE,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;IACnC,CAAC;IAED,oFAAoF;IACpF,MAAM,CAAC,OAAuB;QAC5B,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACrE,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAgB;QACpB,MAAM,IAAI,GAAI,OAAkC,EAAE,SAAS,CAAC;QAC5D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;QAC5B,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAwC,EAAE,WAAuB;QACrE,OAAO,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE;YACvC,MAAM,EAAE,GAAG,OAAO,EAAE,CAAC;YACrB,IAAI,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAAE,WAAW,EAAE,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gEAAgE;IAChE,OAAO;QACL,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM;YAAE,YAAY,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED;;;OAGG;IACK,iBAAiB,CAAC,SAAiB,EAAE,OAAoB,EAAE,MAAgB;QACjF,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,OAAO;QACzC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAG,KAAK,EAAE,KAAa,EAAiB,EAAE;YACrD,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,CAAC;gBACH,OAAO,GAAG,CAAC,MAAM,IAAA,wBAAa,EAAC,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9E,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,GAAG,KAAK,CAAC;YAClB,CAAC;YACD,IAAI,OAAO,EAAE,CAAC;gBACZ,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAClC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAChC,OAAO;YACT,CAAC;YACD,IAAI,KAAK,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC5D,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAChC,OAAO;YACT,CAAC;YACD,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC,CAAC;QACF,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,EAAE;YACjC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC1B,KAAK,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YAC1B,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACjB,KAAgC,CAAC,KAAK,EAAE,EAAE,CAAC;YAC5C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC,CAAC;QACF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;;YACpC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;CACF;AAzKD,oCAyKC"}
|
package/dist/sign-out.d.ts
CHANGED
|
@@ -70,6 +70,24 @@ export interface SessionTombstonesOptions {
|
|
|
70
70
|
storageKey?: string;
|
|
71
71
|
/** Clock, for tests. */
|
|
72
72
|
now?: () => number;
|
|
73
|
+
/**
|
|
74
|
+
* Where to keep the tombstones instead of `localStorage` — for runtimes
|
|
75
|
+
* without one (React Native, Expo, Electron's main process, a Capacitor app,
|
|
76
|
+
* an extension service worker). Pass the platform's persistent store
|
|
77
|
+
* (AsyncStorage, `expo-secure-store`, `chrome.storage`, Capacitor
|
|
78
|
+
* Preferences…) through a two-method adapter. Reads may be async: await
|
|
79
|
+
* {@link SessionTombstones.ready} before trusting `has()` on a cold start.
|
|
80
|
+
*/
|
|
81
|
+
store?: TombstoneStore;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A key → string store for {@link SessionTombstones} on a platform without
|
|
85
|
+
* `localStorage`. Either method may be sync or async; errors are swallowed
|
|
86
|
+
* (memory still protects the running process).
|
|
87
|
+
*/
|
|
88
|
+
export interface TombstoneStore {
|
|
89
|
+
getItem(key: string): string | null | undefined | Promise<string | null | undefined>;
|
|
90
|
+
setItem(key: string, value: string): void | Promise<void>;
|
|
73
91
|
}
|
|
74
92
|
/**
|
|
75
93
|
* The ids of sessions this browser signed out of, so a reload — or a tab opened
|
|
@@ -90,6 +108,14 @@ export declare class SessionTombstones {
|
|
|
90
108
|
private unsaved;
|
|
91
109
|
private readonly storageKey;
|
|
92
110
|
private readonly now;
|
|
111
|
+
private readonly store;
|
|
112
|
+
/** False until a custom store has been read: writing earlier would drop what it holds. */
|
|
113
|
+
private hydrated;
|
|
114
|
+
/**
|
|
115
|
+
* Settles once the tombstones a {@link SessionTombstonesOptions.store} holds
|
|
116
|
+
* have been read (at once for `localStorage`). Never rejects.
|
|
117
|
+
*/
|
|
118
|
+
readonly ready: Promise<void>;
|
|
93
119
|
constructor(options?: string | SessionTombstonesOptions);
|
|
94
120
|
has(sessionId: string | null | undefined): boolean;
|
|
95
121
|
add(...sessionIds: (string | null | undefined)[]): void;
|
|
@@ -111,5 +137,7 @@ export declare class SessionTombstones {
|
|
|
111
137
|
*/
|
|
112
138
|
private sync;
|
|
113
139
|
private prune;
|
|
140
|
+
/** Read a custom store once, merging anything added before it answered. */
|
|
141
|
+
private hydrate;
|
|
114
142
|
private persist;
|
|
115
143
|
}
|
package/dist/sign-out.js
CHANGED
|
@@ -100,10 +100,26 @@ class SessionTombstones {
|
|
|
100
100
|
unsaved = new Set();
|
|
101
101
|
storageKey;
|
|
102
102
|
now;
|
|
103
|
+
store;
|
|
104
|
+
/** False until a custom store has been read: writing earlier would drop what it holds. */
|
|
105
|
+
hydrated = true;
|
|
106
|
+
/**
|
|
107
|
+
* Settles once the tombstones a {@link SessionTombstonesOptions.store} holds
|
|
108
|
+
* have been read (at once for `localStorage`). Never rejects.
|
|
109
|
+
*/
|
|
110
|
+
ready;
|
|
103
111
|
constructor(options = {}) {
|
|
104
112
|
const opts = typeof options === 'string' ? { storageKey: options } : options;
|
|
105
113
|
this.storageKey = opts.storageKey ?? scopedKey(opts.frontendApi);
|
|
106
114
|
this.now = opts.now ?? Date.now;
|
|
115
|
+
this.store = opts.store;
|
|
116
|
+
if (this.store) {
|
|
117
|
+
this.entries = [];
|
|
118
|
+
this.hydrated = false;
|
|
119
|
+
this.ready = this.hydrate(this.store);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
this.ready = Promise.resolve();
|
|
107
123
|
const stored = readStored(this.storageKey) ?? [];
|
|
108
124
|
const legacy = readLegacy(this.storageKey, this.now()).filter((l) => !stored.some((e) => e.id === l.id));
|
|
109
125
|
this.entries = this.prune([...stored, ...legacy]);
|
|
@@ -156,6 +172,8 @@ class SessionTombstones {
|
|
|
156
172
|
* event). Returns the unsubscribe function; a no-op outside a browser.
|
|
157
173
|
*/
|
|
158
174
|
subscribe(listener) {
|
|
175
|
+
if (this.store)
|
|
176
|
+
return () => undefined;
|
|
159
177
|
const target = globalThis;
|
|
160
178
|
if (typeof target.addEventListener !== 'function')
|
|
161
179
|
return () => undefined;
|
|
@@ -173,7 +191,8 @@ class SessionTombstones {
|
|
|
173
191
|
* entries. With no readable storage, memory is all there is.
|
|
174
192
|
*/
|
|
175
193
|
sync() {
|
|
176
|
-
|
|
194
|
+
// A custom store is written through from memory, which is authoritative.
|
|
195
|
+
const stored = this.store ? null : readStored(this.storageKey);
|
|
177
196
|
if (!stored) {
|
|
178
197
|
this.entries = this.prune(this.entries);
|
|
179
198
|
return;
|
|
@@ -192,9 +211,36 @@ class SessionTombstones {
|
|
|
192
211
|
.sort((a, b) => a.at - b.at);
|
|
193
212
|
return fresh.slice(-TOMBSTONE_LIMIT);
|
|
194
213
|
}
|
|
214
|
+
/** Read a custom store once, merging anything added before it answered. */
|
|
215
|
+
async hydrate(store) {
|
|
216
|
+
let raw;
|
|
217
|
+
try {
|
|
218
|
+
raw = await store.getItem(this.storageKey);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
raw = null;
|
|
222
|
+
}
|
|
223
|
+
const added = this.entries;
|
|
224
|
+
this.entries = this.prune([...parseEntries(raw, this.now()), ...added]);
|
|
225
|
+
this.hydrated = true;
|
|
226
|
+
if (added.length)
|
|
227
|
+
this.persist();
|
|
228
|
+
}
|
|
195
229
|
persist() {
|
|
196
230
|
this.entries = this.prune(this.entries);
|
|
197
231
|
const raw = JSON.stringify(this.entries);
|
|
232
|
+
if (this.store) {
|
|
233
|
+
if (!this.hydrated)
|
|
234
|
+
return;
|
|
235
|
+
const store = this.store;
|
|
236
|
+
try {
|
|
237
|
+
void Promise.resolve(store.setItem(this.storageKey, raw)).catch(() => undefined);
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
/* memory still protects this process */
|
|
241
|
+
}
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
198
244
|
if (write('local', this.storageKey, raw)) {
|
|
199
245
|
// Anything parked in sessionStorage by an earlier failed write is now in localStorage.
|
|
200
246
|
try {
|
package/dist/sign-out.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-out.js","sourceRoot":"","sources":["../src/sign-out.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAwDH,sCAgCC;AAvED,gFAAgF;AAChF,MAAa,YAAa,SAAQ,KAAK;IAC5B,IAAI,GAAG,wBAAiC,CAAC;IAClD,6CAA6C;IACpC,SAAS,CAAS;IAC3B,gFAAgF;IACvE,MAAM,CAAS;IAExB,YAAY,SAAiB,EAAE,MAAc;QAC3C,KAAK,CACH,MAAM,KAAK,CAAC;YACV,CAAC,CAAC,yFAAyF;YAC3F,CAAC,CAAC,mFAAmF,MAAM,IAAI,CAClG,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAjBD,oCAiBC;AAaD;;;;;;;GAOG;AACI,KAAK,UAAU,aAAa,CACjC,OAAgE,EAChE,SAAiB,EACjB,UAAgC,EAAE;IAElC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;IAClD,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACpD,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,OAAO,CAAC,uBAAuB,kBAAkB,CAAC,SAAS,CAAC,SAAS,EAAE;gBACtF,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,CAAC,CAAC;YACX,SAAS;QACX,CAAC;QACD,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;QACzB,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;YAChB,IAAI,eAAmC,CAAC;YACxC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAA2C,CAAC;gBACvF,IAAI,OAAO,IAAI,EAAE,iBAAiB,KAAK,QAAQ;oBAAE,eAAe,GAAG,IAAI,CAAC,iBAAiB,CAAC;YAC5F,CAAC;YAAC,MAAM,CAAC;gBACP,0DAA0D;YAC5D,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9F,CAAC;QACD,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACrD,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;IAC5C,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,aAAa,GAAG,2BAA2B,CAAC;AAClD,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,sFAAsF;AACtF,MAAM,gBAAgB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"sign-out.js","sourceRoot":"","sources":["../src/sign-out.ts"],"names":[],"mappings":"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"}
|