@ory/elements-react
Version:
Ory Elements React - a collection of React components for authentication UIs.
1,466 lines (1,459 loc) • 251 kB
JavaScript
'use strict';
var clientFetch = require('@ory/client-fetch');
var react = require('react');
var jsxRuntime = require('react/jsx-runtime');
var reactIntl = require('react-intl');
var reactHookForm = require('react-hook-form');
// src/context/component.tsx
var ComponentContext = react.createContext({
components: null,
// fine because we throw an error if it's not provided
nodeSorter: () => 0,
groupSorter: () => 0
});
function useComponents() {
const ctx = react.useContext(ComponentContext);
if (!ctx) {
throw new Error("useComponents must be used within a ComponentProvider");
}
return ctx.components;
}
function useNodeSorter() {
const ctx = react.useContext(ComponentContext);
if (!ctx) {
throw new Error("useNodeSorter must be used within a ComponentProvider");
}
return ctx.nodeSorter;
}
function useGroupSorter() {
const ctx = react.useContext(ComponentContext);
if (!ctx) {
throw new Error("useGroupSorter must be used within a ComponentProvider");
}
return ctx.groupSorter;
}
var defaultNodeOrder = [
"oidc",
"saml",
"identifier_first",
"default",
"profile",
"password",
// CAPTCHA is below password because otherwise the password input field
// would be above the captcha. Somehow, we sort the password sign up button somewhere else to be always at the bottom.
"captcha",
"passkey",
"code",
"webauthn"
];
function defaultNodeSorter(a, b) {
var _a, _b;
const aGroupWeight = (_a = defaultNodeOrder.indexOf(a.group)) != null ? _a : 999;
const bGroupWeight = (_b = defaultNodeOrder.indexOf(b.group)) != null ? _b : 999;
if (b.group === "captcha" && clientFetch.isUiNodeInputAttributes(a.attributes) && a.attributes.type === "submit") {
return aGroupWeight - (bGroupWeight - 2);
} else if (a.group === "captcha" && clientFetch.isUiNodeInputAttributes(b.attributes) && b.attributes.type === "submit") {
return aGroupWeight - 2 - bGroupWeight;
}
return aGroupWeight - bGroupWeight;
}
var defaultGroupOrder = [
clientFetch.UiNodeGroupEnum.Default,
clientFetch.UiNodeGroupEnum.Profile,
clientFetch.UiNodeGroupEnum.Password,
clientFetch.UiNodeGroupEnum.Oidc,
clientFetch.UiNodeGroupEnum.Code,
clientFetch.UiNodeGroupEnum.LookupSecret,
clientFetch.UiNodeGroupEnum.Passkey,
clientFetch.UiNodeGroupEnum.Webauthn,
clientFetch.UiNodeGroupEnum.Totp
];
function defaultGroupSorter(a, b) {
var _a, _b;
const aGroupWeight = (_a = defaultGroupOrder.indexOf(a)) != null ? _a : 999;
const bGroupWeight = (_b = defaultGroupOrder.indexOf(b)) != null ? _b : 999;
return aGroupWeight - bGroupWeight;
}
function OryComponentProvider({
children,
components,
nodeSorter = defaultNodeSorter,
groupSorter = defaultGroupSorter
}) {
return /* @__PURE__ */ jsxRuntime.jsx(
ComponentContext.Provider,
{
value: {
components,
nodeSorter,
groupSorter
},
children
}
);
}
function isChoosingMethod(flow) {
return flow.flow.ui.nodes.some(
(node) => "name" in node.attributes && node.attributes.name === "screen" && "value" in node.attributes && node.attributes.value === "previous"
) || flow.flow.ui.nodes.some(
(node) => node.group === clientFetch.UiNodeGroupEnum.IdentifierFirst && "name" in node.attributes && node.attributes.name === "identifier" && node.attributes.type === "hidden"
) || flow.flowType === clientFetch.FlowType.Login && flow.flow.requested_aal === "aal2";
}
function removeSsoNodes(nodes) {
return nodes.filter(
(node) => !(node.group === clientFetch.UiNodeGroupEnum.Oidc || node.group === clientFetch.UiNodeGroupEnum.Saml)
);
}
function getFinalNodes(uniqueGroups, selectedGroup) {
var _a, _b, _c, _d;
const selectedNodes = selectedGroup ? (_a = uniqueGroups[selectedGroup]) != null ? _a : [] : [];
return [
...(_b = uniqueGroups == null ? void 0 : uniqueGroups.identifier_first) != null ? _b : [],
...(_c = uniqueGroups == null ? void 0 : uniqueGroups.default) != null ? _c : [],
...(_d = uniqueGroups == null ? void 0 : uniqueGroups.captcha) != null ? _d : []
].flat().filter(
(node) => "type" in node.attributes && node.attributes.type === "hidden"
).concat(selectedNodes);
}
function triggerToWindowCall(trigger) {
if (!trigger) {
return;
}
const fn = triggerToFunction(trigger);
if (fn) {
fn();
return;
}
let i = 0;
const ms = 100;
const interval = setInterval(() => {
i++;
if (i > 100) {
clearInterval(interval);
throw new Error(
"Unable to load Ory's WebAuthn script. Is it being blocked or otherwise failing to load? If you are running an old version of Ory Elements, please upgrade. For more information, please check your browser's developer console."
);
}
const fn2 = triggerToFunction(trigger);
if (fn2) {
clearInterval(interval);
return fn2();
}
}, ms);
return;
}
function triggerToFunction(trigger) {
if (typeof window === "undefined") {
console.debug(
"The Ory SDK is missing a required function: window is undefined."
);
return void 0;
}
const typedWindow = window;
if (!(trigger in typedWindow) || !typedWindow[trigger]) {
console.debug(`The Ory SDK is missing a required function: ${trigger}.`);
return void 0;
}
const triggerFn = typedWindow[trigger];
if (typeof triggerFn !== "function") {
console.debug(
`The Ory SDK is missing a required function: ${trigger}. It is not a function.`
);
return void 0;
}
return triggerFn;
}
function nodesToAuthMethodGroups(nodes, excludeAuthMethods = []) {
var _a;
const groups = {};
for (const node of nodes) {
if (node.type === "script") {
continue;
}
const groupNodes = (_a = groups[node.group]) != null ? _a : [];
groupNodes.push(node);
groups[node.group] = groupNodes;
}
return Object.values(clientFetch.UiNodeGroupEnum).filter((group) => {
var _a2;
return (_a2 = groups[group]) == null ? void 0 : _a2.length;
}).filter(
(group) => ![
clientFetch.UiNodeGroupEnum.Default,
clientFetch.UiNodeGroupEnum.IdentifierFirst,
clientFetch.UiNodeGroupEnum.Profile,
clientFetch.UiNodeGroupEnum.Captcha,
...excludeAuthMethods
].includes(group)
);
}
function useNodesGroups(nodes, { omit } = {}) {
const groupSorter = useGroupSorter();
const groups = react.useMemo(() => {
var _a, _b;
const groups2 = {};
const groupRetained = {};
for (const node of nodes) {
const groupNodes = (_a = groups2[node.group]) != null ? _a : [];
groupNodes.push(node);
groups2[node.group] = groupNodes;
if ((omit == null ? void 0 : omit.includes("script")) && clientFetch.isUiNodeScriptAttributes(node.attributes)) {
continue;
}
if ((omit == null ? void 0 : omit.includes("input_hidden")) && clientFetch.isUiNodeInputAttributes(node.attributes) && node.attributes.type === "hidden") {
continue;
}
groupRetained[node.group] = ((_b = groupRetained[node.group]) != null ? _b : 0) + 1;
}
const finalGroups = {};
for (const [group, count] of Object.entries(groupRetained)) {
if (count > 0) {
finalGroups[group] = groups2[group];
}
}
return finalGroups;
}, [nodes, omit]);
const entries = react.useMemo(
() => Object.entries(groups).sort(([a], [b]) => groupSorter(a, b)),
[groups, groupSorter]
);
return {
groups,
entries
};
}
var findNode = (nodes, opt) => nodes.find((n) => {
return n.attributes.node_type === opt.node_type && (opt.group instanceof RegExp ? n.group.match(opt.group) : n.group === opt.group) && (opt.name && n.attributes.node_type === "input" ? opt.name instanceof RegExp ? n.attributes.name.match(opt.name) : n.attributes.name === opt.name : !opt.name);
});
// src/context/form-state.ts
function findMethodWithMessage(nodes) {
var _a;
return (_a = nodes == null ? void 0 : nodes.filter((n) => !["default", "identifier_first"].includes(n.group))) == null ? void 0 : _a.find((node) => {
var _a2;
return ((_a2 = node.messages) == null ? void 0 : _a2.length) > 0;
});
}
function parseStateFromFlow(flow) {
var _a;
switch (flow.flowType) {
case clientFetch.FlowType.Registration:
case clientFetch.FlowType.Login: {
const methodWithMessage = findMethodWithMessage(flow.flow.ui.nodes);
if (flow.flow.active == "link_recovery") {
return { current: "method_active", method: "link" };
} else if (flow.flow.active == "code_recovery") {
return { current: "method_active", method: "code" };
} else if (methodWithMessage) {
return { current: "method_active", method: methodWithMessage.group };
} else if (flow.flow.active && !["default", "identifier_first", "oidc", "saml"].includes(
flow.flow.active
)) {
return { current: "method_active", method: flow.flow.active };
} else if (isChoosingMethod(flow)) {
const authMethods = nodesToAuthMethodGroups(flow.flow.ui.nodes);
if (authMethods.length === 1 && authMethods[0] !== "code") {
return { current: "method_active", method: authMethods[0] };
}
return { current: "select_method" };
} else if ((_a = flow.flow.ui.messages) == null ? void 0 : _a.some((m) => m.id === 1010016)) {
return { current: "select_method" };
}
return { current: "provide_identifier" };
}
case clientFetch.FlowType.Recovery:
case clientFetch.FlowType.Verification:
if (flow.flow.active === "code" || flow.flow.active === "link") {
if (flow.flow.state === "choose_method") {
return { current: "provide_identifier" };
}
return { current: "method_active", method: flow.flow.active };
}
break;
case clientFetch.FlowType.Settings:
return { current: "settings" };
case clientFetch.FlowType.OAuth2Consent:
return { current: "method_active", method: "oauth2_consent" };
}
console.warn(
`[Ory/Elements React] Encountered an unknown form state on ${flow.flowType} flow with ID ${flow.flow.id}`
);
throw new Error("Unknown form state");
}
function useFormStateReducer(flow) {
const action = parseStateFromFlow(flow);
const [selectedMethod, setSelectedMethod] = react.useState();
const formStateReducer = (state, action2) => {
switch (action2.type) {
case "action_flow_update": {
if (selectedMethod)
return { current: "method_active", method: selectedMethod };
return parseStateFromFlow(action2.flow);
}
case "action_select_method": {
setSelectedMethod(action2.method);
return { current: "method_active", method: action2.method };
}
}
return state;
};
return react.useReducer(formStateReducer, action);
}
function useOryFlow() {
const ctx = react.useContext(OryFlowContext);
if (!ctx) {
throw new Error("useOryFlow must be used within a OryFlowProvider");
}
return ctx;
}
var OryFlowContext = react.createContext(null);
function OryFlowProvider({
children,
...container
}) {
const [flowContainer, setFlowContainer] = react.useState(container);
const [formState, dispatchFormState] = useFormStateReducer(container);
return /* @__PURE__ */ jsxRuntime.jsx(
OryFlowContext.Provider,
{
value: {
...flowContainer,
setFlowContainer: (flowContainer2) => {
setFlowContainer(flowContainer2);
dispatchFormState({
type: "action_flow_update",
flow: flowContainer2
});
},
formState,
dispatchFormState
},
children
}
);
}
function mergeTranslations(customTranslations) {
return Object.keys(customTranslations).reduce((acc, key) => {
acc[key] = { ...OryLocales[key], ...customTranslations[key] };
return acc;
}, OryLocales);
}
var IntlProvider = ({
children,
locale,
customTranslations
}) => {
const messages = mergeTranslations(customTranslations != null ? customTranslations : {});
return /* @__PURE__ */ jsxRuntime.jsx(
reactIntl.IntlProvider,
{
onWarn: () => ({}),
defaultRichTextElements: {
del: (chunks) => /* @__PURE__ */ jsxRuntime.jsx("del", { children: chunks })
},
locale,
messages: messages[locale],
defaultLocale: "en",
children
}
);
};
function OryProvider({
children,
components: Components,
...oryFlowProps
}) {
var _a, _b, _c;
return /* @__PURE__ */ jsxRuntime.jsx(
IntlProvider,
{
locale: (_b = (_a = oryFlowProps.config.intl) == null ? void 0 : _a.locale) != null ? _b : "en",
customTranslations: (_c = oryFlowProps.config.intl) == null ? void 0 : _c.customTranslations,
children: /* @__PURE__ */ jsxRuntime.jsx(OryFlowProvider, { ...oryFlowProps, children: /* @__PURE__ */ jsxRuntime.jsx(OryComponentProvider, { components: Components, children }) })
}
);
}
function OryCardHeader() {
const { Card } = useComponents();
return /* @__PURE__ */ jsxRuntime.jsx(Card.Header, {});
}
function computeDefaultValues(nodes) {
return nodes.reduce((acc, node) => {
const attrs = node.attributes;
if (clientFetch.isUiNodeInputAttributes(attrs)) {
if (attrs.type === "checkbox" && typeof attrs.value === "undefined") {
attrs.value = false;
}
if (attrs.name === "method" || attrs.type === "submit" || typeof attrs.value === "undefined") {
return acc;
}
if (attrs.name.startsWith("grant_scope")) {
const scope = attrs.value;
if (Array.isArray(acc.grant_scope)) {
return {
...acc,
// We want to have all scopes accepted by default, so that the user has to actively uncheck them.
grant_scope: [...acc.grant_scope, scope]
};
} else if (!acc.grant_scope) {
return {
...acc,
grant_scope: [scope]
};
}
return acc;
}
return unrollTrait(
{
name: attrs.name,
value: attrs.value
},
acc
);
}
return acc;
}, {});
}
function unrollTrait(input, output = {}) {
const keys = input.name.split(".");
let current = output;
keys.forEach((key, index) => {
if (!key) return;
current = current[key] = index === keys.length - 1 ? input.value : current[key] || {};
});
return output;
}
function isCodeResendRequest(data) {
var _a;
return (_a = data.email) != null ? _a : data.resend;
}
function useOryFormResolver() {
const flowContainer = useOryFlow();
return (data) => {
if (flowContainer.formState.current === "method_active") {
if (
// When we submit a code
data.method === "code" && // And the code is not present
!data.code && // And the flow is not a code resend request
!isCodeResendRequest(data) && // And the flow has a code input node
flowContainer.flow.ui.nodes.find(({ attributes, group }) => {
if (!clientFetch.isUiNodeInputAttributes(attributes)) {
return false;
}
return group === "code" && attributes.name === "code" && attributes.type !== "hidden";
})
) {
return {
values: data,
errors: {
// We know the code node exists, so we can safely hardcode the ID.
code: {
id: 4000002,
context: {
property: "code"
},
type: "error",
text: "Property code is missing"
}
}
};
}
}
return {
values: data,
errors: {}
};
};
}
function OryFormProvider({
children,
nodes
}) {
const flowContainer = useOryFlow();
const defaultNodes = nodes ? flowContainer.flow.ui.nodes.filter((node) => node.group === clientFetch.UiNodeGroupEnum.Default).concat(nodes) : flowContainer.flow.ui.nodes;
const methods = reactHookForm.useForm({
// TODO: Generify this, so we have typesafety in the submit handler.
defaultValues: computeDefaultValues(defaultNodes),
resolver: useOryFormResolver()
});
return /* @__PURE__ */ jsxRuntime.jsx(reactHookForm.FormProvider, { ...methods, children });
}
function OryCard({ children }) {
const { Card } = useComponents();
return /* @__PURE__ */ jsxRuntime.jsx(Card.Root, { children: /* @__PURE__ */ jsxRuntime.jsx(OryFormProvider, { children }) });
}
function OryCardFooter() {
const { Card } = useComponents();
return /* @__PURE__ */ jsxRuntime.jsx(Card.Footer, {});
}
function OryCardContent({ children }) {
const { Card } = useComponents();
return /* @__PURE__ */ jsxRuntime.jsx(Card.Content, { children });
}
// src/theme/default/utils/form.ts
function isGroupImmediateSubmit(group) {
return group === "code";
}
function frontendClient(sdkUrl, opts = {}) {
const config = new clientFetch.Configuration({
...opts,
basePath: sdkUrl,
headers: {
Accept: "application/json",
...opts.headers
}
});
return new clientFetch.FrontendApi(config);
}
// src/util/internal.ts
function replaceWindowFlowId(flow) {
const url = new URL(window.location.href);
url.searchParams.set("flow", flow);
window.location.href = url.toString();
}
// src/util/onSubmitLogin.ts
async function onSubmitLogin({ config, flow }, {
setFlowContainer,
body,
onRedirect
}) {
var _a;
if (!config.sdk.url) {
throw new Error(
`Please supply your Ory Network SDK url to the Ory Elements configuration.`
);
}
await frontendClient(config.sdk.url, (_a = config.sdk.options) != null ? _a : {}).updateLoginFlowRaw({
flow: flow.id,
updateLoginFlowBody: body
}).then(() => {
var _a2;
window.location.href = // eslint-disable-next-line promise/always-return
(_a2 = flow.return_to) != null ? _a2 : config.sdk.url + "/self-service/login/browser";
}).catch(
clientFetch.handleFlowError({
onRestartFlow: (useFlowId) => {
if (useFlowId) {
replaceWindowFlowId(useFlowId);
} else {
onRedirect(clientFetch.loginUrl(config), true);
}
},
onValidationError: (body2) => {
setFlowContainer({
config,
flow: body2,
flowType: clientFetch.FlowType.Login
});
},
onRedirect
})
);
}
async function onSubmitRecovery({ config, flow }, {
setFlowContainer,
body,
onRedirect
}) {
var _a;
if (!config.sdk.url) {
throw new Error(
`Please supply your Ory Network SDK url to the Ory Elements configuration.`
);
}
await frontendClient(config.sdk.url, (_a = config.sdk.options) != null ? _a : {}).updateRecoveryFlowRaw({
flow: flow.id,
updateRecoveryFlowBody: body
}).then(async (res) => {
const flow2 = await res.value();
const didContinueWith = clientFetch.handleContinueWith(flow2.continue_with, {
onRedirect
});
if (didContinueWith) {
return;
}
setFlowContainer({
flow: flow2,
flowType: clientFetch.FlowType.Recovery,
config
});
}).catch(
clientFetch.handleFlowError({
onRestartFlow: (useFlowId) => {
if (useFlowId) {
replaceWindowFlowId(useFlowId);
} else {
onRedirect(clientFetch.recoveryUrl(config), true);
}
},
onValidationError: (body2) => {
if ("error" in body2) {
handleContinueWithRecoveryUIError(body2.error, config, onRedirect);
return;
} else {
setFlowContainer({
flow: body2,
flowType: clientFetch.FlowType.Recovery,
config
});
}
},
onRedirect
})
);
}
function handleContinueWithRecoveryUIError(error, config, onRedirect) {
if ("continue_with" in error.details && Array.isArray(error.details.continue_with)) {
const continueWithRecovery = error.details.continue_with.find(clientFetch.instanceOfContinueWithRecoveryUi);
if ((continueWithRecovery == null ? void 0 : continueWithRecovery.action) === "show_recovery_ui") {
onRedirect(
config.project.recovery_ui_url + "?flow=" + (continueWithRecovery == null ? void 0 : continueWithRecovery.flow.id),
false
);
return;
}
}
onRedirect(clientFetch.recoveryUrl(config), true);
}
async function onSubmitRegistration({ config, flow }, {
setFlowContainer,
body,
onRedirect
}) {
var _a;
if (!config.sdk.url) {
throw new Error(
`Please supply your Ory Network SDK url to the Ory Elements configuration.`
);
}
const client = frontendClient(config.sdk.url, (_a = config.sdk.options) != null ? _a : {});
await client.updateRegistrationFlowRaw({
flow: flow.id,
updateRegistrationFlowBody: body
}).then(async (res) => {
const body2 = await res.value();
const didContinueWith = clientFetch.handleContinueWith(body2.continue_with, {
onRedirect
});
if (didContinueWith) {
return;
}
onRedirect(clientFetch.registrationUrl(config), true);
}).catch(
clientFetch.handleFlowError({
onRestartFlow: (useFlowId) => {
if (useFlowId) {
replaceWindowFlowId(useFlowId);
} else {
onRedirect(clientFetch.registrationUrl(config), true);
}
},
onValidationError: (body2) => {
setFlowContainer({
flow: body2,
flowType: clientFetch.FlowType.Registration,
config
});
},
onRedirect
})
);
}
async function onSubmitSettings({ config, flow }, {
setFlowContainer,
body,
onRedirect
}) {
var _a;
if (!config.sdk.url) {
throw new Error(
`Please supply your Ory Network SDK url to the Ory Elements configuration.`
);
}
const client = frontendClient(config.sdk.url, (_a = config.sdk.options) != null ? _a : {});
await client.updateSettingsFlowRaw({
flow: flow.id,
updateSettingsFlowBody: body
}).then(async (res) => {
const body2 = await res.value();
const didContinueWith = clientFetch.handleContinueWith(body2.continue_with, {
onRedirect
});
if (didContinueWith) {
return;
}
setFlowContainer({
flow: body2,
flowType: clientFetch.FlowType.Settings,
config
});
}).catch(
clientFetch.handleFlowError({
onRestartFlow: (useFlowId) => {
if (useFlowId) {
replaceWindowFlowId(useFlowId);
} else {
onRedirect(clientFetch.settingsUrl(config), true);
}
},
onValidationError: (body2) => {
setFlowContainer({
flow: body2,
flowType: clientFetch.FlowType.Settings,
config
});
},
onRedirect
})
).catch((err) => {
if (clientFetch.isResponseError(err)) {
if (err.response.status === 401) {
return onRedirect(
clientFetch.loginUrl(config) + "?return_to=" + clientFetch.settingsUrl(config),
true
);
}
throw err;
}
});
}
async function onSubmitVerification({ config, flow }, {
setFlowContainer,
body,
onRedirect
}) {
var _a;
if (!config.sdk.url) {
throw new Error(
`Please supply your Ory Network SDK URL to the Ory Elements configuration.`
);
}
await frontendClient(config.sdk.url, (_a = config.sdk.options) != null ? _a : {}).updateVerificationFlowRaw({
flow: flow.id,
updateVerificationFlowBody: body
}).then(
async (res) => setFlowContainer({
flow: await res.value(),
flowType: clientFetch.FlowType.Verification,
config
})
).catch(
clientFetch.handleFlowError({
onRestartFlow: (useFlowId) => {
if (useFlowId) {
replaceWindowFlowId(useFlowId);
} else {
onRedirect(clientFetch.verificationUrl(config), true);
}
},
onValidationError: (body2) => {
setFlowContainer({
flow: body2,
flowType: clientFetch.FlowType.Verification,
config
});
},
onRedirect
})
);
}
// src/components/form/useOryFormSubmit.ts
var supportsSelectAccountPrompt = ["google", "github"];
function useOryFormSubmit(onAfterSubmit) {
const flowContainer = useOryFlow();
const methods = reactHookForm.useFormContext();
const handleSuccess = (flow) => {
flowContainer.setFlowContainer(flow);
methods.reset(computeDefaultValues(flow.flow.ui.nodes));
};
const onRedirect = (url, _external) => {
window.location.assign(url);
};
const onSubmit = async (data) => {
switch (flowContainer.flowType) {
case clientFetch.FlowType.Login: {
const submitData = {
...data
};
if (submitData.method === "code" && data.code) {
submitData.resend = "";
}
await onSubmitLogin(flowContainer, {
onRedirect,
setFlowContainer: handleSuccess,
body: submitData
});
break;
}
case clientFetch.FlowType.Registration: {
const submitData = {
...data
};
if (submitData.method === "code" && submitData.code) {
submitData.resend = "";
}
await onSubmitRegistration(flowContainer, {
onRedirect,
setFlowContainer: handleSuccess,
body: submitData
});
break;
}
case clientFetch.FlowType.Verification:
await onSubmitVerification(flowContainer, {
onRedirect,
setFlowContainer: handleSuccess,
body: data
});
break;
case clientFetch.FlowType.Recovery: {
const submitData = {
...data
};
if (data.code) {
submitData.email = "";
}
await onSubmitRecovery(flowContainer, {
onRedirect,
setFlowContainer: handleSuccess,
body: submitData
});
break;
}
case clientFetch.FlowType.Settings: {
const submitData = {
...data
};
if ("totp_unlink" in submitData) {
submitData.method = "totp";
}
if ("lookup_secret_confirm" in submitData || "lookup_secret_reveal" in submitData || "lookup_secret_regenerate" in submitData || "lookup_secret_disable" in submitData) {
submitData.method = "lookup_secret";
}
if (submitData.method === clientFetch.UiNodeGroupEnum.Oidc && submitData.link && supportsSelectAccountPrompt.includes(submitData.link)) {
submitData.upstream_parameters = {
prompt: "select_account"
};
}
if ("webauthn_remove" in submitData) {
submitData.method = "webauthn";
}
if ("passkey_remove" in submitData) {
submitData.method = "passkey";
}
await onSubmitSettings(flowContainer, {
onRedirect,
setFlowContainer: handleSuccess,
body: submitData
});
break;
}
case clientFetch.FlowType.OAuth2Consent: {
const response = await fetch(flowContainer.flow.ui.action, {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json"
}
});
const oauth2Success = await response.json();
if (oauth2Success.redirect_to && typeof oauth2Success.redirect_to === "string") {
onRedirect(oauth2Success.redirect_to);
}
}
}
if ("password" in data) {
methods.setValue("password", "");
}
if ("code" in data) {
methods.setValue("code", "");
}
if ("totp_code" in data) {
methods.setValue("totp_code", "");
}
onAfterSubmit == null ? void 0 : onAfterSubmit(data.method);
};
return onSubmit;
}
function OryForm({
children,
onAfterSubmit,
"data-testid": dataTestId
}) {
const { Form } = useComponents();
const flowContainer = useOryFlow();
const methods = reactHookForm.useFormContext();
const { Message } = useComponents();
const intl = reactIntl.useIntl();
const onSubmit = useOryFormSubmit(onAfterSubmit);
const hasMethods = flowContainer.flow.ui.nodes.some((node) => {
if (clientFetch.isUiNodeInputAttributes(node.attributes)) {
return node.attributes.name !== "csrf_token";
} else if (clientFetch.isUiNodeAnchorAttributes(node.attributes)) {
return true;
} else if (clientFetch.isUiNodeImageAttributes(node.attributes)) {
return true;
} else if (clientFetch.isUiNodeScriptAttributes(node.attributes)) {
return true;
}
return false;
});
if (!hasMethods) {
const m = {
id: 5000002,
text: intl.formatMessage({
id: `identities.messages.${5000002}`,
defaultMessage: "No authentication methods are available for this request. Please contact the site or app owner."
}),
type: "error"
};
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": dataTestId, children: /* @__PURE__ */ jsxRuntime.jsx(Message.Root, { children: /* @__PURE__ */ jsxRuntime.jsx(Message.Content, { message: m }, m.id) }) });
}
if (flowContainer.flowType === clientFetch.FlowType.Login && flowContainer.formState.current === "method_active" && flowContainer.formState.method === "code") {
methods.setValue("method", "code");
}
return /* @__PURE__ */ jsxRuntime.jsx(
Form.Root,
{
"data-testid": dataTestId,
action: flowContainer.flow.ui.action,
method: flowContainer.flow.ui.method,
onSubmit: (e) => void methods.handleSubmit(onSubmit)(e),
children
}
);
}
var messageIdsToHide = [
1040009,
1060003,
1080003,
1010004,
1010014,
1040005,
1010016
];
function OryCardValidationMessages({ ...props }) {
var _a;
const { flow } = useOryFlow();
const messages = (_a = flow.ui.messages) == null ? void 0 : _a.filter(
(m) => !messageIdsToHide.includes(m.id)
);
const { Message } = useComponents();
if (!messages) {
return null;
}
return /* @__PURE__ */ jsxRuntime.jsx(Message.Root, { ...props, children: messages == null ? void 0 : messages.map((message) => /* @__PURE__ */ jsxRuntime.jsx(Message.Content, { message }, message.id)) });
}
var NodeInput = ({
node,
attributes
}) => {
var _a;
const { Node: Node2 } = useComponents();
const { setValue, watch } = reactHookForm.useFormContext();
const {
onloadTrigger,
onclickTrigger,
// These properties do not exist on input fields so we remove them (as we already have handled them).
onclick: _ignoredOnclick,
onload: _ignoredOnload,
//
...attrs
} = attributes;
const isResendNode = ((_a = node.meta.label) == null ? void 0 : _a.id) === 1070008;
const isScreenSelectionNode = "name" in node.attributes && node.attributes.name === "screen";
const setFormValue = () => {
if (attrs.value && !(isResendNode || isScreenSelectionNode || node.group === clientFetch.UiNodeGroupEnum.Oauth2Consent)) {
setValue(attrs.name, attrs.value);
}
};
const hasRun = react.useRef(false);
react.useEffect(
() => {
setFormValue();
if (!hasRun.current && onloadTrigger) {
hasRun.current = true;
triggerToWindowCall(onloadTrigger);
}
},
// TODO(jonas): make sure onloadTrigger is stable
// eslint-disable-next-line react-hooks/exhaustive-deps -- ignore onloadTrigger for now, until we make sure this is stable
[]
);
const handleClick = () => {
setFormValue();
if (onclickTrigger) {
triggerToWindowCall(onclickTrigger);
}
};
const isSocial = (attrs.name === "provider" || attrs.name === "link") && (node.group === clientFetch.UiNodeGroupEnum.Oidc || node.group === clientFetch.UiNodeGroupEnum.Saml);
const isPinCodeInput = attrs.name === "code" && node.group === "code" || attrs.name === "totp_code" && node.group === "totp";
const handleScopeChange = (checked) => {
const scopes = watch("grant_scope");
if (Array.isArray(scopes)) {
if (checked) {
setValue("grant_scope", Array.from(/* @__PURE__ */ new Set([...scopes, attrs.value])));
} else {
setValue(
"grant_scope",
scopes.filter((scope) => scope !== attrs.value)
);
}
}
};
switch (attributes.type) {
case clientFetch.UiNodeInputAttributesTypeEnum.Submit:
case clientFetch.UiNodeInputAttributesTypeEnum.Button:
if (isSocial) {
return null;
}
if (isResendNode || isScreenSelectionNode) {
return null;
}
if (node.group === "oauth2_consent") {
return null;
}
return /* @__PURE__ */ jsxRuntime.jsx(
Node2.Label,
{
attributes: { ...attrs, label: void 0 },
node: { ...node, meta: { ...node.meta, label: void 0 } },
children: /* @__PURE__ */ jsxRuntime.jsx(Node2.Button, { attributes: attrs, node, onClick: handleClick })
}
);
case clientFetch.UiNodeInputAttributesTypeEnum.DatetimeLocal:
throw new Error("Not implemented");
case clientFetch.UiNodeInputAttributesTypeEnum.Checkbox:
if (node.group === "oauth2_consent" && node.attributes.node_type === "input") {
switch (node.attributes.name) {
case "grant_scope":
return /* @__PURE__ */ jsxRuntime.jsx(
Node2.ConsentScopeCheckbox,
{
attributes: attrs,
node,
onCheckedChange: handleScopeChange
}
);
default:
return null;
}
}
return /* @__PURE__ */ jsxRuntime.jsx(
Node2.Label,
{
attributes: { ...attrs, label: void 0 },
node: { ...node, meta: { ...node.meta, label: void 0 } },
children: /* @__PURE__ */ jsxRuntime.jsx(Node2.Checkbox, { attributes: attrs, node, onClick: handleClick })
}
);
case clientFetch.UiNodeInputAttributesTypeEnum.Hidden:
return /* @__PURE__ */ jsxRuntime.jsx(Node2.Input, { attributes: attrs, node, onClick: handleClick });
default:
if (isPinCodeInput) {
return /* @__PURE__ */ jsxRuntime.jsx(Node2.Label, { attributes: attrs, node, children: /* @__PURE__ */ jsxRuntime.jsx(
Node2.CodeInput,
{
attributes: attrs,
node,
onClick: handleClick
}
) });
}
return /* @__PURE__ */ jsxRuntime.jsx(Node2.Label, { attributes: attrs, node, children: /* @__PURE__ */ jsxRuntime.jsx(Node2.Input, { attributes: attrs, node, onClick: handleClick }) });
}
};
var Node = ({ node, onClick }) => {
const { Node: Node2 } = useComponents();
if (node.group === clientFetch.UiNodeGroupEnum.Captcha) {
return /* @__PURE__ */ jsxRuntime.jsx(Node2.Captcha, { node });
}
if (clientFetch.isUiNodeImageAttributes(node.attributes)) {
return /* @__PURE__ */ jsxRuntime.jsx(Node2.Image, { node, attributes: node.attributes });
} else if (clientFetch.isUiNodeTextAttributes(node.attributes)) {
const attrs = node.attributes;
return /* @__PURE__ */ jsxRuntime.jsx(Node2.Text, { attributes: attrs, node });
} else if (clientFetch.isUiNodeInputAttributes(node.attributes)) {
return /* @__PURE__ */ jsxRuntime.jsx(NodeInput, { node, attributes: node.attributes, onClick });
} else if (clientFetch.isUiNodeAnchorAttributes(node.attributes)) {
return /* @__PURE__ */ jsxRuntime.jsx(Node2.Anchor, { attributes: node.attributes, node });
} else if (clientFetch.isUiNodeScriptAttributes(node.attributes)) {
const {
crossorigin,
referrerpolicy,
node_type: _nodeType,
...attributes
} = node.attributes;
return /* @__PURE__ */ jsxRuntime.jsx(
"script",
{
crossOrigin: crossorigin,
referrerPolicy: referrerpolicy,
...attributes
}
);
}
return null;
};
function OryFormOidcButtons() {
const {
flow: { ui }
} = useOryFlow();
const { setValue } = reactHookForm.useFormContext();
const filteredNodes = ui.nodes.filter(
(node) => node.group === clientFetch.UiNodeGroupEnum.Oidc || node.group === clientFetch.UiNodeGroupEnum.Saml
);
const { Form, Node: Node2 } = useComponents();
if (filteredNodes.length === 0) {
return null;
}
return /* @__PURE__ */ jsxRuntime.jsx(Form.OidcRoot, { nodes: filteredNodes, children: filteredNodes.map((node, k) => /* @__PURE__ */ jsxRuntime.jsx(
Node2.OidcButton,
{
node,
attributes: node.attributes,
onClick: () => {
setValue(
"provider",
node.attributes.value
);
setValue("method", node.group);
}
},
k
)) });
}
function OryFormSocialButtonsForm() {
const {
flow: { ui }
} = useOryFlow();
const filteredNodes = ui.nodes.filter(
(node) => node.group === clientFetch.UiNodeGroupEnum.Saml || node.group === clientFetch.UiNodeGroupEnum.Oidc
);
if (filteredNodes.length === 0) {
return null;
}
return /* @__PURE__ */ jsxRuntime.jsx(OryFormProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(OryForm, { "data-testid": `ory/form/methods/oidc-saml`, children: /* @__PURE__ */ jsxRuntime.jsx(OryFormOidcButtons, {}) }) });
}
function isUINodeGroupEnum(method) {
return Object.values(clientFetch.UiNodeGroupEnum).includes(method);
}
function OryTwoStepCard() {
var _a, _b, _c, _d;
const { Form, Card } = useComponents();
const { flow, flowType, formState, dispatchFormState } = useOryFlow();
const { ui } = flow;
const nodeSorter = useNodeSorter();
const sortNodes = (a, b) => nodeSorter(a, b, { flowType });
const groupsToShow = useNodesGroups(ui.nodes, {
// We only want to render groups that have visible elements.
omit: ["script", "input_hidden"]
});
const authMethodBlocks = Object.fromEntries(
Object.values(clientFetch.UiNodeGroupEnum).filter((group) => {
var _a2;
return (_a2 = groupsToShow.groups[group]) == null ? void 0 : _a2.length;
}).filter(
(group) => ![
clientFetch.UiNodeGroupEnum.Oidc,
clientFetch.UiNodeGroupEnum.Saml,
clientFetch.UiNodeGroupEnum.Default,
clientFetch.UiNodeGroupEnum.IdentifierFirst,
clientFetch.UiNodeGroupEnum.Profile,
clientFetch.UiNodeGroupEnum.Captcha
].includes(group)
).map((g) => [g, {}])
);
const authMethodAdditionalNodes = ui.nodes.filter(
({ group }) => [
clientFetch.UiNodeGroupEnum.Oidc,
clientFetch.UiNodeGroupEnum.Saml,
clientFetch.UiNodeGroupEnum.Default,
clientFetch.UiNodeGroupEnum.IdentifierFirst,
clientFetch.UiNodeGroupEnum.Profile,
clientFetch.UiNodeGroupEnum.Captcha
].includes(group)
);
if (clientFetch.UiNodeGroupEnum.Code in authMethodBlocks) {
let identifier = (_b = (_a = findNode(ui.nodes, {
group: "identifier_first",
node_type: "input",
name: "identifier"
})) == null ? void 0 : _a.attributes) == null ? void 0 : _b.value;
identifier || (identifier = (_d = (_c = findNode(ui.nodes, {
group: "code",
node_type: "input",
name: "address"
})) == null ? void 0 : _c.attributes) == null ? void 0 : _d.value);
if (identifier) {
authMethodBlocks[clientFetch.UiNodeGroupEnum.Code] = {
title: {
id: "identities.messages.1010023",
values: { address: identifier }
}
};
}
}
const nonSsoNodes = removeSsoNodes(ui.nodes);
const finalNodes = formState.current === "method_active" ? getFinalNodes(groupsToShow.groups, formState.method) : [];
const handleAfterFormSubmit = (method) => {
if (typeof method !== "string" || !isUINodeGroupEnum(method)) {
return;
}
if (isGroupImmediateSubmit(method)) {
dispatchFormState({
type: "action_select_method",
method
});
}
};
const hasSso = ui.nodes.some(
(node) => node.group === clientFetch.UiNodeGroupEnum.Oidc || node.group === clientFetch.UiNodeGroupEnum.Saml
);
const showSso = !(formState.current === "method_active" && !(formState.method === clientFetch.UiNodeGroupEnum.Oidc || formState.method === clientFetch.UiNodeGroupEnum.Saml));
const showSsoDivider = hasSso && nonSsoNodes.some((n) => {
if (clientFetch.isUiNodeInputAttributes(n.attributes)) {
return n.attributes.type !== clientFetch.UiNodeInputAttributesTypeEnum.Hidden;
} else if (clientFetch.isUiNodeScriptAttributes(n.attributes)) {
return false;
}
return true;
});
switch (formState.current) {
case "provide_identifier":
return /* @__PURE__ */ jsxRuntime.jsxs(OryCard, { children: [
/* @__PURE__ */ jsxRuntime.jsx(OryCardHeader, {}),
/* @__PURE__ */ jsxRuntime.jsxs(OryCardContent, { children: [
/* @__PURE__ */ jsxRuntime.jsx(OryCardValidationMessages, {}),
showSso && /* @__PURE__ */ jsxRuntime.jsx(OryFormSocialButtonsForm, {}),
/* @__PURE__ */ jsxRuntime.jsx(
OryForm,
{
"data-testid": `ory/form/methods/local`,
onAfterSubmit: handleAfterFormSubmit,
children: /* @__PURE__ */ jsxRuntime.jsxs(Form.Group, { children: [
showSsoDivider && /* @__PURE__ */ jsxRuntime.jsx(Card.Divider, {}),
nonSsoNodes.sort(sortNodes).map((node, k) => /* @__PURE__ */ jsxRuntime.jsx(Node, { node }, k))
] })
}
)
] }),
/* @__PURE__ */ jsxRuntime.jsx(OryCardFooter, {})
] });
case "select_method":
return /* @__PURE__ */ jsxRuntime.jsxs(OryCard, { children: [
/* @__PURE__ */ jsxRuntime.jsx(OryCardHeader, {}),
/* @__PURE__ */ jsxRuntime.jsxs(OryCardContent, { children: [
/* @__PURE__ */ jsxRuntime.jsx(OryCardValidationMessages, {}),
showSso && /* @__PURE__ */ jsxRuntime.jsx(OryFormSocialButtonsForm, {}),
Object.entries(authMethodBlocks).length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
OryForm,
{
"data-testid": `ory/form/methods/local`,
onAfterSubmit: handleAfterFormSubmit,
children: /* @__PURE__ */ jsxRuntime.jsxs(Form.Group, { children: [
/* @__PURE__ */ jsxRuntime.jsx(Card.Divider, {}),
/* @__PURE__ */ jsxRuntime.jsx(
AuthMethodList,
{
options: authMethodBlocks,
setSelectedGroup: (group) => dispatchFormState({
type: "action_select_method",
method: group
})
}
),
authMethodAdditionalNodes.sort(sortNodes).map((node, k) => /* @__PURE__ */ jsxRuntime.jsx(Node, { node }, k))
] })
}
)
] }),
/* @__PURE__ */ jsxRuntime.jsx(OryCardFooter, {})
] });
case "method_active":
return /* @__PURE__ */ jsxRuntime.jsxs(OryCard, { children: [
/* @__PURE__ */ jsxRuntime.jsx(OryCardHeader, {}),
/* @__PURE__ */ jsxRuntime.jsxs(OryCardContent, { children: [
/* @__PURE__ */ jsxRuntime.jsx(OryCardValidationMessages, {}),
showSso && /* @__PURE__ */ jsxRuntime.jsx(OryFormSocialButtonsForm, {}),
/* @__PURE__ */ jsxRuntime.jsx(
OryForm,
{
"data-testid": `ory/form/methods/local`,
onAfterSubmit: handleAfterFormSubmit,
children: /* @__PURE__ */ jsxRuntime.jsxs(Form.Group, { children: [
ui.nodes.filter(
(n) => clientFetch.isUiNodeScriptAttributes(n.attributes) || n.group === clientFetch.UiNodeGroupEnum.Captcha || n.group === clientFetch.UiNodeGroupEnum.Default || n.group === clientFetch.UiNodeGroupEnum.Profile
).map((node, k) => /* @__PURE__ */ jsxRuntime.jsx(Node, { node }, k)),
finalNodes.sort(sortNodes).map((node, k) => /* @__PURE__ */ jsxRuntime.jsx(Node, { node }, k))
] })
}
)
] }),
/* @__PURE__ */ jsxRuntime.jsx(OryCardFooter, {})
] });
}
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
"unknown form state: ",
formState.current
] });
}
function AuthMethodList({ options, setSelectedGroup }) {
const { Card } = useComponents();
const { setValue, getValues } = reactHookForm.useFormContext();
if (Object.entries(options).length === 0) {
return null;
}
const handleClick = (group, options2) => {
var _a, _b, _c, _d;
if (isGroupImmediateSubmit(group)) {
if (group === "code" && !getValues("identifier") && ((_b = (_a = options2 == null ? void 0 : options2.title) == null ? void 0 : _a.values) == null ? void 0 : _b.address)) {
setValue("identifier", (_d = (_c = options2 == null ? void 0 : options2.title) == null ? void 0 : _c.values) == null ? void 0 : _d.address);
}
setValue("method", group);
} else {
setSelectedGroup(group);
}
};
return /* @__PURE__ */ jsxRuntime.jsx(Card.AuthMethodListContainer, { children: Object.entries(options).map(([group, options2]) => /* @__PURE__ */ jsxRuntime.jsx(
Card.AuthMethodListItem,
{
group,
title: options2.title,
onClick: () => handleClick(group, options2)
},
group
)) });
}
function OryFormGroups({ groups }) {
const {
flow: { ui }
} = useOryFlow();
const nodeSorter = useNodeSorter();
const { flowType } = useOryFlow();
const { Form } = useComponents();
const nodes = ui.nodes.filter((node) => groups.indexOf(node.group) > -1).sort((a, b) => nodeSorter(a, b, { flowType }));
return /* @__PURE__ */ jsxRuntime.jsx(Form.Group, { children: nodes.map((node, k) => {
return /* @__PURE__ */ jsxRuntime.jsx(Node, { node }, k);
}) });
}
function OryFormSection({
children,
nodes,
...rest
}) {
return /* @__PURE__ */ jsxRuntime.jsx(OryFormProvider, { nodes, children: /* @__PURE__ */ jsxRuntime.jsx(OryFormSectionInner, { ...rest, children }) });
}
function OryFormSectionInner({
children,
...rest
}) {
const { Card } = useComponents();
const flowContainer = useOryFlow();
const onSubmit = useOryFormSubmit();
const methods = reactHookForm.useFormContext();
return /* @__PURE__ */ jsxRuntime.jsx(
Card.SettingsSection,
{
action: flowContainer.flow.ui.action,
method: flowContainer.flow.ui.method,
onSubmit: (e) => void methods.handleSubmit(onSubmit)(e),
...rest,
children
}
);
}
function OryConsentCard() {
const { Form, Card } = useComponents();
const flow = useOryFlow();
return /* @__PURE__ */ jsxRuntime.jsxs(OryCard, { children: [
/* @__PURE__ */ jsxRuntime.jsx(OryCardHeader, {}),
/* @__PURE__ */ jsxRuntime.jsx(OryCardContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(OryForm, { children: [
/* @__PURE__ */ jsxRuntime.jsx(Card.Divider, {}),
/* @__PURE__ */ jsxRuntime.jsx(Form.Group, { children: flow.flow.ui.nodes.map((node, k) => /* @__PURE__ */ jsxRuntime.jsx(Node, { node }, k)) }),
/* @__PURE__ */ jsxRuntime.jsx(Card.Divider, {}),
/* @__PURE__ */ jsxRuntime.jsx(OryCardFooter, {})
] }) })
] });
}
function OryFormGroupDivider() {
const { Card } = useComponents();
const {
flow: { ui }
} = useOryFlow();
const filteredNodes = ui.nodes.filter(
(node) => node.group === clientFetch.UiNodeGroupEnum.Oidc || node.group === clientFetch.UiNodeGroupEnum.Saml
);
const otherNodes = ui.nodes.filter(
(node) => !(node.group === clientFetch.UiNodeGroupEnum.Oidc || node.group === clientFetch.UiNodeGroupEnum.Saml) && node.group !== "default"
);
if (filteredNodes.length > 0 && otherNodes.length > 0) {
return /* @__PURE__ */ jsxRuntime.jsx(Card.Divider, {});
}
return null;
}
var HeadlessPageHeader = () => {
const { Page } = useComponents();
return /* @__PURE__ */ jsxRuntime.jsx(Page.Header, {});
};
var getLinkButtons = (nodes) => nodes.filter(
(node) => "name" in node.attributes && node.attributes.name === "link"
);
var getUnlinkButtons = (nodes) => nodes.filter(
(node) => "name" in node.attributes && node.attributes.name === "unlink"
);
function OrySettingsOidc({ nodes }) {
const { Card, Form } = useComponents();
const intl = reactIntl.useIntl();
const { setValue } = reactHookForm.useFormContext();
const linkButtons = getLinkButtons(nodes).map((node) => ({
...node,
onClick: () => {
if (node.attributes.node_type === "input") {
setValue("link", node.attributes.value);
setValue("method", node.group);
}
}
}));
const unlinkButtons = getUnlinkButtons(nodes).map((node) => ({
...node,
onClick: () => {
if (node.attributes.node_type === "input") {
setValue("unlink", node.attributes.value);
setValue("method", node.group);
}
}
}));
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
/* @__PURE__ */ jsxRuntime.jsx(
Card.SettingsSectionContent,
{
title: intl.formatMessage({ id: "settings.oidc.title" }),
description: intl.formatMessage({ id: "settings.oidc.description" }),
children: /* @__PURE__ */ jsxRuntime.jsx(
Form.OidcSettings,
{
linkButtons,
unlinkButtons
}
)
}
),
/* @__PURE__ */ jsxRuntime.jsx(
Card.SettingsSectionFooter,
{
text: intl.formatMessage({ id: "settings.oidc.info" })
}
)
] });
}
var getTriggerNode = (nodes) => nodes.find(
(node) => "name" in node.attributes && node.attributes.name === "passkey_register_trigger"
);
var getSettingsNodes = (nodes) => nodes.filter(
(node) => "name" in node.attributes && (node.attributes.name === "passkey_settings_register" || node.attributes.name === "passkey_create_data")
);
var getRemoveNodes = (nodes) => nodes.filter(
(node) => "name" in node.attributes && node.attributes.name === "passkey_remove"
);
function OrySettingsPasskey({ nodes }) {
const { Card, For