@atlasauth/js 0.5.0 → 0.6.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 +152 -0
- package/dist/api-keys.d.ts +84 -0
- package/dist/api-keys.js +77 -0
- package/dist/api-keys.js.map +1 -0
- package/dist/captcha-controller.d.ts +95 -0
- package/dist/captcha-controller.js +205 -0
- package/dist/captcha-controller.js.map +1 -0
- package/dist/captcha-widget.d.ts +81 -0
- package/dist/captcha-widget.js +263 -0
- package/dist/captcha-widget.js.map +1 -0
- package/dist/config.d.ts +59 -0
- package/dist/config.js +146 -0
- package/dist/config.js.map +1 -0
- package/dist/frontchannel-logout.d.ts +7 -0
- package/dist/frontchannel-logout.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/dist/instance-settings.d.ts +58 -0
- package/dist/instance-settings.js +74 -0
- package/dist/instance-settings.js.map +1 -0
- package/dist/redirect.d.ts +8 -0
- package/dist/redirect.js +21 -2
- package/dist/redirect.js.map +1 -1
- package/dist/sign-out.d.ts +74 -0
- package/dist/sign-out.js +134 -0
- package/dist/sign-out.js.map +1 -0
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -15,6 +15,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.isPermissionKey = exports.isRoleKey = exports.PERMISSION_KEY_PATTERN = exports.ROLE_KEY_PATTERN = exports.ForbiddenError = exports.protectFromClaims = exports.hasFromClaims = exports.evaluate = void 0;
|
|
18
|
+
// Provider configuration checks (frontendApi) shared by every framework SDK.
|
|
19
|
+
__exportStar(require("./config"), exports);
|
|
20
|
+
__exportStar(require("./sign-out"), exports);
|
|
18
21
|
__exportStar(require("./token-cache"), exports);
|
|
19
22
|
__exportStar(require("./tab-election"), exports);
|
|
20
23
|
__exportStar(require("./attempt"), exports);
|
|
@@ -27,6 +30,8 @@ __exportStar(require("./telegram"), exports);
|
|
|
27
30
|
__exportStar(require("./siwe"), exports);
|
|
28
31
|
__exportStar(require("./telemetry"), exports);
|
|
29
32
|
__exportStar(require("./reauth"), exports);
|
|
33
|
+
// End-user API key self-service (`/v1/client/me/api_keys`).
|
|
34
|
+
__exportStar(require("./api-keys"), exports);
|
|
30
35
|
__exportStar(require("./connect"), exports);
|
|
31
36
|
__exportStar(require("./password-reset"), exports);
|
|
32
37
|
__exportStar(require("./check-session"), exports);
|
|
@@ -35,6 +40,13 @@ __exportStar(require("./frontchannel-logout"), exports);
|
|
|
35
40
|
// §5 "captcha anywhere" — fetch the central widget config, render the provider
|
|
36
41
|
// widget, and verify a solved token, all without hardcoding provider keys.
|
|
37
42
|
__exportStar(require("./captcha"), exports);
|
|
43
|
+
// Per-flow captcha widget rendering shared by the prebuilt sign-in / sign-up UIs.
|
|
44
|
+
__exportStar(require("./captcha-widget"), exports);
|
|
45
|
+
// The framework-agnostic captcha lifecycle + sign-in / sign-up submit helpers the
|
|
46
|
+
// prebuilt forms of every framework SDK share, and the instance's public
|
|
47
|
+
// sign-in / sign-up settings (`/v1/appearance`) they are driven by.
|
|
48
|
+
__exportStar(require("./captcha-controller"), exports);
|
|
49
|
+
__exportStar(require("./instance-settings"), exports);
|
|
38
50
|
// Framework-agnostic authorization primitive — `has()`/`evaluate` for any
|
|
39
51
|
// non-React/Next consumer reading org_role/org_permissions off its session.
|
|
40
52
|
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,gDAA8B;AAC9B,iDAA+B;AAC/B,4CAA0B;AAC1B,6CAA2B;AAC3B,yCAAuB;AACvB,2CAAyB;AACzB,mDAAiC;AACjC,4CAA0B;AAC1B,6CAA2B;AAC3B,yCAAuB;AACvB,8CAA4B;AAC5B,2CAAyB;AACzB,4CAA0B;AAC1B,mDAAiC;AACjC,kDAAgC;AAChC,sDAAsD;AACtD,wDAAsC;AACtC,+EAA+E;AAC/E,2EAA2E;AAC3E,4CAA0B;AAC1B,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,gDAA8B;AAC9B,iDAA+B;AAC/B,4CAA0B;AAC1B,6CAA2B;AAC3B,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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { CaptchaFlowKey, FlowCaptcha } from './captcha-widget';
|
|
2
|
+
import type { FapiClient } from './fapi';
|
|
3
|
+
/**
|
|
4
|
+
* The instance's public sign-in / sign-up settings, read from
|
|
5
|
+
* `GET /v1/appearance`. Public data only — the captcha provider + site key +
|
|
6
|
+
* render mode per flow, the extra fields the create-account form collects, and
|
|
7
|
+
* the legal-consent policy. The prebuilt sign-in / sign-up components of every
|
|
8
|
+
* framework SDK read it so that turning a setting on in the dashboard (e.g.
|
|
9
|
+
* captcha on sign-up) takes effect with no code change.
|
|
10
|
+
*/
|
|
11
|
+
export interface InstanceSettings {
|
|
12
|
+
/** Per-flow captcha (`{ enabled: false }` when a flow draws nothing). */
|
|
13
|
+
captchaFlows: Partial<Record<CaptchaFlowKey, FlowCaptcha>>;
|
|
14
|
+
/** Extra fields the create-account form collects, in display order. */
|
|
15
|
+
signUpFields: PublicSignUpField[];
|
|
16
|
+
/** Terms / privacy links and whether agreement is required on sign-up. */
|
|
17
|
+
legal: {
|
|
18
|
+
termsUrl: string | null;
|
|
19
|
+
privacyUrl: string | null;
|
|
20
|
+
required: boolean;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** One configurable sign-up field, exactly as `/v1/appearance` resolves it. */
|
|
24
|
+
export interface PublicSignUpField {
|
|
25
|
+
key: string;
|
|
26
|
+
label: string;
|
|
27
|
+
type: 'text' | 'email' | 'tel' | 'url' | 'number' | 'date' | 'textarea' | 'checkbox' | 'select' | 'radio' | 'multiselect';
|
|
28
|
+
required: boolean;
|
|
29
|
+
placeholder: string | null;
|
|
30
|
+
options: string[];
|
|
31
|
+
builtin: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Read the settings out of a `/v1/appearance` body. Defensive: an older server
|
|
35
|
+
* (no `captchaFlows`) or a malformed body yields empty settings, so the
|
|
36
|
+
* components behave exactly as they did before these settings existed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function instanceSettingsFrom(body: unknown): InstanceSettings;
|
|
39
|
+
/**
|
|
40
|
+
* Fetch the instance's public settings (`GET /v1/appearance`). Null when the
|
|
41
|
+
* server can't be reached or answers with an error — the components then fall
|
|
42
|
+
* back to their defaults, and the server still enforces every gate.
|
|
43
|
+
*/
|
|
44
|
+
export declare function fetchInstanceSettings(client: FapiClient): Promise<InstanceSettings | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Split a create-account form's values into the `startSignUp` input: `email` and
|
|
47
|
+
* `password` are the credentials, everything else is a configured sign-up field
|
|
48
|
+
* (blank optional fields are dropped, so the server never sees empty strings).
|
|
49
|
+
* `consent` is the legal-consent tick (send `true` when no agreement is
|
|
50
|
+
* required — the server only enforces it when the instance opts in).
|
|
51
|
+
*/
|
|
52
|
+
export declare function toSignUpInput(values: Record<string, string>, consent: boolean, captchaToken?: string | null): {
|
|
53
|
+
email: string;
|
|
54
|
+
password: string;
|
|
55
|
+
fields: Record<string, string>;
|
|
56
|
+
consent: boolean;
|
|
57
|
+
captchaToken?: string;
|
|
58
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instanceSettingsFrom = instanceSettingsFrom;
|
|
4
|
+
exports.fetchInstanceSettings = fetchInstanceSettings;
|
|
5
|
+
exports.toSignUpInput = toSignUpInput;
|
|
6
|
+
const NO_LEGAL = { termsUrl: null, privacyUrl: null, required: false };
|
|
7
|
+
const isRecord = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
8
|
+
/**
|
|
9
|
+
* Read the settings out of a `/v1/appearance` body. Defensive: an older server
|
|
10
|
+
* (no `captchaFlows`) or a malformed body yields empty settings, so the
|
|
11
|
+
* components behave exactly as they did before these settings existed.
|
|
12
|
+
*/
|
|
13
|
+
function instanceSettingsFrom(body) {
|
|
14
|
+
const root = isRecord(body) ? body : {};
|
|
15
|
+
const appearance = isRecord(root.appearance) ? root.appearance : {};
|
|
16
|
+
const captchaFlows = {};
|
|
17
|
+
if (isRecord(root.captchaFlows)) {
|
|
18
|
+
for (const key of ['signUp', 'signIn', 'guest', 'passwordReset']) {
|
|
19
|
+
const flow = root.captchaFlows[key];
|
|
20
|
+
if (!isRecord(flow))
|
|
21
|
+
continue;
|
|
22
|
+
captchaFlows[key] = {
|
|
23
|
+
enabled: flow.enabled === true,
|
|
24
|
+
...(typeof flow.provider === 'string' ? { provider: flow.provider } : {}),
|
|
25
|
+
...(typeof flow.siteKey === 'string' ? { siteKey: flow.siteKey } : {}),
|
|
26
|
+
...(flow.widget === 'invisible' || flow.widget === 'managed' || flow.widget === 'visible'
|
|
27
|
+
? { widget: flow.widget }
|
|
28
|
+
: {}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const signUpFields = Array.isArray(appearance.signUpFields)
|
|
33
|
+
? appearance.signUpFields.filter((f) => isRecord(f) && typeof f.key === 'string' && typeof f.label === 'string')
|
|
34
|
+
: [];
|
|
35
|
+
const legalRaw = isRecord(appearance.legal) ? appearance.legal : null;
|
|
36
|
+
const legal = legalRaw
|
|
37
|
+
? {
|
|
38
|
+
termsUrl: typeof legalRaw.termsUrl === 'string' ? legalRaw.termsUrl : null,
|
|
39
|
+
privacyUrl: typeof legalRaw.privacyUrl === 'string' ? legalRaw.privacyUrl : null,
|
|
40
|
+
required: legalRaw.required === true,
|
|
41
|
+
}
|
|
42
|
+
: NO_LEGAL;
|
|
43
|
+
return { captchaFlows, signUpFields, legal };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Fetch the instance's public settings (`GET /v1/appearance`). Null when the
|
|
47
|
+
* server can't be reached or answers with an error — the components then fall
|
|
48
|
+
* back to their defaults, and the server still enforces every gate.
|
|
49
|
+
*/
|
|
50
|
+
async function fetchInstanceSettings(client) {
|
|
51
|
+
try {
|
|
52
|
+
const response = await client.get('/v1/appearance');
|
|
53
|
+
return response.ok ? instanceSettingsFrom(response.data) : null;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Split a create-account form's values into the `startSignUp` input: `email` and
|
|
61
|
+
* `password` are the credentials, everything else is a configured sign-up field
|
|
62
|
+
* (blank optional fields are dropped, so the server never sees empty strings).
|
|
63
|
+
* `consent` is the legal-consent tick (send `true` when no agreement is
|
|
64
|
+
* required — the server only enforces it when the instance opts in).
|
|
65
|
+
*/
|
|
66
|
+
function toSignUpInput(values, consent, captchaToken) {
|
|
67
|
+
const { email = '', password = '', ...rest } = values;
|
|
68
|
+
const fields = {};
|
|
69
|
+
for (const [key, value] of Object.entries(rest))
|
|
70
|
+
if (value)
|
|
71
|
+
fields[key] = value;
|
|
72
|
+
return { email, password, fields, consent, ...(captchaToken ? { captchaToken } : {}) };
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=instance-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-settings.js","sourceRoot":"","sources":["../src/instance-settings.ts"],"names":[],"mappings":"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"}
|
package/dist/redirect.d.ts
CHANGED
|
@@ -73,6 +73,14 @@ export interface PopupStartResult extends PopupOutcome {
|
|
|
73
73
|
/** True when the popup was blocked — the caller falls back to a full redirect. */
|
|
74
74
|
blocked?: boolean;
|
|
75
75
|
authorizationUrl?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Set (with `error: 'popup_closed'`) when the popup closed without posting a
|
|
78
|
+
* result but the Frontend API now reports a signed-in session — e.g. the
|
|
79
|
+
* browser severed `window.opener`, so the popup finished the sign-in itself.
|
|
80
|
+
* The caller should re-boot its session state rather than treat it as a
|
|
81
|
+
* cancel.
|
|
82
|
+
*/
|
|
83
|
+
sessionDetected?: boolean;
|
|
76
84
|
}
|
|
77
85
|
/**
|
|
78
86
|
* Start a POPUP OAuth sign-in (GSI-style). Opens the provider authorize URL in a
|
package/dist/redirect.js
CHANGED
|
@@ -133,13 +133,32 @@ async function startOauthPopup(client, input, handlers = {}) {
|
|
|
133
133
|
settle(outcome);
|
|
134
134
|
};
|
|
135
135
|
// The user closing the popup without finishing is a cancellation, not a hang.
|
|
136
|
+
// But a popup can also close (or look closed to us) after completing the
|
|
137
|
+
// sign-in itself: when a browser severs `window.opener` (a Cross-Origin-
|
|
138
|
+
// Opener-Policy on some hop, a privacy feature), the callback page cannot
|
|
139
|
+
// post back and finishes via the ordinary redirect instead, which sets the
|
|
140
|
+
// session cookies. So check the session once before calling it a cancel.
|
|
141
|
+
let probing = false;
|
|
136
142
|
const timer = setInterval(() => {
|
|
137
|
-
if (popup
|
|
138
|
-
|
|
143
|
+
if (!popup || !popup.closed || probing || done)
|
|
144
|
+
return;
|
|
145
|
+
probing = true;
|
|
146
|
+
clearInterval(timer);
|
|
147
|
+
void probeSession(client).then((signedIn) => settle(signedIn ? { error: 'popup_closed', sessionDetected: true } : { error: 'popup_closed' }));
|
|
139
148
|
}, 500);
|
|
140
149
|
window.addEventListener('message', onMessage);
|
|
141
150
|
});
|
|
142
151
|
}
|
|
152
|
+
/** One `GET /v1/client`: is there a signed-in session on this browser now? */
|
|
153
|
+
async function probeSession(client) {
|
|
154
|
+
try {
|
|
155
|
+
const res = await client.get('/v1/client');
|
|
156
|
+
return Boolean(res.ok && res.data?.session && res.data?.user);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
143
162
|
/**
|
|
144
163
|
* Read Atlas's redirect params from a query string (`window.location.search`).
|
|
145
164
|
*
|
package/dist/redirect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redirect.js","sourceRoot":"","sources":["../src/redirect.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAcH,gDAUC;AAiCD,wDAuBC;
|
|
1
|
+
{"version":3,"file":"redirect.js","sourceRoot":"","sources":["../src/redirect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign-out that cannot be silently undone.
|
|
3
|
+
*
|
|
4
|
+
* A browser sign-out has two halves: revoke the session on the Frontend API
|
|
5
|
+
* (which also clears the HttpOnly session cookies), and drop the local state.
|
|
6
|
+
* If the first half fails — a network blip, a 5xx — the refresh cookie is still
|
|
7
|
+
* valid, and the very next `reload()` (a tab regaining focus, a sibling tab's
|
|
8
|
+
* broadcast, a failed token refresh) re-boots straight back into the session
|
|
9
|
+
* the user just signed out of.
|
|
10
|
+
*
|
|
11
|
+
* These helpers close that gap: {@link revokeSession} retries once and reports
|
|
12
|
+
* what the server did (including the parked session it promoted, if any), and
|
|
13
|
+
* {@link SessionTombstones} remembers the ids of sessions this tab signed out of
|
|
14
|
+
* so a reload never re-adopts one, even when the revoke never reached the
|
|
15
|
+
* server.
|
|
16
|
+
*/
|
|
17
|
+
/** What `signOut()` resolves with. */
|
|
18
|
+
export interface SignOutResult {
|
|
19
|
+
/**
|
|
20
|
+
* Whether the Frontend API confirmed the session was revoked. `false` means
|
|
21
|
+
* the local sign-out happened but the server could not be reached (or
|
|
22
|
+
* refused); the session id is remembered so this browser never re-adopts it.
|
|
23
|
+
*/
|
|
24
|
+
revoked: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Set when another account signed in on this browser was promoted to active
|
|
27
|
+
* (multi-session). The SDK adopts it, so the user stays signed in as it.
|
|
28
|
+
*/
|
|
29
|
+
activeSessionId?: string;
|
|
30
|
+
}
|
|
31
|
+
/** The error `signOut({ strict: true })` rejects with when the revoke fails. */
|
|
32
|
+
export declare class SignOutError extends Error {
|
|
33
|
+
readonly code: "SIGN_OUT_REVOKE_FAILED";
|
|
34
|
+
/** The session that could not be revoked. */
|
|
35
|
+
readonly sessionId: string;
|
|
36
|
+
/** The last HTTP status from the Frontend API, or `0` for a network failure. */
|
|
37
|
+
readonly status: number;
|
|
38
|
+
constructor(sessionId: string, status: number);
|
|
39
|
+
}
|
|
40
|
+
/** The outcome of {@link revokeSession}. */
|
|
41
|
+
export interface RevokeSessionOutcome {
|
|
42
|
+
revoked: boolean;
|
|
43
|
+
/** The last HTTP status (`0` when the request never got a response). */
|
|
44
|
+
status: number;
|
|
45
|
+
/** The successful response, for front-channel logout. */
|
|
46
|
+
response?: Response;
|
|
47
|
+
/** A parked session the server promoted to active, if any. */
|
|
48
|
+
activeSessionId?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* `POST /v1/client/sessions/:id/revoke`, retried once on a network failure or a
|
|
52
|
+
* 5xx/429. A 4xx other than 429 is not retried: the server understood and said
|
|
53
|
+
* no. A 404 counts as revoked — the session is already gone.
|
|
54
|
+
*
|
|
55
|
+
* `request` is the caller's credentialed FAPI fetch (cookies included), so the
|
|
56
|
+
* server can clear the session cookies on this browser.
|
|
57
|
+
*/
|
|
58
|
+
export declare function revokeSession(request: (path: string, init?: RequestInit) => Promise<Response>, sessionId: string, options?: {
|
|
59
|
+
retries?: number;
|
|
60
|
+
}): Promise<RevokeSessionOutcome>;
|
|
61
|
+
/**
|
|
62
|
+
* The ids of sessions this tab signed out of. Held in memory and mirrored to
|
|
63
|
+
* `sessionStorage` (so a page reload in the same tab remembers them); sibling
|
|
64
|
+
* tabs learn them through the SDK's cross-tab session broadcast. Never holds a
|
|
65
|
+
* token — a session id is not a credential.
|
|
66
|
+
*/
|
|
67
|
+
export declare class SessionTombstones {
|
|
68
|
+
private readonly storageKey;
|
|
69
|
+
private readonly ids;
|
|
70
|
+
constructor(storageKey?: string);
|
|
71
|
+
has(sessionId: string | null | undefined): boolean;
|
|
72
|
+
add(...sessionIds: (string | null | undefined)[]): void;
|
|
73
|
+
list(): string[];
|
|
74
|
+
}
|
package/dist/sign-out.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Sign-out that cannot be silently undone.
|
|
4
|
+
*
|
|
5
|
+
* A browser sign-out has two halves: revoke the session on the Frontend API
|
|
6
|
+
* (which also clears the HttpOnly session cookies), and drop the local state.
|
|
7
|
+
* If the first half fails — a network blip, a 5xx — the refresh cookie is still
|
|
8
|
+
* valid, and the very next `reload()` (a tab regaining focus, a sibling tab's
|
|
9
|
+
* broadcast, a failed token refresh) re-boots straight back into the session
|
|
10
|
+
* the user just signed out of.
|
|
11
|
+
*
|
|
12
|
+
* These helpers close that gap: {@link revokeSession} retries once and reports
|
|
13
|
+
* what the server did (including the parked session it promoted, if any), and
|
|
14
|
+
* {@link SessionTombstones} remembers the ids of sessions this tab signed out of
|
|
15
|
+
* so a reload never re-adopts one, even when the revoke never reached the
|
|
16
|
+
* server.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.SessionTombstones = exports.SignOutError = void 0;
|
|
20
|
+
exports.revokeSession = revokeSession;
|
|
21
|
+
/** The error `signOut({ strict: true })` rejects with when the revoke fails. */
|
|
22
|
+
class SignOutError extends Error {
|
|
23
|
+
code = 'SIGN_OUT_REVOKE_FAILED';
|
|
24
|
+
/** The session that could not be revoked. */
|
|
25
|
+
sessionId;
|
|
26
|
+
/** The last HTTP status from the Frontend API, or `0` for a network failure. */
|
|
27
|
+
status;
|
|
28
|
+
constructor(sessionId, status) {
|
|
29
|
+
super(status === 0
|
|
30
|
+
? 'Signed out locally, but the Atlas Frontend API could not be reached to end the session.'
|
|
31
|
+
: `Signed out locally, but the Atlas Frontend API refused to end the session (HTTP ${status}).`);
|
|
32
|
+
this.name = 'SignOutError';
|
|
33
|
+
this.sessionId = sessionId;
|
|
34
|
+
this.status = status;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.SignOutError = SignOutError;
|
|
38
|
+
/**
|
|
39
|
+
* `POST /v1/client/sessions/:id/revoke`, retried once on a network failure or a
|
|
40
|
+
* 5xx/429. A 4xx other than 429 is not retried: the server understood and said
|
|
41
|
+
* no. A 404 counts as revoked — the session is already gone.
|
|
42
|
+
*
|
|
43
|
+
* `request` is the caller's credentialed FAPI fetch (cookies included), so the
|
|
44
|
+
* server can clear the session cookies on this browser.
|
|
45
|
+
*/
|
|
46
|
+
async function revokeSession(request, sessionId, options = {}) {
|
|
47
|
+
const retries = Math.max(0, options.retries ?? 1);
|
|
48
|
+
let status = 0;
|
|
49
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
50
|
+
let response;
|
|
51
|
+
try {
|
|
52
|
+
response = await request(`/v1/client/sessions/${encodeURIComponent(sessionId)}/revoke`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
status = 0;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
status = response.status;
|
|
61
|
+
if (response.ok) {
|
|
62
|
+
let activeSessionId;
|
|
63
|
+
try {
|
|
64
|
+
const body = (await response.clone().json());
|
|
65
|
+
if (typeof body?.active_session_id === 'string')
|
|
66
|
+
activeSessionId = body.active_session_id;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// A body we cannot read still means the revoke succeeded.
|
|
70
|
+
}
|
|
71
|
+
return { revoked: true, status, response, ...(activeSessionId ? { activeSessionId } : {}) };
|
|
72
|
+
}
|
|
73
|
+
if (status === 404)
|
|
74
|
+
return { revoked: true, status };
|
|
75
|
+
if (status < 500 && status !== 429)
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
return { revoked: false, status };
|
|
79
|
+
}
|
|
80
|
+
const TOMBSTONE_KEY = 'atlas:signed-out-sessions';
|
|
81
|
+
const TOMBSTONE_LIMIT = 20;
|
|
82
|
+
/**
|
|
83
|
+
* The ids of sessions this tab signed out of. Held in memory and mirrored to
|
|
84
|
+
* `sessionStorage` (so a page reload in the same tab remembers them); sibling
|
|
85
|
+
* tabs learn them through the SDK's cross-tab session broadcast. Never holds a
|
|
86
|
+
* token — a session id is not a credential.
|
|
87
|
+
*/
|
|
88
|
+
class SessionTombstones {
|
|
89
|
+
storageKey;
|
|
90
|
+
ids;
|
|
91
|
+
constructor(storageKey = TOMBSTONE_KEY) {
|
|
92
|
+
this.storageKey = storageKey;
|
|
93
|
+
this.ids = readStored(storageKey);
|
|
94
|
+
}
|
|
95
|
+
has(sessionId) {
|
|
96
|
+
return typeof sessionId === 'string' && this.ids.includes(sessionId);
|
|
97
|
+
}
|
|
98
|
+
add(...sessionIds) {
|
|
99
|
+
let changed = false;
|
|
100
|
+
for (const id of sessionIds) {
|
|
101
|
+
if (typeof id !== 'string' || !id || this.ids.includes(id))
|
|
102
|
+
continue;
|
|
103
|
+
this.ids.push(id);
|
|
104
|
+
changed = true;
|
|
105
|
+
}
|
|
106
|
+
if (!changed)
|
|
107
|
+
return;
|
|
108
|
+
while (this.ids.length > TOMBSTONE_LIMIT)
|
|
109
|
+
this.ids.shift();
|
|
110
|
+
try {
|
|
111
|
+
globalThis.sessionStorage?.setItem(this.storageKey, JSON.stringify(this.ids));
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Private mode / storage disabled: memory still protects this tab.
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
list() {
|
|
118
|
+
return [...this.ids];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.SessionTombstones = SessionTombstones;
|
|
122
|
+
function readStored(key) {
|
|
123
|
+
try {
|
|
124
|
+
const raw = globalThis.sessionStorage?.getItem(key);
|
|
125
|
+
const parsed = raw ? JSON.parse(raw) : [];
|
|
126
|
+
return Array.isArray(parsed)
|
|
127
|
+
? parsed.filter((v) => typeof v === 'string').slice(-TOMBSTONE_LIMIT)
|
|
128
|
+
: [];
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=sign-out.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-out.js","sourceRoot":"","sources":["../src/sign-out.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlasauth/js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"devDependencies": {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist",
|
|
19
|
-
"README.md"
|
|
19
|
+
"README.md",
|
|
20
|
+
"CHANGELOG.md"
|
|
20
21
|
],
|
|
21
22
|
"scripts": {
|
|
22
23
|
"build": "tsc -p tsconfig.build.json",
|