@atlasauth/js 0.15.0 → 0.16.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/account-hints.d.ts +53 -0
  3. package/dist/account-hints.js +114 -0
  4. package/dist/account-hints.js.map +1 -0
  5. package/dist/attempt.d.ts +3 -1
  6. package/dist/attempt.js.map +1 -1
  7. package/dist/captcha-controller.d.ts +21 -0
  8. package/dist/captcha-controller.js +35 -0
  9. package/dist/captcha-controller.js.map +1 -1
  10. package/dist/environment.d.ts +48 -0
  11. package/dist/environment.js +54 -1
  12. package/dist/environment.js.map +1 -1
  13. package/dist/fapi.d.ts +29 -0
  14. package/dist/fapi.js +90 -7
  15. package/dist/fapi.js.map +1 -1
  16. package/dist/impersonation.d.ts +44 -0
  17. package/dist/impersonation.js +47 -0
  18. package/dist/impersonation.js.map +1 -0
  19. package/dist/index.d.ts +9 -0
  20. package/dist/index.js +16 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/instance-settings.d.ts +7 -1
  23. package/dist/instance-settings.js +15 -1
  24. package/dist/instance-settings.js.map +1 -1
  25. package/dist/mfa-enrollment.d.ts +154 -0
  26. package/dist/mfa-enrollment.js +288 -0
  27. package/dist/mfa-enrollment.js.map +1 -0
  28. package/dist/organization-audit-log.d.ts +63 -0
  29. package/dist/organization-audit-log.js +96 -0
  30. package/dist/organization-audit-log.js.map +1 -0
  31. package/dist/organizations.d.ts +330 -0
  32. package/dist/organizations.js +375 -0
  33. package/dist/organizations.js.map +1 -0
  34. package/dist/passkey-sign-in.d.ts +40 -0
  35. package/dist/passkey-sign-in.js +41 -0
  36. package/dist/passkey-sign-in.js.map +1 -0
  37. package/dist/passkey-sign-up.d.ts +46 -0
  38. package/dist/passkey-sign-up.js +98 -0
  39. package/dist/passkey-sign-up.js.map +1 -0
  40. package/dist/password-reset.d.ts +12 -0
  41. package/dist/password-reset.js +15 -0
  42. package/dist/password-reset.js.map +1 -1
  43. package/dist/phone-numbers.d.ts +43 -0
  44. package/dist/phone-numbers.js +65 -0
  45. package/dist/phone-numbers.js.map +1 -0
  46. package/dist/profile.d.ts +24 -0
  47. package/dist/profile.js +24 -0
  48. package/dist/profile.js.map +1 -0
  49. package/dist/referral-detect.js +1 -1
  50. package/dist/referral.d.ts +1 -1
  51. package/dist/referral.js +3 -2
  52. package/dist/referral.js.map +1 -1
  53. package/dist/sign-out-guard.d.ts +8 -0
  54. package/dist/sign-out-guard.js +15 -0
  55. package/dist/sign-out-guard.js.map +1 -1
  56. package/dist/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/package.json +2 -2
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mfaChoicesFrom = mfaChoicesFrom;
4
+ exports.prepareMfaEnrollment = prepareMfaEnrollment;
5
+ exports.groupSetupKey = groupSetupKey;
6
+ exports.createMfaEnrollment = createMfaEnrollment;
7
+ exports.enrollmentSubmitKey = enrollmentSubmitKey;
8
+ exports.prepareSecondFactorCode = prepareSecondFactorCode;
9
+ exports.recoveryCodesFrom = recoveryCodesFrom;
10
+ exports.pendingRecoveryCodes = pendingRecoveryCodes;
11
+ exports.acknowledgeRecoveryCodes = acknowledgeRecoveryCodes;
12
+ exports.recoveryCodesText = recoveryCodesText;
13
+ exports.copyRecoveryCodes = copyRecoveryCodes;
14
+ exports.canDownloadRecoveryCodes = canDownloadRecoveryCodes;
15
+ exports.downloadRecoveryCodes = downloadRecoveryCodes;
16
+ /**
17
+ * The MFA choices from the instance's settings (`InstanceSettings.account`).
18
+ * A server that does not publish `account.mfa` gets the authenticator app only,
19
+ * with two code fields (an instance asking for one code uses the first). Texted
20
+ * codes are offered only where the server says the app can text, so nobody is
21
+ * shown an option that cannot work.
22
+ */
23
+ function mfaChoicesFrom(account) {
24
+ const mfa = account?.mfa;
25
+ return {
26
+ totp: mfa ? mfa.totp : true,
27
+ sms: mfa ? mfa.sms : false,
28
+ emailCode: account?.emailSecondFactor === true,
29
+ totpCodes: mfa ? mfa.totpCodes : 2,
30
+ };
31
+ }
32
+ /** `POST /v1/client/sign_ins/:id/prepare_mfa_enrollment`. */
33
+ async function prepareMfaEnrollment(client, attemptId, input) {
34
+ const response = await client.post(`/v1/client/sign_ins/${encodeURIComponent(attemptId)}/prepare_mfa_enrollment`, input.strategy === 'phone_code' ? { strategy: 'phone_code', phone: input.phone } : { strategy: 'totp' });
35
+ const d = response.data;
36
+ if (!response.ok || !d || typeof d.factor_id !== 'string') {
37
+ return { enrollment: null, errors: response.errors, status: response.status };
38
+ }
39
+ const str = (v) => (typeof v === 'string' && v ? v : undefined);
40
+ const enrollment = { strategy: input.strategy, factorId: d.factor_id };
41
+ if (input.strategy === 'totp') {
42
+ if (str(d.secret))
43
+ enrollment.secret = str(d.secret);
44
+ if (str(d.uri))
45
+ enrollment.uri = str(d.uri);
46
+ // Only an image URL a page can show safely.
47
+ const qr = str(d.qr_code);
48
+ if (qr && /^data:image\//.test(qr))
49
+ enrollment.qrCode = qr;
50
+ }
51
+ else if (str(d.phone)) {
52
+ enrollment.phone = str(d.phone);
53
+ }
54
+ return { enrollment, errors: [], status: response.status };
55
+ }
56
+ /** A setup key split into groups of four, for reading off the screen. */
57
+ function groupSetupKey(secret) {
58
+ return secret.replace(/\s+/g, '').replace(/(.{4})(?=.)/g, '$1 ');
59
+ }
60
+ /**
61
+ * A refusal that means the factor cannot be set up here at all (turned off, or
62
+ * the app cannot text), as opposed to a mistake the user can fix (a bad number).
63
+ */
64
+ function factorUnavailable(errors, status) {
65
+ if (status === 409)
66
+ return true;
67
+ return errors.some((e) => e.code === 'STRATEGY_DISABLED' || e.param === 'strategy');
68
+ }
69
+ /** The enrolment step's state and requests, independent of any framework. */
70
+ function createMfaEnrollment(client, choices) {
71
+ const read = () => (typeof choices === 'function' ? choices() : choices);
72
+ const offered = (c) => {
73
+ const out = [];
74
+ if (c.totp)
75
+ out.push('totp');
76
+ if (c.sms)
77
+ out.push('phone_code');
78
+ // Nothing on offer is a misconfiguration; the authenticator app is the
79
+ // default the server enrols, so ask for it rather than draw nothing.
80
+ return out.length ? out : ['totp'];
81
+ };
82
+ const empty = () => {
83
+ const c = read();
84
+ return { options: offered(c), method: null, enrollment: null, busy: false, errors: [], totpCodes: c.totpCodes };
85
+ };
86
+ let state = empty();
87
+ let attemptId = null;
88
+ let phone = '';
89
+ let generation = 0;
90
+ const listeners = new Set();
91
+ const set = (patch) => {
92
+ state = { ...state, ...patch };
93
+ for (const listener of listeners)
94
+ listener(state);
95
+ };
96
+ const prepare = async (input) => {
97
+ const id = attemptId;
98
+ if (!id)
99
+ return;
100
+ const gen = generation;
101
+ set({ busy: true, errors: [] });
102
+ const out = await prepareMfaEnrollment(client, id, input);
103
+ if (gen !== generation)
104
+ return; // the flow moved on meanwhile
105
+ if (out.enrollment) {
106
+ set({ busy: false, enrollment: out.enrollment, errors: [] });
107
+ return;
108
+ }
109
+ if (factorUnavailable(out.errors, out.status) && state.options.length > 1) {
110
+ // Not available after all: drop it and carry on with what is left.
111
+ const options = state.options.filter((m) => m !== input.strategy);
112
+ set({ busy: false, options, method: null, enrollment: null, errors: out.errors });
113
+ if (options.length === 1)
114
+ await controller.choose(options[0]);
115
+ return;
116
+ }
117
+ set({ busy: false, errors: out.errors });
118
+ };
119
+ const controller = {
120
+ get state() {
121
+ return state;
122
+ },
123
+ subscribe(listener) {
124
+ listeners.add(listener);
125
+ return () => listeners.delete(listener);
126
+ },
127
+ start(id) {
128
+ if (id === attemptId)
129
+ return;
130
+ generation += 1;
131
+ attemptId = id;
132
+ phone = '';
133
+ state = empty();
134
+ set({});
135
+ if (state.options.length === 1)
136
+ void controller.choose(state.options[0]);
137
+ },
138
+ async choose(method) {
139
+ if (!state.options.includes(method))
140
+ return;
141
+ set({ method, enrollment: null, errors: [] });
142
+ if (method === 'totp')
143
+ await prepare({ strategy: 'totp' });
144
+ },
145
+ async sendPhone(value) {
146
+ const number = value.trim();
147
+ if (!number) {
148
+ set({ errors: [{ code: 'FORM_PARAM_MISSING', message: 'A phone number is required.', param: 'phone' }] });
149
+ return;
150
+ }
151
+ phone = number;
152
+ await prepare({ strategy: 'phone_code', phone: number });
153
+ },
154
+ async resend() {
155
+ if (state.method === 'phone_code' && phone)
156
+ await prepare({ strategy: 'phone_code', phone });
157
+ },
158
+ async submit(values) {
159
+ if (state.method === 'phone_code' && !state.enrollment) {
160
+ await controller.sendPhone(values.phone ?? '');
161
+ return null;
162
+ }
163
+ const e = state.enrollment;
164
+ if (!e)
165
+ return null;
166
+ const out = e.strategy === 'phone_code'
167
+ ? { factorId: e.factorId, enrollStrategy: 'phone_code', code: values.code ?? '' }
168
+ : { factorId: e.factorId, code: values.code ?? '', secondCode: values.secondCode ?? '' };
169
+ return out;
170
+ },
171
+ reset() {
172
+ generation += 1;
173
+ attemptId = null;
174
+ phone = '';
175
+ state = empty();
176
+ set({});
177
+ },
178
+ };
179
+ return controller;
180
+ }
181
+ /**
182
+ * The enrolment form's submit button: the catalog key of its label, or null
183
+ * when the step has nothing to submit yet (choosing a method, or fetching the
184
+ * authenticator's setup). `mfa.sendCode` before a code was texted.
185
+ */
186
+ function enrollmentSubmitKey(state) {
187
+ if (state.method === null)
188
+ return null;
189
+ if (state.method === 'phone_code' && !state.enrollment)
190
+ return 'mfa.sendCode';
191
+ return state.enrollment ? 'signIn.submit' : null;
192
+ }
193
+ /**
194
+ * "Text me a code instead" / "Email me a code instead" at the second-factor
195
+ * step: `prepare_second_factor` sends the code (to the account's enrolled phone,
196
+ * or its verified email), and it is then submitted like any second-factor code.
197
+ * Resolves the masked number / address; throws with the server's message (and
198
+ * `code`) when the channel is not offered for this sign-in.
199
+ */
200
+ async function prepareSecondFactorCode(client, attemptId, strategy) {
201
+ const response = await client.post(`/v1/client/sign_ins/${encodeURIComponent(attemptId)}/prepare_second_factor`, { strategy });
202
+ if (!response.ok) {
203
+ const first = response.errors[0];
204
+ const error = new Error(first?.message ?? 'Could not send the code.');
205
+ if (first?.code)
206
+ error.code = first.code;
207
+ throw error;
208
+ }
209
+ return { sentTo: typeof response.data?.sent_to === 'string' ? response.data.sent_to : '' };
210
+ }
211
+ /**
212
+ * The recovery codes in an `attempt_mfa_enrollment` response (`backup_codes`):
213
+ * the non-empty strings, or null when there are none.
214
+ */
215
+ function recoveryCodesFrom(value) {
216
+ if (!Array.isArray(value))
217
+ return null;
218
+ const codes = value.filter((c) => typeof c === 'string' && c.length > 0);
219
+ return codes.length ? codes : null;
220
+ }
221
+ /**
222
+ * The recovery codes to show before the sign-in finishes: set while an
223
+ * enrolment has completed the attempt and the user has not yet said they saved
224
+ * them. A component that gets codes here draws them (once) instead of the
225
+ * finished step, and holds the ticket exchange until
226
+ * {@link acknowledgeRecoveryCodes}.
227
+ */
228
+ function pendingRecoveryCodes(state) {
229
+ if (state.attempt?.status !== 'complete')
230
+ return null;
231
+ return state.recoveryCodes && state.recoveryCodes.length ? state.recoveryCodes : null;
232
+ }
233
+ /** The flow once the user has saved the codes: forgotten, ready for the ticket exchange. */
234
+ function acknowledgeRecoveryCodes(state) {
235
+ return { ...state, recoveryCodes: null };
236
+ }
237
+ /** The codes as a text file's contents: one per line. */
238
+ function recoveryCodesText(codes) {
239
+ return `${codes.join('\n')}\n`;
240
+ }
241
+ /** Copy the codes (one per line) to the clipboard. Resolves whether it worked. */
242
+ async function copyRecoveryCodes(codes) {
243
+ const clipboard = globalThis.navigator
244
+ ?.clipboard;
245
+ if (!clipboard?.writeText)
246
+ return false;
247
+ try {
248
+ await clipboard.writeText(codes.join('\n'));
249
+ return true;
250
+ }
251
+ catch {
252
+ return false;
253
+ }
254
+ }
255
+ /** Whether this page can offer the codes as a download. */
256
+ function canDownloadRecoveryCodes() {
257
+ return (typeof document !== 'undefined' &&
258
+ typeof Blob !== 'undefined' &&
259
+ typeof URL !== 'undefined' &&
260
+ typeof URL.createObjectURL === 'function');
261
+ }
262
+ /**
263
+ * Save the codes as a .txt file (`recovery-codes.txt`) through a temporary
264
+ * object URL, revoked straight after. Returns whether the download started.
265
+ */
266
+ function downloadRecoveryCodes(codes, filename = 'recovery-codes.txt') {
267
+ if (!canDownloadRecoveryCodes())
268
+ return false;
269
+ const url = URL.createObjectURL(new Blob([recoveryCodesText(codes)], { type: 'text/plain;charset=utf-8' }));
270
+ try {
271
+ const a = document.createElement('a');
272
+ a.href = url;
273
+ a.download = filename;
274
+ a.rel = 'noopener';
275
+ a.style.display = 'none';
276
+ (document.body ?? document.documentElement).appendChild(a);
277
+ a.click();
278
+ a.remove();
279
+ return true;
280
+ }
281
+ catch {
282
+ return false;
283
+ }
284
+ finally {
285
+ setTimeout(() => URL.revokeObjectURL(url), 0);
286
+ }
287
+ }
288
+ //# sourceMappingURL=mfa-enrollment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-enrollment.js","sourceRoot":"","sources":["../src/mfa-enrollment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import type { FapiClient } from './fapi';
2
+ /**
3
+ * An organization's audit trail, for its admins — the framework-agnostic state
4
+ * behind `<OrganizationAuditLog/>` in every framework SDK, so React, Vue,
5
+ * Svelte, Solid and Angular page through `GET /v1/client/organizations/:id/
6
+ * audit_logs` the same way.
7
+ */
8
+ /** One entry of an organization's audit trail, as the Frontend API returns it. */
9
+ export interface OrganizationAuditLogEntry {
10
+ object: 'audit_log';
11
+ id: string;
12
+ action: string;
13
+ actor: {
14
+ type: string;
15
+ id: string | null;
16
+ };
17
+ target: {
18
+ type: string | null;
19
+ id: string | null;
20
+ };
21
+ organization_id: string;
22
+ ip: string | null;
23
+ user_agent: string | null;
24
+ metadata: Record<string, unknown>;
25
+ created_at: number;
26
+ }
27
+ export interface OrganizationAuditLogOptions {
28
+ /** Entries per page (the server caps it). Default 20. */
29
+ pageSize?: number;
30
+ /** Narrow to one action, or a prefix ending in `.` (`organization_membership.`). */
31
+ action?: string;
32
+ }
33
+ export interface OrganizationAuditLogState {
34
+ entries: OrganizationAuditLogEntry[];
35
+ loading: boolean;
36
+ /** The signed-in member may not read this organization's log: hide the surface. */
37
+ forbidden: boolean;
38
+ hasMore: boolean;
39
+ }
40
+ export interface OrganizationAuditLogController {
41
+ getState(): OrganizationAuditLogState;
42
+ /** Called with the new state on every change; returns the unsubscribe. */
43
+ subscribe(listener: (state: OrganizationAuditLogState) => void): () => void;
44
+ /** Show `organizationId`'s log from its newest entry (null clears it). */
45
+ load(organizationId: string | null): Promise<void>;
46
+ /** Append the next page, when there is one. */
47
+ loadMore(): Promise<void>;
48
+ /** Re-read the current organization's log from its newest entry. */
49
+ reload(): Promise<void>;
50
+ }
51
+ export declare const EMPTY_ORGANIZATION_AUDIT_LOG: OrganizationAuditLogState;
52
+ /** The Frontend API path for one page of an organization's audit log. */
53
+ export declare function organizationAuditLogPath(organizationId: string, options?: OrganizationAuditLogOptions & {
54
+ startingAfter?: string | null;
55
+ }): string;
56
+ /** `organization_invitation.created` → `Organization invitation created`. */
57
+ export declare function describeAuditAction(action: string): string;
58
+ /**
59
+ * Page through an organization's audit log. A response for an organization
60
+ * that is no longer the one shown (the user switched while it was in flight)
61
+ * is dropped, so a slow page never lands in the wrong organization's list.
62
+ */
63
+ export declare function createOrganizationAuditLog(client: Pick<FapiClient, 'get'>, options?: OrganizationAuditLogOptions): OrganizationAuditLogController;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EMPTY_ORGANIZATION_AUDIT_LOG = void 0;
4
+ exports.organizationAuditLogPath = organizationAuditLogPath;
5
+ exports.describeAuditAction = describeAuditAction;
6
+ exports.createOrganizationAuditLog = createOrganizationAuditLog;
7
+ exports.EMPTY_ORGANIZATION_AUDIT_LOG = Object.freeze({
8
+ entries: [],
9
+ loading: false,
10
+ forbidden: false,
11
+ hasMore: false,
12
+ });
13
+ /** The Frontend API path for one page of an organization's audit log. */
14
+ function organizationAuditLogPath(organizationId, options = {}) {
15
+ const params = new URLSearchParams({ limit: String(options.pageSize ?? 20) });
16
+ if (options.startingAfter)
17
+ params.set('starting_after', options.startingAfter);
18
+ if (options.action)
19
+ params.set('action', options.action);
20
+ return `/v1/client/organizations/${encodeURIComponent(organizationId)}/audit_logs?${params.toString()}`;
21
+ }
22
+ /** `organization_invitation.created` → `Organization invitation created`. */
23
+ function describeAuditAction(action) {
24
+ const text = action.replace(/[._]+/g, ' ').trim();
25
+ return text.charAt(0).toUpperCase() + text.slice(1);
26
+ }
27
+ /**
28
+ * Page through an organization's audit log. A response for an organization
29
+ * that is no longer the one shown (the user switched while it was in flight)
30
+ * is dropped, so a slow page never lands in the wrong organization's list.
31
+ */
32
+ function createOrganizationAuditLog(client, options = {}) {
33
+ let state = exports.EMPTY_ORGANIZATION_AUDIT_LOG;
34
+ let organizationId = null;
35
+ let cursor = null;
36
+ let generation = 0;
37
+ const listeners = new Set();
38
+ const set = (patch) => {
39
+ state = { ...state, ...patch };
40
+ for (const listener of listeners)
41
+ listener(state);
42
+ };
43
+ const fetchPage = async (after) => {
44
+ const orgId = organizationId;
45
+ if (!orgId)
46
+ return;
47
+ const mine = generation;
48
+ set({ loading: true });
49
+ try {
50
+ const res = await client.get(organizationAuditLogPath(orgId, { ...options, startingAfter: after }));
51
+ if (mine !== generation)
52
+ return;
53
+ if (!res.ok || !res.data) {
54
+ set({ forbidden: res.status === 403 || res.status === 404 });
55
+ return;
56
+ }
57
+ const page = res.data;
58
+ cursor = page.next_cursor;
59
+ set({
60
+ forbidden: false,
61
+ entries: after ? [...state.entries, ...page.data] : page.data,
62
+ hasMore: page.has_more,
63
+ });
64
+ }
65
+ finally {
66
+ if (mine === generation)
67
+ set({ loading: false });
68
+ }
69
+ };
70
+ return {
71
+ getState: () => state,
72
+ subscribe(listener) {
73
+ listeners.add(listener);
74
+ return () => {
75
+ listeners.delete(listener);
76
+ };
77
+ },
78
+ async load(next) {
79
+ generation += 1;
80
+ organizationId = next;
81
+ cursor = null;
82
+ set({ ...exports.EMPTY_ORGANIZATION_AUDIT_LOG });
83
+ await fetchPage(null);
84
+ },
85
+ async loadMore() {
86
+ if (cursor && !state.loading)
87
+ await fetchPage(cursor);
88
+ },
89
+ async reload() {
90
+ generation += 1;
91
+ cursor = null;
92
+ await fetchPage(null);
93
+ },
94
+ };
95
+ }
96
+ //# sourceMappingURL=organization-audit-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization-audit-log.js","sourceRoot":"","sources":["../src/organization-audit-log.ts"],"names":[],"mappings":"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"}