@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.
- package/CHANGELOG.md +14 -0
- package/dist/AtlasProvider.js +17 -1
- package/dist/AtlasProvider.js.map +1 -1
- package/dist/account-factors.d.ts +14 -0
- package/dist/account-factors.js +195 -0
- package/dist/account-factors.js.map +1 -0
- package/dist/account.d.ts +12 -1
- package/dist/account.js +92 -3
- package/dist/account.js.map +1 -1
- package/dist/app-launcher.d.ts +11 -0
- package/dist/app-launcher.js +28 -0
- package/dist/app-launcher.js.map +1 -0
- package/dist/billing.d.ts +25 -4
- package/dist/billing.js +40 -22
- package/dist/billing.js.map +1 -1
- package/dist/compat-amplify.d.ts +116 -0
- package/dist/compat-amplify.js +212 -0
- package/dist/compat-amplify.js.map +1 -0
- package/dist/compat-baas.d.ts +155 -0
- package/dist/compat-baas.js +401 -0
- package/dist/compat-baas.js.map +1 -0
- package/dist/compat-descope.d.ts +85 -0
- package/dist/compat-descope.js +191 -0
- package/dist/compat-descope.js.map +1 -0
- package/dist/compat-firebase.d.ts +67 -0
- package/dist/compat-firebase.js +166 -0
- package/dist/compat-firebase.js.map +1 -0
- package/dist/compat-kinde.d.ts +166 -0
- package/dist/compat-kinde.js +184 -0
- package/dist/compat-kinde.js.map +1 -0
- package/dist/compat-msal.d.ts +84 -0
- package/dist/compat-msal.js +158 -0
- package/dist/compat-msal.js.map +1 -0
- package/dist/compat-okta.d.ts +102 -0
- package/dist/compat-okta.js +240 -0
- package/dist/compat-okta.js.map +1 -0
- package/dist/compat.d.ts +11 -1
- package/dist/compat.js +27 -1
- package/dist/compat.js.map +1 -1
- package/dist/components.d.ts +12 -1
- package/dist/components.js +26 -2
- package/dist/components.js.map +1 -1
- package/dist/connected-accounts.d.ts +36 -0
- package/dist/connected-accounts.js +107 -0
- package/dist/connected-accounts.js.map +1 -0
- package/dist/delegated-ui.d.ts +49 -0
- package/dist/delegated-ui.js +42 -0
- package/dist/delegated-ui.js.map +1 -0
- package/dist/flow.d.ts +37 -1
- package/dist/flow.js +111 -2
- package/dist/flow.js.map +1 -1
- package/dist/i18n.js +14 -0
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/marketing.d.ts +31 -0
- package/dist/marketing.js +64 -0
- package/dist/marketing.js.map +1 -0
- package/dist/mfa-steps.d.ts +9 -0
- package/dist/mfa-steps.js +51 -0
- package/dist/mfa-steps.js.map +1 -1
- package/dist/organization-admin.d.ts +38 -0
- package/dist/organization-admin.js +192 -0
- package/dist/organization-admin.js.map +1 -0
- package/dist/organization.d.ts +8 -1
- package/dist/organization.js +15 -2
- package/dist/organization.js.map +1 -1
- package/dist/profile-completion.d.ts +11 -0
- package/dist/profile-completion.js +49 -0
- package/dist/profile-completion.js.map +1 -0
- package/dist/profile-traits.d.ts +37 -0
- package/dist/profile-traits.js +143 -0
- package/dist/profile-traits.js.map +1 -0
- package/dist/sign-up.js +48 -7
- package/dist/sign-up.js.map +1 -1
- package/dist/styles.js +6 -4
- package/dist/styles.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- 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)();
|