@atlasauth/react 0.19.0 → 0.21.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 (82) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/AtlasProvider.js +17 -1
  3. package/dist/AtlasProvider.js.map +1 -1
  4. package/dist/account-factors.d.ts +14 -0
  5. package/dist/account-factors.js +195 -0
  6. package/dist/account-factors.js.map +1 -0
  7. package/dist/account.d.ts +12 -1
  8. package/dist/account.js +92 -3
  9. package/dist/account.js.map +1 -1
  10. package/dist/app-launcher.d.ts +11 -0
  11. package/dist/app-launcher.js +28 -0
  12. package/dist/app-launcher.js.map +1 -0
  13. package/dist/billing.d.ts +25 -4
  14. package/dist/billing.js +40 -22
  15. package/dist/billing.js.map +1 -1
  16. package/dist/compat-amplify.d.ts +116 -0
  17. package/dist/compat-amplify.js +212 -0
  18. package/dist/compat-amplify.js.map +1 -0
  19. package/dist/compat-baas.d.ts +155 -0
  20. package/dist/compat-baas.js +401 -0
  21. package/dist/compat-baas.js.map +1 -0
  22. package/dist/compat-descope.d.ts +85 -0
  23. package/dist/compat-descope.js +191 -0
  24. package/dist/compat-descope.js.map +1 -0
  25. package/dist/compat-firebase.d.ts +67 -0
  26. package/dist/compat-firebase.js +166 -0
  27. package/dist/compat-firebase.js.map +1 -0
  28. package/dist/compat-kinde.d.ts +166 -0
  29. package/dist/compat-kinde.js +184 -0
  30. package/dist/compat-kinde.js.map +1 -0
  31. package/dist/compat-msal.d.ts +84 -0
  32. package/dist/compat-msal.js +158 -0
  33. package/dist/compat-msal.js.map +1 -0
  34. package/dist/compat-okta.d.ts +102 -0
  35. package/dist/compat-okta.js +240 -0
  36. package/dist/compat-okta.js.map +1 -0
  37. package/dist/compat.d.ts +11 -1
  38. package/dist/compat.js +27 -1
  39. package/dist/compat.js.map +1 -1
  40. package/dist/components.d.ts +12 -1
  41. package/dist/components.js +26 -2
  42. package/dist/components.js.map +1 -1
  43. package/dist/connected-accounts.d.ts +36 -0
  44. package/dist/connected-accounts.js +107 -0
  45. package/dist/connected-accounts.js.map +1 -0
  46. package/dist/delegated-ui.d.ts +49 -0
  47. package/dist/delegated-ui.js +42 -0
  48. package/dist/delegated-ui.js.map +1 -0
  49. package/dist/flow.d.ts +37 -1
  50. package/dist/flow.js +111 -2
  51. package/dist/flow.js.map +1 -1
  52. package/dist/i18n.js +14 -0
  53. package/dist/i18n.js.map +1 -1
  54. package/dist/index.d.ts +1 -0
  55. package/dist/index.js +5 -1
  56. package/dist/index.js.map +1 -1
  57. package/dist/marketing.d.ts +31 -0
  58. package/dist/marketing.js +64 -0
  59. package/dist/marketing.js.map +1 -0
  60. package/dist/mfa-steps.d.ts +9 -0
  61. package/dist/mfa-steps.js +51 -0
  62. package/dist/mfa-steps.js.map +1 -1
  63. package/dist/organization-admin.d.ts +38 -0
  64. package/dist/organization-admin.js +192 -0
  65. package/dist/organization-admin.js.map +1 -0
  66. package/dist/organization.d.ts +8 -1
  67. package/dist/organization.js +15 -2
  68. package/dist/organization.js.map +1 -1
  69. package/dist/profile-completion.d.ts +11 -0
  70. package/dist/profile-completion.js +49 -0
  71. package/dist/profile-completion.js.map +1 -0
  72. package/dist/profile-traits.d.ts +37 -0
  73. package/dist/profile-traits.js +143 -0
  74. package/dist/profile-traits.js.map +1 -0
  75. package/dist/sign-up.js +48 -7
  76. package/dist/sign-up.js.map +1 -1
  77. package/dist/styles.js +6 -4
  78. package/dist/styles.js.map +1 -1
  79. package/dist/types.d.ts +8 -0
  80. package/dist/version.d.ts +1 -1
  81. package/dist/version.js +1 -1
  82. package/package.json +5 -5
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useConnectedAccounts = useConnectedAccounts;
5
+ exports.ConnectedAccounts = ConnectedAccounts;
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const js_1 = require("@atlasauth/js");
9
+ const components_shared_1 = require("./components-shared");
10
+ function useConnectedAccounts(options = {}) {
11
+ const { organizationId } = options;
12
+ const client = (0, components_shared_1.useMeClient)();
13
+ const errorText = (0, components_shared_1.useErrorText)();
14
+ const [status, setStatus] = (0, react_1.useState)('loading');
15
+ const [error, setError] = (0, react_1.useState)(null);
16
+ const [connections, setConnections] = (0, react_1.useState)([]);
17
+ const reload = (0, react_1.useCallback)(async () => {
18
+ try {
19
+ setConnections(organizationId ? await (0, js_1.listOrganizationConnections)(client, organizationId) : await (0, js_1.listMyConnections)(client));
20
+ setStatus('ready');
21
+ setError(null);
22
+ }
23
+ catch (err) {
24
+ setStatus('error');
25
+ setError(err instanceof Error ? errorText(err) : String(err));
26
+ }
27
+ }, [client, errorText, organizationId]);
28
+ (0, react_1.useEffect)(() => {
29
+ void reload();
30
+ }, [reload]);
31
+ const authorize = (0, react_1.useCallback)(async (connection, redirectUrl) => {
32
+ const opts = { api: client.frontendApiOrigin, publishableKey: client.publishableKey, redirectUrl };
33
+ const start = connection.external_account_id
34
+ ? await (0, js_1.reauthorizeExternalAccount)(connection.external_account_id, opts)
35
+ : await (0, js_1.connectExternalAccount)(connection.provider, opts);
36
+ (0, components_shared_1.navigateTo)(start.authorizationUrl);
37
+ }, [client]);
38
+ const saveCredentials = (0, react_1.useCallback)(async (connection, input) => {
39
+ if (!organizationId)
40
+ throw new Error('Credentials are entered on organization connections.');
41
+ await (0, js_1.setOrganizationConnectionCredentials)(client, organizationId, connection.id, input);
42
+ await reload();
43
+ }, [client, organizationId, reload]);
44
+ const share = (0, react_1.useCallback)(async (connection) => {
45
+ if (!organizationId)
46
+ throw new Error('Only organization connections are shared.');
47
+ await (0, js_1.shareOrganizationConnection)(client, organizationId, connection.id);
48
+ await reload();
49
+ }, [client, organizationId, reload]);
50
+ return { status, connections, error, reload, authorize, saveCredentials, share };
51
+ }
52
+ const DEFAULT_LABELS = {
53
+ title: 'Connected accounts',
54
+ empty: 'No connections are set up yet.',
55
+ connect: 'Connect',
56
+ reauthorize: 'Reauthorize',
57
+ share: 'Share my account',
58
+ save: 'Save',
59
+ apiKey: 'API key',
60
+ clientId: 'Client ID',
61
+ clientSecret: 'Client secret',
62
+ tokenUrl: 'Token URL',
63
+ connected: 'Connected',
64
+ not_connected: 'Not connected',
65
+ needs_reauth: 'Needs reauthorization',
66
+ };
67
+ function ConnectedAccounts({ organizationId, redirectUrl, labels }) {
68
+ const api = useConnectedAccounts({ organizationId });
69
+ const errorText = (0, components_shared_1.useErrorText)();
70
+ const cardClass = (0, components_shared_1.useClass)('card', 'atlas-card');
71
+ const cls = (0, components_shared_1.useClasses)();
72
+ const l = { ...DEFAULT_LABELS, ...labels };
73
+ const [busy, setBusy] = (0, react_1.useState)(null);
74
+ const [formError, setFormError] = (0, react_1.useState)(null);
75
+ const [draft, setDraft] = (0, react_1.useState)({});
76
+ if (api.status === 'loading')
77
+ return null;
78
+ const run = async (id, fn) => {
79
+ setBusy(id);
80
+ setFormError(null);
81
+ try {
82
+ await fn();
83
+ }
84
+ catch (err) {
85
+ setFormError(err instanceof Error ? errorText(err) : String(err));
86
+ }
87
+ finally {
88
+ setBusy(null);
89
+ }
90
+ };
91
+ const field = (c, name) => draft[c.id]?.[name] ?? '';
92
+ const setField = (c, name, value) => setDraft((d) => ({ ...d, [c.id]: { ...d[c.id], [name]: value } }));
93
+ const back = redirectUrl ?? (typeof window !== 'undefined' ? window.location.href : '');
94
+ const secretInput = (c, name, label, type = 'password') => ((0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [(0, jsx_runtime_1.jsx)("span", { className: cls('formFieldLabel', 'atlas-field-label'), children: label }), (0, jsx_runtime_1.jsx)("input", { className: cls('formFieldInput', 'atlas-input'), type: type, autoComplete: "off", value: field(c, name), onChange: (e) => setField(c, name, e.target.value) })] }, name));
95
+ return ((0, jsx_runtime_1.jsxs)("section", { className: cardClass, "data-atlas": "connected-accounts", children: [(0, jsx_runtime_1.jsx)("h2", { className: cls('sectionTitle', 'atlas-section-title'), children: l.title }), api.error || formError ? (0, jsx_runtime_1.jsx)("p", { className: cls('alert', 'atlas-alert'), role: "alert", children: formError ?? api.error }) : null, api.connections.length === 0 ? (0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: l.empty }) : null, (0, jsx_runtime_1.jsx)("ul", { "data-atlas": "connected-accounts-list", children: api.connections.map((c) => ((0, jsx_runtime_1.jsxs)("li", { "data-atlas": "connected-account", "data-status": c.status, children: [(0, jsx_runtime_1.jsxs)("p", { className: cls('text', 'atlas-text'), children: [(0, jsx_runtime_1.jsx)("strong", { children: c.name }), " ", (0, jsx_runtime_1.jsx)("span", { "data-atlas": "connected-account-status", children: l[c.status] })] }), c.kind === 'oauth' ? ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: !organizationId ? ((0, jsx_runtime_1.jsx)("button", { className: cls('formButtonSecondary', 'atlas-button-secondary'), type: "button", disabled: busy === c.id, onClick: () => void run(c.id, () => api.authorize(c, back)), children: c.status === 'connected' ? l.reauthorize : c.status === 'needs_reauth' ? l.reauthorize : l.connect })) : ((0, jsx_runtime_1.jsx)("button", { className: cls('formButtonSecondary', 'atlas-button-secondary'), type: "button", disabled: busy === c.id, onClick: () => void run(c.id, () => api.share(c)), children: l.share })) })) : organizationId ? ((0, jsx_runtime_1.jsxs)("form", { onSubmit: (e) => {
96
+ e.preventDefault();
97
+ void run(c.id, async () => {
98
+ await api.saveCredentials(c, c.kind === 'api_key'
99
+ ? { apiKey: field(c, 'apiKey') }
100
+ : { clientId: field(c, 'clientId'), clientSecret: field(c, 'clientSecret'), tokenUrl: field(c, 'tokenUrl') });
101
+ setDraft((d) => ({ ...d, [c.id]: {} }));
102
+ });
103
+ }, children: [c.kind === 'api_key'
104
+ ? secretInput(c, 'apiKey', l.apiKey)
105
+ : [secretInput(c, 'clientId', l.clientId, 'text'), secretInput(c, 'clientSecret', l.clientSecret), secretInput(c, 'tokenUrl', l.tokenUrl, 'url')], (0, jsx_runtime_1.jsx)("button", { className: cls('formButtonPrimary', 'atlas-button'), type: "submit", disabled: busy === c.id, children: l.save })] })) : null] }, c.id))) })] }));
106
+ }
107
+ //# sourceMappingURL=connected-accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connected-accounts.js","sourceRoot":"","sources":["../src/connected-accounts.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { type DelegatedChallenge, type DelegatedUiClientOptions } from '@atlasauth/js';
2
+ /**
3
+ * React helpers for your own login / consent pages (the page Atlas redirects
4
+ * to with `?login_challenge=` or `?consent_challenge=`). Reading and deciding
5
+ * the challenge goes through YOUR backend (see `delegatedUiProxy` in
6
+ * @atlasauth/backend); this hook wraps the calls with busy/error state and
7
+ * follows the redirect Atlas returns.
8
+ */
9
+ export declare function useDelegatedChallenge(href?: string): DelegatedChallenge | null;
10
+ export declare function useDelegatedFlow(opts: DelegatedUiClientOptions & {
11
+ allowedOrigins?: string[];
12
+ navigate?: (url: string) => void;
13
+ }): {
14
+ client: {
15
+ getLoginRequest: (challenge: string) => Promise<import("@atlasauth/js").DelegatedLoginRequest>;
16
+ acceptLogin: (challenge: string, body: {
17
+ subject: string;
18
+ remember?: boolean;
19
+ amr?: string[];
20
+ acr?: string;
21
+ }) => Promise<{
22
+ redirect_to: string;
23
+ }>;
24
+ rejectLogin: (challenge: string, body?: {
25
+ error?: string;
26
+ error_description?: string;
27
+ }) => Promise<{
28
+ redirect_to: string;
29
+ }>;
30
+ getConsentRequest: (challenge: string) => Promise<import("@atlasauth/js").DelegatedConsentRequest>;
31
+ acceptConsent: (challenge: string, body?: {
32
+ grant_scope?: string[];
33
+ remember?: boolean;
34
+ }) => Promise<{
35
+ redirect_to: string;
36
+ }>;
37
+ rejectConsent: (challenge: string, body?: {
38
+ error?: string;
39
+ error_description?: string;
40
+ }) => Promise<{
41
+ redirect_to: string;
42
+ }>;
43
+ };
44
+ decide: (call: () => Promise<{
45
+ redirect_to: string;
46
+ }>) => Promise<void>;
47
+ busy: boolean;
48
+ error: string | null;
49
+ };
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useDelegatedChallenge = useDelegatedChallenge;
5
+ exports.useDelegatedFlow = useDelegatedFlow;
6
+ const react_1 = require("react");
7
+ const js_1 = require("@atlasauth/js");
8
+ /**
9
+ * React helpers for your own login / consent pages (the page Atlas redirects
10
+ * to with `?login_challenge=` or `?consent_challenge=`). Reading and deciding
11
+ * the challenge goes through YOUR backend (see `delegatedUiProxy` in
12
+ * @atlasauth/backend); this hook wraps the calls with busy/error state and
13
+ * follows the redirect Atlas returns.
14
+ */
15
+ function useDelegatedChallenge(href) {
16
+ return (0, react_1.useMemo)(() => {
17
+ const where = href ?? (typeof window !== 'undefined' ? window.location.href : undefined);
18
+ return where ? (0, js_1.readDelegatedChallenge)(where) : null;
19
+ }, [href]);
20
+ }
21
+ function useDelegatedFlow(opts) {
22
+ const { baseUrl, fetch: f, headers, allowedOrigins, navigate } = opts;
23
+ const client = (0, react_1.useMemo)(() => (0, js_1.createDelegatedUiClient)({ baseUrl, fetch: f, headers }), [baseUrl, f, headers]);
24
+ const [busy, setBusy] = (0, react_1.useState)(false);
25
+ const [error, setError] = (0, react_1.useState)(null);
26
+ /** Run a call that returns `{ redirect_to }` and follow it. */
27
+ const decide = (0, react_1.useCallback)(async (call) => {
28
+ setBusy(true);
29
+ setError(null);
30
+ try {
31
+ (0, js_1.continueDelegatedFlow)((await call()).redirect_to, { allowedOrigins, navigate });
32
+ }
33
+ catch (e) {
34
+ setError(e instanceof Error ? e.message : 'Request failed.');
35
+ }
36
+ finally {
37
+ setBusy(false);
38
+ }
39
+ }, [allowedOrigins, navigate]);
40
+ return { client, decide, busy, error };
41
+ }
42
+ //# sourceMappingURL=delegated-ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegated-ui.js","sourceRoot":"","sources":["../src/delegated-ui.ts"],"names":[],"mappings":";AAAA,YAAY,CAAC;;AAkBb,sDAKC;AAED,4CAuBC;AA9CD,iCAAuD;AACvD,sCAMuB;AAEvB;;;;;;GAMG;AACH,SAAgB,qBAAqB,CAAC,IAAa;IACjD,OAAO,IAAA,eAAO,EAAC,GAAG,EAAE;QAClB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACzF,OAAO,KAAK,CAAC,CAAC,CAAC,IAAA,2BAAsB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACtD,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;AACb,CAAC;AAED,SAAgB,gBAAgB,CAAC,IAAgG;IAC/H,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACtE,MAAM,MAAM,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE,CAAC,IAAA,4BAAuB,EAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7G,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAgB,IAAI,CAAC,CAAC;IAExD,+DAA+D;IAC/D,MAAM,MAAM,GAAG,IAAA,mBAAW,EACxB,KAAK,EAAE,IAA4C,EAAE,EAAE;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC;QACd,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC;YACH,IAAA,0BAAqB,EAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,CAAC;QAClF,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,QAAQ,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;QAC/D,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;IACH,CAAC,EACD,CAAC,cAAc,EAAE,QAAQ,CAAC,CAC3B,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzC,CAAC"}
package/dist/flow.d.ts CHANGED
@@ -19,6 +19,13 @@ import { FapiClient, type FlowState } from './fapi';
19
19
  export interface FlowProps {
20
20
  /** Where to send the user once the flow completes. */
21
21
  afterUrl?: string;
22
+ /**
23
+ * `<SignUp>`: offer the instance's plans as a step before the account form and,
24
+ * once the account exists, send the user straight to Checkout for the chosen
25
+ * plan (they return to `afterUrl`, else the current page). `required` makes the
26
+ * choice mandatory; `optional` lets them skip. Only user (B2C) plans are shown.
27
+ */
28
+ planSelection?: 'optional' | 'required';
22
29
  /**
23
30
  * `<SignIn>`: where "No account? Sign up" goes. Default: the instance's hosted
24
31
  * sign-up page (carrying the visitor's referral). `null` hides the link (your
@@ -166,8 +173,14 @@ export declare function afterUrlTarget(afterUrl?: string): string | null;
166
173
  * the native/popup social `onComplete`, and the cross-device poll), after the
167
174
  * session re-boot, so the destination page already sees a signed-in user.
168
175
  */
169
- export declare function useFlow({ afterUrl, signInCaptcha, referral, referralConsent, }?: {
176
+ export declare function useFlow({ afterUrl, afterSignIn, signInCaptcha, referral, referralConsent, }?: {
170
177
  afterUrl?: string;
178
+ /**
179
+ * Runs once the session is held (after the re-boot), before `afterUrl`. Resolve
180
+ * `true` when it navigated the user itself (e.g. to a plan's Checkout), which
181
+ * skips `afterUrl`. `<SignUp planSelection>` uses it.
182
+ */
183
+ afterSignIn?: (client: FapiClient) => Promise<boolean>;
171
184
  /** See {@link FlowProps.referral}. */
172
185
  referral?: false;
173
186
  /** See {@link FlowProps.referralConsent}. */
@@ -341,6 +354,29 @@ export declare function AtlasSsoButton({ onClick, className, labelClassName, }:
341
354
  /** Class for the label (defaults to `atlas-social-label`). */
342
355
  labelClassName?: string;
343
356
  }): import("react").JSX.Element;
357
+ /**
358
+ * §6 all enabled social sign-in providers, native-first.
359
+ *
360
+ * A provider with an in-page native flow (Google today) renders its secretless
361
+ * button — no redirect, no new tab, no stranded initiating tab. Every OTHER
362
+ * enabled provider (GitHub, Microsoft, Discord, …) renders a redirect button that
363
+ * starts the OAuth flow and returns to THIS page, where `<AtlasProvider>` exchanges
364
+ * the returned ticket for a session. Previously `<SignIn>` rendered only the
365
+ * Google button, so an instance's other social providers never appeared.
366
+ */
367
+ /** "Resend link" on a link-only sign-up: a fresh email (the earlier link stops working). */
368
+ /**
369
+ * The `identity_verification` required action: a start button, then the
370
+ * verification service's page in a new tab, and a poll (plus a manual
371
+ * "I've finished") that re-reads the verdict. The sign-in continues on its own
372
+ * once the check is approved; a declined or in-review check says so.
373
+ */
374
+ export declare const IDENTITY_POLL_MS = 4000;
375
+ export declare function IdentityVerificationStep({ attempt, busy, onSubmit }: {
376
+ attempt: AttemptView | null;
377
+ busy: boolean;
378
+ onSubmit(values: Record<string, string>): void;
379
+ }): import("react").JSX.Element;
344
380
  /**
345
381
  * The method this browser last signed in with (`password`, `oauth_github`, …),
346
382
  * for the "Last used" badge — null until known, when none is remembered, or
package/dist/flow.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  'use client';
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.IDENTITY_POLL_MS = void 0;
4
5
  exports.AttemptFlow = AttemptFlow;
5
6
  exports.afterUrlTarget = afterUrlTarget;
6
7
  exports.useFlow = useFlow;
@@ -10,6 +11,7 @@ exports.resolveGoogleButtonRender = resolveGoogleButtonRender;
10
11
  exports.useGoogleNativeOverlay = useGoogleNativeOverlay;
11
12
  exports.atlasSsoTarget = atlasSsoTarget;
12
13
  exports.AtlasSsoButton = AtlasSsoButton;
14
+ exports.IdentityVerificationStep = IdentityVerificationStep;
13
15
  exports.useLastSignInMethod = useLastSignInMethod;
14
16
  exports.LastUsedBadge = LastUsedBadge;
15
17
  exports.LastUsedHint = LastUsedHint;
@@ -126,6 +128,9 @@ function AttemptFlow({ attempt, onSubmit, busy, errors, titleKey, hideTitle, cap
126
128
  ...(mfaChoices.emailCode && attempt?.id
127
129
  ? [(0, jsx_runtime_1.jsx)(mfa_steps_1.SecondFactorCodeOption, { attemptId: attempt.id, strategy: "email_code" }, "email-2fa")]
128
130
  : []),
131
+ ...(mfaChoices.duo && attempt?.id
132
+ ? [(0, jsx_runtime_1.jsx)(mfa_steps_1.DuoSecondFactorOption, { attemptId: attempt.id, onSubmit: onSubmit }, "duo-2fa")]
133
+ : []),
129
134
  ];
130
135
  case 'enroll_second_factor':
131
136
  /**
@@ -146,6 +151,64 @@ function AttemptFlow({ attempt, onSubmit, busy, errors, titleKey, hideTitle, cap
146
151
  return [field('code', 'signIn.emailCodeLabel')];
147
152
  case 'collect_new_password':
148
153
  return [field('password', 'reset.newPasswordLabel', 'password')];
154
+ case 'complete_required_action': {
155
+ // The user owes a step (USR-04): ask for what the first one needs.
156
+ const owed = step.actions[0];
157
+ if (owed?.action === 'update_password')
158
+ return [field('password', 'reset.newPasswordLabel', 'password', { autoComplete: 'new-password' })];
159
+ if (owed?.action === 'update_profile') {
160
+ return [
161
+ field('first_name', 'requiredAction.firstNameLabel', 'text', { autoComplete: 'given-name' }),
162
+ field('last_name', 'requiredAction.lastNameLabel', 'text', { autoComplete: 'family-name' }),
163
+ ];
164
+ }
165
+ if (owed?.action === 'complete_profile') {
166
+ // Progressive profiling: the fields the application asked for; optional-only steps can be skipped.
167
+ const asked = owed.fields ?? [];
168
+ return [
169
+ ...asked.map((f) => ((0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [(0, jsx_runtime_1.jsxs)("span", { className: cls('formFieldLabel', 'atlas-field-label'), children: [f.label, f.required ? ' *' : ''] }), (0, jsx_runtime_1.jsx)("input", { className: inputClass, type: f.type === 'tel' || f.type === 'email' || f.type === 'url' || f.type === 'number' || f.type === 'date' ? f.type : 'text', name: `field:${f.key}`, placeholder: f.placeholder ?? undefined, value: values[`field:${f.key}`] ?? '', onChange: (event) => setValues((v) => ({ ...v, [`field:${f.key}`]: event.target.value })) })] }, f.key))),
170
+ asked.every((f) => !f.required) ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonClass, disabled: busy, onClick: () => onSubmit({ action: 'complete_profile', skip: 'true' }), children: t('requiredAction.skip') }, "skip")) : null,
171
+ ];
172
+ }
173
+ if (owed?.action === 'journey_form') {
174
+ // A server-driven form from the sign-in journey: rendered from its fields.
175
+ const asked = owed.fields ?? [];
176
+ const set = (key, v) => setValues((cur) => ({ ...cur, [`field:${key}`]: v }));
177
+ return [
178
+ owed.title ? (0, jsx_runtime_1.jsx)("h2", { className: cls('headerTitle', 'atlas-title'), children: owed.title }, "title") : null,
179
+ owed.intro ? (0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: owed.intro }, "intro") : null,
180
+ ...asked.map((f) => {
181
+ const name = `field:${f.key}`;
182
+ const label = (0, jsx_runtime_1.jsxs)("span", { className: cls('formFieldLabel', 'atlas-field-label'), children: [f.label, f.required ? ' *' : ''] });
183
+ if (f.type === 'checkbox') {
184
+ return ((0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", name: name, checked: values[name] === 'true', onChange: (event) => set(f.key, event.target.checked ? 'true' : 'false') }), label] }, f.key));
185
+ }
186
+ if (f.type === 'select') {
187
+ return ((0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [label, (0, jsx_runtime_1.jsxs)("select", { className: inputClass, name: name, value: values[name] ?? '', onChange: (event) => set(f.key, event.target.value), children: [(0, jsx_runtime_1.jsx)("option", { value: "" }), f.options.map((o) => (0, jsx_runtime_1.jsx)("option", { value: o, children: o }, o))] })] }, f.key));
188
+ }
189
+ if (f.type === 'textarea') {
190
+ return ((0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [label, (0, jsx_runtime_1.jsx)("textarea", { className: inputClass, name: name, rows: 3, placeholder: f.placeholder ?? undefined, value: values[name] ?? '', onChange: (event) => set(f.key, event.target.value) })] }, f.key));
191
+ }
192
+ return ((0, jsx_runtime_1.jsxs)("label", { className: cls('formField', 'atlas-field'), children: [label, (0, jsx_runtime_1.jsx)("input", { className: inputClass, type: f.type === 'email' || f.type === 'number' || f.type === 'date' ? f.type : 'text', name: name, placeholder: f.placeholder ?? undefined, value: values[name] ?? '', onChange: (event) => set(f.key, event.target.value) })] }, f.key));
193
+ }),
194
+ ];
195
+ }
196
+ if (owed?.action === 'identity_verification') {
197
+ return [(0, jsx_runtime_1.jsx)(IdentityVerificationStep, { attempt: attempt, busy: busy, onSubmit: onSubmit }, "idv")];
198
+ }
199
+ if (owed?.action === 'verify_email') {
200
+ return [
201
+ (0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t('requiredAction.verifyIntro') }, "verify"),
202
+ (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonClass, disabled: busy, onClick: () => onSubmit({ action: 'verify_email', step: 'send' }), children: t('requiredAction.sendCode') }, "send"),
203
+ field('code', 'signIn.emailCodeLabel', 'text', { autoComplete: 'one-time-code', inputMode: 'numeric' }),
204
+ ];
205
+ }
206
+ // An application-defined step: the app closes it, then the user signs in again.
207
+ return [
208
+ (0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t('requiredAction.customIntro') }, "custom"),
209
+ owed?.url ? ((0, jsx_runtime_1.jsx)("a", { className: buttonClass, href: owed.url, rel: "noopener", children: t('signIn.submit') }, "go")) : null,
210
+ ];
211
+ }
149
212
  case 'accept_policies':
150
213
  // A required policy changed: scroll-mode ones in their dialog, the rest as checkboxes.
151
214
  return [
@@ -206,10 +269,17 @@ function AttemptFlow({ attempt, onSubmit, busy, errors, titleKey, hideTitle, cap
206
269
  });
207
270
  return;
208
271
  }
272
+ // Required actions: name the action; the emailed code is verified, not re-sent.
273
+ if (step.kind === 'complete_required_action') {
274
+ const owed = step.actions[0]?.action;
275
+ onSubmit({ ...values, action: owed ?? '', ...(owed === 'verify_email' ? { step: 'verify' } : {}) });
276
+ return;
277
+ }
209
278
  onSubmit(values);
210
279
  }, children: [fields(), step.kind === 'collect_identifier' || step.kind === 'collect_captcha' ? captcha : null, step.kind !== 'done' &&
211
280
  step.kind !== 'await_oauth' &&
212
281
  step.kind !== 'accept_policies' &&
282
+ !(step.kind === 'complete_required_action' && step.actions[0]?.action === 'custom') &&
213
283
  !noFirstFactor &&
214
284
  sent !== 'email_link' &&
215
285
  !sending &&
@@ -261,7 +331,7 @@ function navigateAfterComplete(afterUrl) {
261
331
  * the native/popup social `onComplete`, and the cross-device poll), after the
262
332
  * session re-boot, so the destination page already sees a signed-in user.
263
333
  */
264
- function useFlow({ afterUrl, signInCaptcha = true, referral, referralConsent, } = {}) {
334
+ function useFlow({ afterUrl, afterSignIn, signInCaptcha = true, referral, referralConsent, } = {}) {
265
335
  const { publishableKey, frontendApi, locale, reload, pendingAttempt, instanceSettings, broadcastSessionChange } = (0, AtlasProvider_1.useAtlas)();
266
336
  const t = (0, components_shared_1.useText)();
267
337
  // Seeded from a redirect that came back mid-flow (e.g. OAuth needing a second
@@ -320,12 +390,15 @@ function useFlow({ afterUrl, signInCaptcha = true, referral, referralConsent, }
320
390
  // alone says nothing about whether the cookie was kept.
321
391
  setState({ ...next, busy: true });
322
392
  let exchanged = true;
393
+ // Where the sign-in journey sends the user next, when it names a place.
394
+ let journeyUrl = null;
323
395
  if (next.ticket) {
324
396
  const res = await client.post('/v1/client/tickets/exchange', {
325
397
  attempt_id: next.attempt.id,
326
398
  ticket: next.ticket,
327
399
  });
328
400
  exchanged = res.ok;
401
+ journeyUrl = res.ok ? (0, js_1.journeyRedirectUrl)(res.data) : null;
329
402
  }
330
403
  const kept = exchanged && (await reload());
331
404
  setState({ ...next, busy: false, sessionKept: kept });
@@ -333,9 +406,15 @@ function useFlow({ afterUrl, signInCaptcha = true, referral, referralConsent, }
333
406
  return;
334
407
  // §R3.2 wake sibling tabs now, rather than when they next gain focus.
335
408
  broadcastSessionChange();
409
+ if (afterSignIn && (await afterSignIn(client)))
410
+ return;
411
+ if (journeyUrl && typeof window !== 'undefined') {
412
+ window.location.assign(journeyUrl);
413
+ return;
414
+ }
336
415
  // §10.2 send the user on once signed-in — no-op when afterUrl is unset.
337
416
  navigateAfterComplete(afterUrl);
338
- }, [afterUrl, broadcastSessionChange, client, reload]);
417
+ }, [afterSignIn, afterUrl, broadcastSessionChange, client, reload]);
339
418
  const submit = (0, react_1.useCallback)((values) => {
340
419
  void (async () => {
341
420
  setState((current) => ({ ...current, busy: true, errors: [] }));
@@ -437,6 +516,11 @@ function useFlow({ afterUrl, signInCaptcha = true, referral, referralConsent, }
437
516
  setState((current) => ({ ...current, sessionKept: kept }));
438
517
  if (!kept)
439
518
  return;
519
+ const journeyUrl = (0, js_1.journeyRedirectUrl)(res.data);
520
+ if (journeyUrl && typeof window !== 'undefined') {
521
+ window.location.assign(journeyUrl);
522
+ return;
523
+ }
440
524
  // §10.2 a cross-device sign-in that completes here redirects too.
441
525
  navigateAfterComplete(afterUrl);
442
526
  })();
@@ -798,6 +882,31 @@ function AtlasSsoButton({ onClick, className, labelClassName, }) {
798
882
  * Google button, so an instance's other social providers never appeared.
799
883
  */
800
884
  /** "Resend link" on a link-only sign-up: a fresh email (the earlier link stops working). */
885
+ /**
886
+ * The `identity_verification` required action: a start button, then the
887
+ * verification service's page in a new tab, and a poll (plus a manual
888
+ * "I've finished") that re-reads the verdict. The sign-in continues on its own
889
+ * once the check is approved; a declined or in-review check says so.
890
+ */
891
+ exports.IDENTITY_POLL_MS = 4_000;
892
+ function IdentityVerificationStep({ attempt, busy, onSubmit }) {
893
+ const t = (0, components_shared_1.useText)();
894
+ const cls = (0, components_shared_1.useClasses)();
895
+ const buttonClass = (0, components_shared_1.useClass)('formButtonPrimary', 'atlas-button');
896
+ const idv = attempt?.identity_verification ?? null;
897
+ const url = idv?.url ?? null;
898
+ const submitRef = (0, react_1.useRef)(onSubmit);
899
+ submitRef.current = onSubmit;
900
+ (0, react_1.useEffect)(() => {
901
+ if (!url)
902
+ return;
903
+ const timer = setInterval(() => submitRef.current({ action: 'identity_verification' }), exports.IDENTITY_POLL_MS);
904
+ return () => clearInterval(timer);
905
+ }, [url]);
906
+ const note = idv?.status === 'declined' ? t('requiredAction.idvDeclined') : idv?.status === 'needs_review' ? t('requiredAction.idvReview') : url ? t('requiredAction.idvWaiting') : null;
907
+ const returnTo = typeof window !== 'undefined' && window.location.protocol === 'https:' ? window.location.href : undefined;
908
+ return ((0, jsx_runtime_1.jsxs)("div", { "data-atlas-identity-verification": "", children: [(0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), children: t('requiredAction.idvIntro') }), url ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("a", { className: buttonClass, href: url, target: "_blank", rel: "noopener noreferrer", children: t('requiredAction.idvOpen') }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonClass, disabled: busy, onClick: () => onSubmit({ action: 'identity_verification' }), children: t('requiredAction.idvCheck') })] })) : ((0, jsx_runtime_1.jsx)("button", { type: "button", className: buttonClass, disabled: busy, onClick: () => onSubmit({ action: 'identity_verification', step: 'start', ...(returnTo ? { redirect_url: returnTo } : {}) }), children: t('requiredAction.idvStart') })), note ? (0, jsx_runtime_1.jsx)("p", { className: cls('text', 'atlas-text'), role: "status", "aria-live": "polite", children: note }) : null] }));
909
+ }
801
910
  function ResendSignUpLink({ attempt }) {
802
911
  const client = (0, components_shared_1.useFapiClient)();
803
912
  const t = (0, components_shared_1.useText)();