@ory/elements-react
Version:
Ory Elements React - a collection of React components for authentication UIs.
1 lines • 609 kB
Source Map (JSON)
{"version":3,"sources":["../../../src/theme/default/assets/ory-badge-horizontal.svg","../../../src/theme/default/assets/ory-badge-vertical.svg","../../../src/theme/default/components/card/badge.tsx","../../../src/theme/default/components/card/content.tsx","../../../src/theme/default/components/card/footer.tsx","../../../src/components/card/two-step/state-select-method.tsx","../../../src/context/component.tsx","../../../src/context/flow-context.tsx","../../../src/context/form-state.ts","../../../src/components/card/two-step/utils.ts","../../../src/theme/default/utils/form.ts","../../../src/util/ui/index.ts","../../../src/context/config.tsx","../../../src/client/config.ts","../../../src/util/client.ts","../../../src/context/intl-context.tsx","../../../src/components/form/form.tsx","../../../src/components/form/useOryFormSubmit.ts","../../../src/util/onSubmitLogin.ts","../../../src/util/sdk-helpers/error.ts","../../../src/util/sdk-helpers/utils.ts","../../../src/util/onSubmitRecovery.ts","../../../src/util/onSubmitRegistration.ts","../../../src/util/onSubmitSettings.ts","../../../src/util/onSubmitVerification.ts","../../../src/components/form/form-helpers.ts","../../../src/components/form/groups.tsx","../../../src/components/form/nodes/input.tsx","../../../src/components/form/nodes/node.tsx","../../../src/components/form/messages.tsx","../../../src/components/form/social.tsx","../../../src/components/form/form-provider.tsx","../../../src/components/form/form-resolver.ts","../../../src/components/form/section.tsx","../../../src/components/generic/divider.tsx","../../../src/components/generic/page-header.tsx","../../../src/components/settings/settings-card.tsx","../../../src/components/settings/oidc-settings.tsx","../../../src/components/settings/passkey-settings.tsx","../../../src/components/settings/recovery-codes-settings.tsx","../../../src/components/settings/totp-settings.tsx","../../../src/components/settings/webauthn-settings.tsx","../../../src/util/i18n/index.ts","../../../src/locales/en.json","../../../src/locales/de.json","../../../src/locales/es.json","../../../src/locales/fr.json","../../../src/locales/nl.json","../../../src/locales/pl.json","../../../src/locales/pt.json","../../../src/locales/sv.json","../../../src/locales/index.ts","../../../src/context/provider.tsx","../../../src/components/card/header.tsx","../../../src/components/card/card.tsx","../../../src/components/card/footer.tsx","../../../src/components/card/content.tsx","../../../src/components/card/two-step/state-method-active.tsx","../../../src/components/card/two-step/state-provide-identifier.tsx","../../../src/components/card/card-two-step.tsx","../../../src/components/card/card-consent.tsx","../../../src/components/card/two-step/list-methods.tsx","../../../src/util/nodes.ts","../../../src/theme/default/utils/logout.ts","../../../src/theme/default/utils/url.ts","../../../src/theme/default/components/card/header.tsx","../../../src/theme/default/utils/constructCardHeader.ts","../../../src/theme/default/components/card/current-identifier-button.tsx","../../../src/theme/default/utils/attributes.ts","../../../src/util/omitAttributes.ts","../../../src/theme/default/assets/icons/arrow-left.svg","../../../src/theme/default/components/card/logo.tsx","../../../src/theme/default/components/card/layout.tsx","../../../src/theme/default/components/card/index.tsx","../../../src/theme/default/utils/cn.ts","../../../src/theme/default/components/form/index.tsx","../../../src/theme/default/components/form/social.tsx","../../../src/theme/default/provider-logos/apple.svg","../../../src/theme/default/provider-logos/auth0.svg","../../../src/theme/default/provider-logos/discord.svg","../../../src/theme/default/provider-logos/facebook.svg","../../../src/theme/default/provider-logos/github.svg","../../../src/theme/default/provider-logos/gitlab.svg","../../../src/theme/default/provider-logos/google.svg","../../../src/theme/default/provider-logos/linkedin.svg","../../../src/theme/default/provider-logos/microsoft.svg","../../../src/theme/default/provider-logos/slack.svg","../../../src/theme/default/provider-logos/spotify.svg","../../../src/theme/default/provider-logos/yandex.svg","../../../src/theme/default/provider-logos/x.svg","../../../src/theme/default/provider-logos/index.ts","../../../src/theme/default/components/form/spinner.tsx","../../../src/theme/default/components/card/auth-method-list-item.tsx","../../../src/theme/default/assets/icons/code.svg","../../../src/theme/default/assets/icons/passkey.svg","../../../src/theme/default/assets/icons/password.svg","../../../src/theme/default/assets/icons/webauthn.svg","../../../src/theme/default/assets/icons/totp.svg","../../../src/theme/default/assets/icons/code-asterix.svg","../../../src/theme/default/components/card/list-item.tsx","../../../src/theme/default/components/form/button.tsx","../../../src/theme/default/components/form/checkbox.tsx","../../../src/theme/default/components/ui/checkbox-label.tsx","../../../src/theme/default/components/form/group-container.tsx","../../../src/util/childCounter.ts","../../../src/theme/default/components/form/horizontal-divider.tsx","../../../src/theme/default/components/form/image.tsx","../../../src/theme/default/components/form/input.tsx","../../../src/theme/default/assets/icons/eye-off.svg","../../../src/theme/default/assets/icons/eye.svg","../../../src/theme/default/components/form/label.tsx","../../../src/theme/default/components/form/link-button.tsx","../../../src/theme/default/components/form/pin-code-input.tsx","../../../src/theme/default/components/form/shadcn/otp-input.tsx","../../../src/theme/default/components/form/section.tsx","../../../src/theme/default/components/form/text.tsx","../../../src/theme/default/components/generic/page-header.tsx","../../../src/theme/default/components/ui/user-menu.tsx","../../../src/theme/default/assets/icons/logout.svg","../../../src/theme/default/assets/icons/settings.svg","../../../src/theme/default/utils/user.ts","../../../src/theme/default/components/ui/dropdown-menu.tsx","../../../src/theme/default/components/ui/user-avater.tsx","../../../src/theme/default/assets/icons/user.svg","../../../src/theme/default/components/settings/settings-oidc.tsx","../../../src/theme/default/assets/icons/trash.svg","../../../src/theme/default/components/settings/settings-passkey.tsx","../../../src/theme/default/assets/icons/download.svg","../../../src/theme/default/assets/icons/refresh.svg","../../../src/theme/default/components/settings/settings-recovery-codes.tsx","../../../src/theme/default/components/settings/settings-totp.tsx","../../../src/theme/default/assets/icons/qrcode.svg","../../../src/theme/default/components/settings/settings-webauthn.tsx","../../../src/theme/default/assets/icons/key.svg","../../../src/theme/default/components/card/auth-method-list-container.tsx","../../../src/theme/default/components/form/captcha.tsx","../../../src/theme/default/assets/icons/personal.svg","../../../src/theme/default/assets/icons/message.svg","../../../src/theme/default/assets/icons/phone.svg","../../../src/theme/default/components/form/consent-scope-checkbox.tsx","../../../src/theme/default/components/default-components.tsx","../../../src/theme/default/flows/error.tsx","../../../src/theme/default/flows/login.tsx","../../../src/theme/default/flows/recovery.tsx","../../../src/theme/default/flows/registration.tsx","../../../src/theme/default/flows/settings.tsx","../../../src/theme/default/flows/verification.tsx","../../../src/theme/default/flows/consent.tsx","../../../src/theme/default/utils/oauth2.ts"],"sourcesContent":["import * as React from \"react\";\nconst SvgOryBadgeHorizontal = props => <svg xmlns=\"http://www.w3.org/2000/svg\" width={props?.width ? props.width : props?.size ?? 20} height={props?.height ? props.height : props?.size ?? 20} fill=\"none\" {...props}><path fill=\"#0F172A\" d=\"M18.007 8h-1.71l2.007-3.996L16.296 0h1.711l1.145 2.301L20.327 0H22z\" /><path fill=\"#0F172A\" fillRule=\"evenodd\" d=\"M12.902 4.86a2.47 2.47 0 0 0 1.796-2.365v-.038C14.687 1.097 13.592 0 12.245 0H9.037v1.44l1.208 1.985H9.04V8h1.51V4.949h.633L13.04 8h1.775zm-.876-1.431h.223c.52 0 .943-.427.943-.953a.95.95 0 0 0-.943-.952h-1.39zM4 0a4 4 0 1 0-.001 7.999A4 4 0 0 0 4 0M1.524 4a2.476 2.476 0 1 0 4.952 0 2.476 2.476 0 0 0-4.952 0\" clipRule=\"evenodd\" /></svg>;\nexport default SvgOryBadgeHorizontal;","import * as React from \"react\";\nconst SvgOryBadgeVertical = props => <svg xmlns=\"http://www.w3.org/2000/svg\" width={props?.width ? props.width : props?.size ?? 20} height={props?.height ? props.height : props?.size ?? 20} fill=\"none\" {...props}><path fill=\"#0F172A\" d=\"M8 3.993v1.71L4.004 3.697 0 5.704V3.993l2.301-1.145L0 1.673V0z\" /><path fill=\"#0F172A\" fillRule=\"evenodd\" d=\"M4.86 9.099a2.47 2.47 0 0 0-2.365-1.797h-.038C1.097 7.313 0 8.408 0 9.755v3.208h1.44l1.985-1.208v1.204H8v-1.51H4.949v-.633L8 8.96V7.185zm-1.431.875v-.223a.95.95 0 0 0-.953-.943.95.95 0 0 0-.952.943v1.39zM0 18a4 4 0 1 0 8 0 4 4 0 0 0-8 0m4 2.476a2.476 2.476 0 1 0 0-4.952 2.476 2.476 0 0 0 0 4.952\" clipRule=\"evenodd\" /></svg>;\nexport default SvgOryBadgeVertical;","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport OryLogoHorizontal from \"../../assets/ory-badge-horizontal.svg\"\nimport OryLogoVertical from \"../../assets/ory-badge-vertical.svg\"\n\nexport function Badge() {\n return (\n <div className=\"absolute bg-ory-background-default p-2 font-bold max-sm:bottom-0 max-sm:left-8 max-sm:translate-y-full max-sm:rounded-b-branding sm:right-0 sm:top-8 sm:translate-x-full sm:rounded-r-branding border-ory-border-default border max-sm:py-[7px] sm:pl-[7px]\">\n <OryLogoHorizontal width={22} height={8} className=\"sm:hidden\" />\n <OryLogoVertical width={8} height={22} className=\"max-sm:hidden\" />\n </div>\n )\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { OryCardContentProps } from \"@ory/elements-react\"\n\nexport function DefaultCardContent({ children }: OryCardContentProps) {\n return children\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { FlowType, LoginFlow, UiNodeInputAttributes } from \"@ory/client-fetch\"\nimport {\n ConsentFlow,\n FormState,\n useComponents,\n useOryConfiguration,\n useOryFlow,\n} from \"@ory/elements-react\"\nimport { useIntl } from \"react-intl\"\nimport { toAuthMethodPickerOptions } from \"../../../../components/card/two-step/state-select-method\"\nimport { findScreenSelectionButton } from \"../../../../util/nodes\"\nimport {\n findNode,\n nodesToAuthMethodGroups,\n useNodeGroupsWithVisibleNodes,\n} from \"../../../../util/ui\"\nimport { useClientLogout } from \"../../utils/logout\"\nimport { initFlowUrl, restartFlowUrl } from \"../../utils/url\"\n\nexport function DefaultCardFooter() {\n const oryFlow = useOryFlow()\n switch (oryFlow.flowType) {\n case FlowType.Login:\n return <LoginCardFooter />\n case FlowType.Registration:\n return <RegistrationCardFooter />\n case FlowType.Recovery:\n return <RecoveryCardFooter />\n case FlowType.Verification:\n return <VerificationCardFooter />\n case FlowType.OAuth2Consent:\n return <ConsentCardFooter flow={oryFlow.flow} />\n default:\n return null\n }\n}\n\nfunction shouldShowLogoutButton(\n flow: LoginFlow,\n formState: FormState,\n authMethods: string[],\n) {\n // Always for refresh flows, as we know there is a session\n if (flow.refresh) {\n return true\n }\n\n // In aal2 flows we sometimes show the logout button\n if (flow.requested_aal === \"aal2\") {\n // Always on the \"method selector\" screen\n if (formState.current === \"select_method\") {\n return true\n }\n // On the \"method active\" screen, if it's a code method\n // If the method is any other than code, we want to show a \"Choose another method\" button\n // This is handled below.\n // TODO: refactor this, to not have this logic in two places\n if (formState.current === \"method_active\" && flow.active === \"code\") {\n return true\n }\n // If there are no other methods, we want to show the logout button\n // This is the case when the user only has one method (e.g. code or totp), set up\n // and the user is on the \"method active\" screen\n // In that case there is no \"select_method\" state, so going back to that screen wouldn't work\n if (formState.current === \"method_active\" && authMethods.length === 1) {\n return true\n }\n }\n return false\n}\n\nfunction LoginCardFooter() {\n const { formState, flow, flowType, dispatchFormState } = useOryFlow()\n const config = useOryConfiguration()\n const intl = useIntl()\n\n if (flowType !== FlowType.Login) {\n return null\n }\n\n const authMethods = nodesToAuthMethodGroups(flow.ui.nodes)\n\n let returnTo = config.project.default_redirect_url\n if (flow.return_to) {\n returnTo = flow.return_to\n }\n if (!returnTo) {\n returnTo = restartFlowUrl(\n flow,\n `${config.sdk.url}/self-service/${flowType}/browser`,\n )\n }\n\n if (shouldShowLogoutButton(flow, formState, authMethods)) {\n return <LogoutButton returnTo={returnTo} />\n }\n\n return (\n <>\n {formState.current === \"provide_identifier\" &&\n config.project.registration_enabled && (\n <span className=\"font-normal leading-normal antialiased text-interface-foreground-default-primary\">\n {intl.formatMessage({\n id: \"login.registration-label\",\n defaultMessage: \"No account?\",\n })}{\" \"}\n <a\n className=\"text-button-link-brand-brand transition-colors hover:text-button-link-brand-brand-hover underline\"\n href={initFlowUrl(config.sdk.url, \"registration\", flow)}\n data-testid={\"ory/screen/login/action/register\"}\n >\n {intl.formatMessage({\n id: \"login.registration-button\",\n defaultMessage: \"Sign up\",\n })}\n </a>\n </span>\n )}\n {authMethods.length > 1 && formState.current === \"method_active\" && (\n <span className=\"font-normal leading-normal antialiased text-interface-foreground-default-primary\">\n <button\n className=\"text-button-link-brand-brand transition-colors hover:text-button-link-brand-brand-hover underline\"\n onClick={() => {\n dispatchFormState({\n type: \"action_clear_active_method\",\n })\n }}\n data-testid={\"ory/screen/login/mfa/action/selectMethod\"}\n >\n {intl.formatMessage({\n id: \"login.2fa.method.go-back\",\n })}\n </button>\n </span>\n )}\n {authMethods.length === 1 &&\n authMethods[0] === \"code\" &&\n formState.current === \"method_active\" && (\n <span className=\"font-normal leading-normal antialiased text-interface-foreground-default-primary\">\n <a\n className=\"text-button-link-brand-brand transition-colors hover:text-button-link-brand-brand-hover underline\"\n href={returnTo}\n data-testid={\"ory/screen/login/action/cancel\"}\n >\n {intl.formatMessage({\n id: \"login.2fa.go-back.link\",\n })}\n </a>\n </span>\n )}\n </>\n )\n}\n\ntype LogoutButtonProps = {\n returnTo?: string\n}\n\nfunction LogoutButton({ returnTo }: LogoutButtonProps) {\n const config = useOryConfiguration()\n const intl = useIntl()\n const { logoutFlow: logout, didLoad: didLoadLogout } = useClientLogout(config)\n\n return (\n <span className=\"font-normal leading-normal antialiased text-interface-foreground-default-primary\">\n {intl.formatMessage({\n id: \"login.2fa.go-back\",\n })}{\" \"}\n <a\n className=\"text-button-link-brand-brand transition-colors hover:text-button-link-brand-brand-hover underline\"\n href={logout ? logout?.logout_url : returnTo}\n data-testid={\n // Only add the test-id when the logout link has loaded.\n didLoadLogout ? \"ory/screen/login/action/logout\" : undefined\n }\n >\n {intl.formatMessage({\n id:\n !didLoadLogout || logout\n ? \"login.logout-button\"\n : \"login.2fa.go-back.link\",\n })}\n </a>\n </span>\n )\n}\n\nfunction RegistrationCardFooter() {\n const intl = useIntl()\n const { flow, formState } = useOryFlow()\n const config = useOryConfiguration()\n const visibleGroups = useNodeGroupsWithVisibleNodes(flow.ui.nodes)\n const authMethodBlocks = toAuthMethodPickerOptions(visibleGroups)\n\n const screenSelectionNode = findScreenSelectionButton(flow.ui.nodes)\n switch (formState.current) {\n case \"method_active\":\n if (!screenSelectionNode || Object.entries(authMethodBlocks).length < 2) {\n return null\n }\n\n return (\n <span className=\"font-normal leading-normal antialiased\">\n <a\n className=\"font-medium text-button-link-brand-brand hover:text-button-link-brand-brand-hover\"\n // This works, because it essentially reloads the page.\n // TODO: this should not do a full reload, but rather just update the state.....\n href=\"\"\n data-testid={\"ory/screen/registration/action/selectMethod\"}\n >\n {intl.formatMessage({\n id: \"card.footer.select-another-method\",\n defaultMessage: \"Select another method\",\n })}\n </a>\n </span>\n )\n case \"select_method\":\n default:\n return (\n <span className=\"font-normal leading-normal antialiased text-interface-foreground-default-primary\">\n {intl.formatMessage({\n id: \"registration.login-label\",\n defaultMessage: \"Already have an account?\",\n })}{\" \"}\n <a\n className=\"text-button-link-brand-brand transition-colors hover:text-button-link-brand-brand-hover underline\"\n href={initFlowUrl(config.sdk.url, \"login\", flow)}\n data-testid={\"ory/screen/registration/action/login\"}\n >\n {intl.formatMessage({\n id: \"registration.login-button\",\n defaultMessage: \"Sign in\",\n })}\n </a>\n </span>\n )\n }\n}\n\nfunction RecoveryCardFooter() {\n return null\n}\n\nfunction VerificationCardFooter() {\n return null\n}\n\ntype ConsentFlowProps = {\n flow: ConsentFlow\n}\n\nfunction ConsentCardFooter({ flow }: ConsentFlowProps) {\n const { Node } = useComponents()\n\n const rememberNode = findNode(flow.ui.nodes, {\n group: \"oauth2_consent\",\n node_type: \"input\",\n name: \"remember\",\n })\n\n return (\n <div className=\"flex gap-8 flex-col\">\n <div>\n <p className=\"text-interface-foreground-default-secondary leading-normal font-medium\">\n Make sure you trust {flow.consent_request.client?.client_name}\n </p>\n <p className=\"text-interface-foreground-default-secondary leading-normal\">\n You may be sharing sensitive information with this site or\n application.\n </p>\n </div>\n {rememberNode && (\n <Node.Checkbox\n attributes={rememberNode.attributes}\n node={rememberNode}\n />\n )}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-2\">\n {flow.ui.nodes\n .filter(\n (n) =>\n n.attributes.node_type === \"input\" &&\n n.attributes.type === \"submit\",\n )\n .map((n) => {\n const attributes = n.attributes as UiNodeInputAttributes\n return (\n <Node.Button\n key={attributes.value}\n node={n}\n attributes={attributes}\n />\n )\n })}\n </div>\n <p className=\"text-sm\">\n <span className=\"text-interface-foreground-default-tertiary\">\n Authorizing will redirect to{\" \"}\n </span>\n {flow.consent_request.client?.client_name}\n </p>\n </div>\n )\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { UiNode, UiNodeGroupEnum, UiText } from \"@ory/client-fetch\"\nimport { OryCard, OryCardContent, OryCardFooter } from \"./../\"\nimport { useComponents, useNodeSorter, useOryFlow } from \"../../../context\"\nimport {\n findNode,\n GroupedNodes,\n hasSingleSignOnNodes,\n useFunctionalNodes,\n useNodeGroupsWithVisibleNodes,\n} from \"../../../util/ui\"\nimport { OryForm } from \"../../form/form\"\nimport { OryCardValidationMessages } from \"../../form/messages\"\nimport { Node } from \"../../form/nodes/node\"\nimport { OryFormSocialButtonsForm } from \"../../form/social\"\nimport { handleAfterFormSubmit } from \"./utils\"\nimport { OryCardHeader } from \"../header\"\nimport { useIntl } from \"react-intl\"\nimport { AuthMethodList, AuthMethodOptions } from \"./list-methods\"\n\n/**\n * Converts the visible groups of nodes into a format suitable for the\n * AuthMethodOptions\n *\n * @param visibleGroups - The visible groups of nodes\n */\nexport function toAuthMethodPickerOptions(\n visibleGroups: GroupedNodes,\n): AuthMethodOptions {\n return Object.fromEntries(\n Object.values(UiNodeGroupEnum)\n .filter((group) => visibleGroups[group]?.length)\n .filter(\n (group) =>\n !(\n [\n UiNodeGroupEnum.Oidc,\n UiNodeGroupEnum.Saml,\n UiNodeGroupEnum.Default,\n UiNodeGroupEnum.IdentifierFirst,\n UiNodeGroupEnum.Profile,\n UiNodeGroupEnum.Captcha,\n ] as UiNodeGroupEnum[]\n ).includes(group),\n )\n .map((g) => [g, {}]),\n )\n}\n\nexport function OryTwoStepCardStateSelectMethod() {\n const { Form, Card, Message } = useComponents()\n const { flow, flowType, dispatchFormState } = useOryFlow()\n const { ui } = flow\n const intl = useIntl()\n\n const nodeSorter = useNodeSorter()\n const sortNodes = (a: UiNode, b: UiNode) => nodeSorter(a, b, { flowType })\n\n const visibleGroups = useNodeGroupsWithVisibleNodes(ui.nodes)\n const authMethodBlocks = toAuthMethodPickerOptions(visibleGroups)\n const authMethodAdditionalNodes = useFunctionalNodes(ui.nodes)\n\n // Special case to show the address on code method selector\n if (UiNodeGroupEnum.Code in authMethodBlocks) {\n let identifier = findNode(ui.nodes, {\n group: \"identifier_first\",\n node_type: \"input\",\n name: \"identifier\",\n })?.attributes?.value\n identifier ||= findNode(ui.nodes, {\n group: \"code\",\n node_type: \"input\",\n name: \"address\",\n })?.attributes?.value\n if (identifier) {\n authMethodBlocks[UiNodeGroupEnum.Code] = {\n title: {\n id: \"identities.messages.1010023\",\n values: { address: identifier },\n },\n }\n }\n }\n\n // This is defined in Ory Kratos as well.\n const noMethods: UiText = {\n id: 5000002,\n text: intl.formatMessage({\n id: `identities.messages.5000002`,\n defaultMessage:\n \"No authentication methods are available for this request. Please contact the site or app owner.\",\n }),\n type: \"error\",\n }\n\n return (\n <OryCard>\n <OryCardHeader />\n <OryCardContent>\n <OryCardValidationMessages />\n <OryFormSocialButtonsForm />\n {Object.entries(authMethodBlocks).length > 0 ? (\n <OryForm\n data-testid={`ory/form/methods/local`}\n onAfterSubmit={handleAfterFormSubmit(dispatchFormState)}\n >\n <Form.Group>\n <Card.Divider />\n <AuthMethodList\n options={authMethodBlocks}\n setSelectedGroup={(group) =>\n dispatchFormState({\n type: \"action_select_method\",\n method: group,\n })\n }\n />\n {authMethodAdditionalNodes.sort(sortNodes).map((node, k) => (\n <Node node={node} key={k} />\n ))}\n </Form.Group>\n </OryForm>\n ) : (\n !hasSingleSignOnNodes(ui.nodes) && (\n <div data-testid={`ory/form/methods/local`}>\n <Message.Root>\n <Message.Content key={noMethods.id} message={noMethods} />\n </Message.Root>\n </div>\n )\n )}\n </OryCardContent>\n <OryCardFooter />\n </OryCard>\n )\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n isUiNodeInputAttributes,\n UiNode,\n UiNodeGroupEnum,\n} from \"@ory/client-fetch\"\nimport { PropsWithChildren, createContext, useContext } from \"react\"\nimport { OryFlowComponents } from \"../components\"\n\ntype ComponentContextValue = {\n components: OryFlowComponents\n nodeSorter: (a: UiNode, b: UiNode, ctx: { flowType: string }) => number\n groupSorter: (a: UiNodeGroupEnum, b: UiNodeGroupEnum) => number\n}\n\nconst ComponentContext = createContext<ComponentContextValue>({\n components: null!, // fine because we throw an error if it's not provided\n nodeSorter: () => 0,\n groupSorter: () => 0,\n})\n\nexport function useComponents() {\n const ctx = useContext(ComponentContext)\n if (!ctx) {\n throw new Error(\"useComponents must be used within a ComponentProvider\")\n }\n return ctx.components\n}\n\nexport function useNodeSorter() {\n const ctx = useContext(ComponentContext)\n if (!ctx) {\n throw new Error(\"useNodeSorter must be used within a ComponentProvider\")\n }\n return ctx.nodeSorter\n}\n\nexport function useGroupSorter() {\n const ctx = useContext(ComponentContext)\n if (!ctx) {\n throw new Error(\"useGroupSorter must be used within a ComponentProvider\")\n }\n return ctx.groupSorter\n}\n\nconst defaultNodeOrder = [\n \"oidc\",\n \"saml\",\n \"identifier_first\",\n \"default\",\n \"profile\",\n \"password\",\n // CAPTCHA is below password because otherwise the password input field\n // would be above the captcha. Somehow, we sort the password sign up button somewhere else to be always at the bottom.\n \"captcha\",\n \"passkey\",\n \"code\",\n \"webauthn\",\n]\n\nexport function defaultNodeSorter(\n a: UiNode,\n b: UiNode,\n // ctx: { flowType: string },\n): number {\n // First handle the special case: captcha vs submit button\n const aIsCaptcha = a.group === \"captcha\"\n const bIsCaptcha = b.group === \"captcha\"\n const aIsSubmit =\n isUiNodeInputAttributes(a.attributes) && a.attributes.type === \"submit\"\n const bIsSubmit =\n isUiNodeInputAttributes(b.attributes) && b.attributes.type === \"submit\"\n\n // If comparing captcha and submit, always put captcha first\n if (aIsCaptcha && bIsSubmit) {\n return -1 // a (captcha) comes before b (submit)\n }\n if (bIsCaptcha && aIsSubmit) {\n return 1 // b (captcha) comes before a (submit)\n }\n\n // For all other cases, use the standard group ordering\n const aGroupWeight = defaultNodeOrder.indexOf(a.group) ?? 999\n const bGroupWeight = defaultNodeOrder.indexOf(b.group) ?? 999\n\n return aGroupWeight - bGroupWeight\n}\n\nconst defaultGroupOrder: UiNodeGroupEnum[] = [\n UiNodeGroupEnum.Default,\n UiNodeGroupEnum.Profile,\n UiNodeGroupEnum.Password,\n UiNodeGroupEnum.Oidc,\n UiNodeGroupEnum.Code,\n UiNodeGroupEnum.LookupSecret,\n UiNodeGroupEnum.Passkey,\n UiNodeGroupEnum.Webauthn,\n UiNodeGroupEnum.Totp,\n]\n\nfunction defaultGroupSorter(a: UiNodeGroupEnum, b: UiNodeGroupEnum): number {\n const aGroupWeight = defaultGroupOrder.indexOf(a) ?? 999\n const bGroupWeight = defaultGroupOrder.indexOf(b) ?? 999\n\n return aGroupWeight - bGroupWeight\n}\n\ntype ComponentProviderProps = {\n components: OryFlowComponents\n nodeSorter?: (a: UiNode, b: UiNode, ctx: { flowType: string }) => number\n groupSorter?: (a: UiNodeGroupEnum, b: UiNodeGroupEnum) => number\n}\n\nexport function OryComponentProvider({\n children,\n components,\n nodeSorter = defaultNodeSorter,\n groupSorter = defaultGroupSorter,\n}: PropsWithChildren<ComponentProviderProps>) {\n return (\n <ComponentContext.Provider\n value={{\n components,\n nodeSorter,\n groupSorter,\n }}\n >\n {children}\n </ComponentContext.Provider>\n )\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n Dispatch,\n PropsWithChildren,\n createContext,\n useContext,\n useState,\n} from \"react\"\nimport { OryFlowContainer } from \"../util/flowContainer\"\nimport { FormState, FormStateAction, useFormStateReducer } from \"./form-state\"\n\n/**\n * Returns an object that contains the current flow and the flow type, as well as the configuration.\n *\n * @returns The current flow container\n */\nexport function useOryFlow() {\n const ctx = useContext(OryFlowContext)\n if (!ctx) {\n throw new Error(\"useOryFlow must be used within a OryFlowProvider\")\n }\n\n return ctx\n}\n\n/**\n * Function to set the flow container.\n */\nexport type FlowContainerSetter = Dispatch<OryFlowContainer>\n\n/**\n * The return value of the OryFlowContext.\n */\nexport type FlowContextValue = OryFlowContainer & {\n /**\n * Function to set the flow container.\n */\n setFlowContainer: FlowContainerSetter\n\n /**\n * The current form state.\n * @see FormState\n */\n formState: FormState\n\n /**\n * Dispatch function to update the form state.\n */\n dispatchFormState: Dispatch<FormStateAction>\n}\n\n// This is fine, because we don't export the context itself and guard from it being null in useOryFlow\nconst OryFlowContext = createContext<FlowContextValue>(null!)\n\nexport type OryFlowProviderProps = PropsWithChildren<OryFlowContainer>\n\nexport function OryFlowProvider({\n children,\n ...container\n}: OryFlowProviderProps) {\n const [flowContainer, setFlowContainer] = useState(container)\n const [formState, dispatchFormState] = useFormStateReducer(container)\n\n return (\n <OryFlowContext.Provider\n value={\n {\n ...flowContainer,\n setFlowContainer: (flowContainer) => {\n setFlowContainer(flowContainer)\n dispatchFormState({\n type: \"action_flow_update\",\n flow: flowContainer,\n })\n },\n formState,\n dispatchFormState,\n } as FlowContextValue\n }\n >\n {children}\n </OryFlowContext.Provider>\n )\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { FlowType, UiNode, UiNodeGroupEnum } from \"@ory/client-fetch\"\nimport { useReducer, useState } from \"react\"\nimport { isChoosingMethod } from \"../components/card/two-step/utils\"\nimport { OryFlowContainer } from \"../util\"\nimport { nodesToAuthMethodGroups } from \"../util/ui\"\n\nexport type FormStateSelectMethod = { current: \"select_method\" }\nexport type FormStateProvideIdentifier = { current: \"provide_identifier\" }\nexport type FormStateMethodActive = {\n current: \"method_active\"\n method: UiNodeGroupEnum\n}\n\nexport type FormState =\n | FormStateSelectMethod\n | FormStateProvideIdentifier\n | FormStateMethodActive\n | { current: \"success_screen\" }\n | { current: \"settings\" }\n\nexport type FormStateAction =\n | {\n type: \"action_flow_update\"\n flow: OryFlowContainer\n }\n | {\n type: \"action_select_method\"\n method: UiNodeGroupEnum\n }\n | {\n type: \"action_clear_active_method\"\n }\n\nfunction findMethodWithMessage(nodes?: UiNode[]) {\n return nodes\n ?.filter((n) => ![\"default\", \"identifier_first\"].includes(n.group))\n ?.find((node) => node.messages?.length > 0)\n}\n\nfunction parseStateFromFlow(flow: OryFlowContainer): FormState {\n switch (flow.flowType) {\n case FlowType.Registration:\n case FlowType.Login: {\n const methodWithMessage = findMethodWithMessage(flow.flow.ui.nodes)\n if (flow.flow.active == \"link_recovery\") {\n return { current: \"method_active\", method: \"link\" }\n } else if (flow.flow.active == \"code_recovery\") {\n return { current: \"method_active\", method: \"code\" }\n } else if (methodWithMessage) {\n return { current: \"method_active\", method: methodWithMessage.group }\n } else if (flow.flow.ui.messages?.some((m) => m.id === 1010016)) {\n // Account linking edge case\n return { current: \"select_method\" }\n } else if (\n flow.flow.active &&\n ![\"default\", \"identifier_first\"].includes(flow.flow.active)\n ) {\n return { current: \"method_active\", method: flow.flow.active }\n } else if (isChoosingMethod(flow)) {\n // Login has a special case where we only have one method. Here, we\n // do not want to display the chooser.\n const authMethods = nodesToAuthMethodGroups(flow.flow.ui.nodes)\n if (authMethods.length === 1 && authMethods[0] !== \"code\") {\n // TODO: https://github.com/ory/kratos/issues/4271 - once this is fixed in Kratos, we can remove the check for \"code\"\n return { current: \"method_active\", method: authMethods[0] }\n }\n return { current: \"select_method\" }\n }\n return { current: \"provide_identifier\" }\n }\n case FlowType.Recovery:\n case FlowType.Verification:\n // The API does not provide types for the active field of the recovery flow\n // TODO: Add types for the recovery flow in Kratos\n if (flow.flow.active === \"code\" || flow.flow.active === \"link\") {\n if (flow.flow.state === \"choose_method\") {\n return { current: \"provide_identifier\" }\n }\n return { current: \"method_active\", method: flow.flow.active }\n }\n break\n case FlowType.Settings:\n return { current: \"settings\" }\n case FlowType.OAuth2Consent:\n return { current: \"method_active\", method: \"oauth2_consent\" }\n }\n console.warn(\n `[Ory/Elements React] Encountered an unknown form state on ${flow.flowType} flow with ID ${flow.flow.id}`,\n )\n throw new Error(\"Unknown form state\")\n}\n\nexport function useFormStateReducer(flow: OryFlowContainer) {\n const action = parseStateFromFlow(flow)\n const [selectedMethod, setSelectedMethod] = useState<\n UiNodeGroupEnum | undefined\n >()\n\n const formStateReducer = (\n state: FormState,\n action: FormStateAction,\n ): FormState => {\n switch (action.type) {\n case \"action_flow_update\": {\n if (selectedMethod) {\n return { current: \"method_active\", method: selectedMethod }\n }\n return parseStateFromFlow(action.flow)\n }\n case \"action_select_method\": {\n setSelectedMethod(action.method)\n return { current: \"method_active\", method: action.method }\n }\n case \"action_clear_active_method\": {\n return {\n current: \"select_method\",\n }\n }\n }\n return state\n }\n\n return useReducer(formStateReducer, action)\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { FlowType, UiNode, UiNodeGroupEnum } from \"@ory/client-fetch\"\nimport {\n LoginFlowContainer,\n RegistrationFlowContainer,\n} from \"../../../util/flowContainer\"\nimport { isGroupImmediateSubmit } from \"../../../theme/default/utils/form\"\nimport { GroupedNodes, isUiNodeGroupEnum } from \"../../../util/ui\"\nimport { Dispatch } from \"react\"\nimport { FormStateAction } from \"@ory/elements-react\"\n\nexport function isChoosingMethod(\n flow: LoginFlowContainer | RegistrationFlowContainer,\n): boolean {\n return (\n flow.flow.ui.nodes.some(\n (node) =>\n \"name\" in node.attributes &&\n node.attributes.name === \"screen\" &&\n \"value\" in node.attributes &&\n node.attributes.value === \"previous\",\n ) ||\n flow.flow.ui.nodes.some(\n (node) =>\n node.group === UiNodeGroupEnum.IdentifierFirst &&\n \"name\" in node.attributes &&\n node.attributes.name === \"identifier\" &&\n node.attributes.type === \"hidden\",\n ) ||\n (flow.flowType === FlowType.Login && flow.flow.requested_aal === \"aal2\")\n )\n}\n\nexport function getFinalNodes(\n uniqueGroups: GroupedNodes,\n selectedGroup: UiNodeGroupEnum | undefined,\n): UiNode[] {\n const selectedNodes: UiNode[] = selectedGroup\n ? (uniqueGroups[selectedGroup] ?? [])\n : []\n\n return [\n ...(uniqueGroups?.identifier_first ?? []),\n ...(uniqueGroups?.default ?? []),\n ...(uniqueGroups?.captcha ?? []),\n ]\n .flat()\n .filter(\n (node) => \"type\" in node.attributes && node.attributes.type === \"hidden\",\n )\n .concat(selectedNodes)\n}\n\nexport const handleAfterFormSubmit =\n (dispatchFormState: Dispatch<FormStateAction>) => (method: unknown) => {\n if (typeof method !== \"string\" || !isUiNodeGroupEnum(method)) {\n return\n }\n\n if (isGroupImmediateSubmit(method)) {\n dispatchFormState({\n type: \"action_select_method\",\n method: method,\n })\n }\n }\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nexport function isGroupImmediateSubmit(group: string) {\n // TODO: Other methods might also benefit from this.\n return group === \"code\"\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n isUiNodeInputAttributes,\n isUiNodeScriptAttributes,\n UiNode,\n} from \"@ory/client-fetch\"\n\nimport type {\n UiNodeAttributes,\n UiNodeInputAttributesOnclickTriggerEnum,\n UiNodeInputAttributesOnloadTriggerEnum,\n} from \"@ory/client-fetch\"\nimport { UiNodeGroupEnum } from \"@ory/client-fetch\"\nimport { useMemo } from \"react\"\nimport { useGroupSorter } from \"../../context/component\"\n\nexport function triggerToWindowCall(\n trigger:\n | UiNodeInputAttributesOnclickTriggerEnum\n | UiNodeInputAttributesOnloadTriggerEnum\n | undefined,\n) {\n if (!trigger) {\n return\n }\n\n const fn = triggerToFunction(trigger)\n if (fn) {\n fn()\n return\n }\n\n // Retry every 100ms for 10 seconds\n let i = 0\n const ms = 100\n const interval = setInterval(() => {\n i++\n if (i > 100) {\n clearInterval(interval)\n throw new Error(\n \"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.\",\n )\n }\n\n const fn = triggerToFunction(trigger)\n if (fn) {\n clearInterval(interval)\n return fn()\n }\n }, ms)\n return\n}\n\nfunction triggerToFunction(\n trigger:\n | UiNodeInputAttributesOnclickTriggerEnum\n | UiNodeInputAttributesOnloadTriggerEnum,\n) {\n if (typeof window === \"undefined\") {\n console.debug(\n \"The Ory SDK is missing a required function: window is undefined.\",\n )\n return undefined\n }\n\n const typedWindow = window as { [key: string]: any } // eslint-disable-line @typescript-eslint/no-explicit-any\n if (!(trigger in typedWindow) || !typedWindow[trigger]) {\n console.debug(`The Ory SDK is missing a required function: ${trigger}.`)\n return undefined\n }\n const triggerFn = typedWindow[trigger]\n if (typeof triggerFn !== \"function\") {\n console.debug(\n `The Ory SDK is missing a required function: ${trigger}. It is not a function.`,\n )\n return undefined\n }\n return triggerFn as () => void\n}\n\ntype Entries<T> = {\n [K in keyof T]: [K, T[K]]\n}[keyof T][]\n\n/**\n * Returns a list of auth methods from a list of nodes. For example,\n * if Password and Passkey are present, it will return [password, passkey].\n *\n * Please note that OIDC is not considered an auth method because it is\n * usually shown as a separate auth method\n *\n * This method the default, identifier_first, and profile groups.\n *\n * @param nodes - The nodes to extract the auth methods from\n * @param excludeAuthMethods - A list of auth methods to exclude\n */\nexport function nodesToAuthMethodGroups(\n nodes: Array<UiNode>,\n excludeAuthMethods = [],\n): UiNodeGroupEnum[] {\n const groups: Partial<Record<UiNodeGroupEnum, UiNode[]>> = {}\n\n for (const node of nodes) {\n if (node.type === \"script\") {\n // We always render all scripts, because the scripts for passkeys are part of the webauthn group,\n // which leads to this hook returning a webauthn group on passkey flows (which it should not - webauthn is the \"legacy\" passkey implementation).\n continue\n }\n const groupNodes = groups[node.group] ?? []\n groupNodes.push(node)\n groups[node.group] = groupNodes\n }\n\n return Object.values(UiNodeGroupEnum)\n .filter((group) => groups[group]?.length)\n .filter(\n (group) =>\n !(\n [\n UiNodeGroupEnum.Default,\n UiNodeGroupEnum.IdentifierFirst,\n UiNodeGroupEnum.Profile,\n UiNodeGroupEnum.Captcha,\n ...excludeAuthMethods,\n ] as UiNodeGroupEnum[]\n ).includes(group),\n )\n}\n\n/**\n * Groups nodes by their group and returns an object with the groups and entries.\n *\n * @deprecated use useNodeGroupsWithVisibleNodes instead\n * @param nodes - The nodes to group\n * @param opts - The options to use\n */\nexport function useNodesGroups(\n nodes: UiNode[],\n { omit }: { omit?: Array<\"script\" | \"input_hidden\"> } = {},\n) {\n const groupSorter = useGroupSorter()\n\n const groups = useMemo(() => {\n const groups: Partial<Record<UiNodeGroupEnum, UiNode[]>> = {}\n const groupRetained: Partial<Record<UiNodeGroupEnum, number>> = {}\n\n for (const node of nodes) {\n const groupNodes = groups[node.group] ?? []\n groupNodes.push(node)\n groups[node.group] = groupNodes\n\n if (\n omit?.includes(\"script\") &&\n isUiNodeScriptAttributes(node.attributes)\n ) {\n continue\n }\n\n if (\n omit?.includes(\"input_hidden\") &&\n isUiNodeInputAttributes(node.attributes) &&\n node.attributes.type === \"hidden\"\n ) {\n continue\n }\n\n groupRetained[node.group] = (groupRetained[node.group] ?? 0) + 1\n }\n\n const finalGroups: Partial<Record<UiNodeGroupEnum, UiNode[]>> = {}\n for (const [group, count] of Object.entries(groupRetained)) {\n if (count > 0) {\n finalGroups[group as UiNodeGroupEnum] = groups[group as UiNodeGroupEnum]\n }\n }\n\n return finalGroups\n }, [nodes, omit])\n\n const entries = useMemo(\n () =>\n (\n Object.entries(groups) as Entries<Record<UiNodeGroupEnum, UiNode[]>>\n ).sort(([a], [b]) => groupSorter(a, b)),\n [groups, groupSorter],\n )\n\n return {\n groups,\n entries,\n }\n}\n\n/**\n * Find a node\n * @param nodes - The list of nodes to search\n * @param opt - The matching options\n * @returns The first matching node\n */\nexport const findNode = <T extends UiNodeAttributes[\"node_type\"]>(\n nodes: UiNode[],\n opt: {\n node_type: T\n group: UiNodeGroupEnum | RegExp\n name?: string | RegExp\n },\n) =>\n nodes.find((n) => {\n return (\n n.attributes.node_type === opt.node_type &&\n (opt.group instanceof RegExp\n ? n.group.match(opt.group)\n : n.group === opt.group) &&\n (opt.name && n.attributes.node_type === \"input\"\n ? opt.name instanceof RegExp\n ? n.attributes.name.match(opt.name)\n : n.attributes.name === opt.name\n : !opt.name)\n )\n }) as\n | (UiNode & { attributes: UiNodeAttributes & { node_type: T } })\n | undefined\n\n/**\n * Returns functional nodes not related to credentials (e.g. password node) but\n * nodes belonging profile information, identifier first nodes, captcha, or default\n * nodes (e.g. csrf_token).\n *\n * @param nodes - Array of nodes to filter on.\n */\nexport function useFunctionalNodes(nodes: UiNode[]) {\n return nodes.filter(({ group }) =>\n (\n [\n UiNodeGroupEnum.Default,\n UiNodeGroupEnum.IdentifierFirst,\n UiNodeGroupEnum.Profile,\n UiNodeGroupEnum.Captcha,\n ] as UiNodeGroupEnum[]\n ).includes(group),\n )\n}\n\n/**\n * Type guard for UiNodeGroupEnum\n *\n * @param method - The string to type guard\n */\nexport function isUiNodeGroupEnum(method: string): method is UiNodeGroupEnum {\n // @ts-expect-error it's a string array, but typescript thinks the argument must be validated stricter\n return Object.values(UiNodeGroupEnum).includes(method)\n}\n\n/**\n * Returns true if the node is of group saml or oidc.\n *\n * @param node - The node\n */\nfunction isSingleSignOnNode(node: UiNode): boolean {\n return (\n node.group === UiNodeGroupEnum.Oidc || node.group === UiNodeGroupEnum.Saml\n )\n}\n\n/**\n * Returns true if the node group contains oidc or saml nodes.\n *\n * @param nodes - Array of nodes to search in.\n */\nexport function hasSingleSignOnNodes(nodes: UiNode[]) {\n return nodes.some(isSingleSignOnNode)\n}\n\n/** Returns all nodes that are not single sign on nodes (saml, oidc).\n *\n * @param nodes - Array of nodes to filter.\n */\nexport function withoutSingleSignOnNodes(nodes: UiNode[]) {\n return nodes.filter((node: UiNode) => !isSingleSignOnNode(node))\n}\n\n/**\n * Returns true if the node is visible.\n *\n * @param node - The node to check.\n */\nexport function isNodeVisible(node: UiNode) {\n if (isUiNodeScriptAttributes(node.attributes)) {\n return false\n } else if (isUiNodeInputAttributes(node.attributes)) {\n if (node.attributes.type === \"hidden\") {\n return false\n }\n }\n return true\n}\n\nexport type GroupedNodes = Partial<Record<UiNodeGroupEnum, UiNode[]>>\n\n/**\n * Returns a record which have at least one visible or interactive element (button,\n * text field, image).\n *\n * Groups which have only hidden or otherwise non-interactive elements (e.g. scripts or\n * hidden input fields) are omitted from the result.\n *\n * @param nodes - Array of nodes to filter on.\n * @returns Record of groups with at least one visible element and their nodes.\n */\nexport function useNodeGroupsWithVisibleNodes(nodes: UiNode[]): GroupedNodes {\n return useMemo(() => {\n const groups: Partial<Record<UiNodeGroupEnum, UiNode[]>> = {}\n const groupRetained: Partial<Record<UiNodeGroupEnum, number>> = {}\n\n for (const node of nodes) {\n const groupNodes = groups[node.group] ?? []\n const groupCount = groupRetained[node.group] ?? 0\n\n groupNodes.push(node)\n groups[node.group] = groupNodes\n if (!isNodeVisible(node)) {\n continue\n }\n\n groupRetained[node.group] = groupCount + 1\n }\n\n const finalGroups: Partial<Record<UiNodeGroupEnum, UiNode[]>> = {}\n for (const [group, count] of Object.entries(groupRetained)) {\n if (count > 0) {\n finalGroups[group as UiNodeGroupEnum] = groups[group as UiNodeGroupEnum]\n }\n }\n\n return finalGroups\n }, [nodes])\n}\n","// Copyright © 2025 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { createContext, PropsWithChildren, useContext, useRef } from \"react\"\nimport { isProduction } from \"../client/config\"\nimport { OryClientConfiguration } from \"../util\"\nimport { frontendClient } from \"../util/client\"\nimport {\n AccountExperienceConfiguration,\n ConfigurationParameters,\n FrontendApi,\n} from \"@ory/client-fetch\"\n\nexport type OryElementsConfiguration = {\n sdk: OrySDK\n project: AccountExperienceConfiguration\n}\n\nconst defaultProject: AccountExperienceConfiguration = {\n name: \"Ory\",\n registration_enabled: true,\n verification_enabled: true,\n recovery_enabled: true,\n recovery_ui_url: \"/ui/recovery\",\n registration_ui_url: \"/ui/registration\",\n verification_ui_url: \"/ui/verification\",\n login_ui_url: \"/ui/login\",\n settings_ui_url: \"/ui/settings\",\n default_redirect_url: \"/ui/welcome\",\n error_ui_url: \"/ui/error\",\n default_locale: \"en\",\n locale_behavior: \"force_default\",\n}\n\nexport function useOryConfiguration(): OryElementsConfiguration {\n const configCtx = useContext(OryConfigurationContext)\n return {\n sdk: {\n ...configCtx.sdk,\n frontend: frontendClient(configCtx.sdk.url, configCtx.sdk.options),\n },\n project: {\n ...configCtx.project,\n },\n }\n}\n\nexport type OrySDK = SDKConfig & {\n /**\n * The frontend client for the Ory SDK.\n * This client is used to interact with the Ory SDK and should be used to make API calls.\n */\n frontend: FrontendApi\n}\n\ntype SDKConfig = {\n /**\n * The URL of the Ory SDK.\n * This URL is used to connect to the Ory SDK and should be set to the base URL of your Ory instance.\n */\n url: string\n options?: Partial<ConfigurationParameters>\n}\n\ntype OryElementsConfigContextType = {\n sdk: SDKConfig\n project: AccountExperienceConfiguration\n}\n\nconst OryConfigurationContext = createContext<OryElementsConfigContextType>({\n sdk: computeSdkConfig({}),\n project: defaultProject,\n})\n\nexport function OryConfigurationProvider({\n children,\n sdk: initialConfig,\n project,\n}: PropsWithChildren<{\n sdk?: Partial<OryClientConfiguration[\"sdk\"]>\n project?: Partial<AccountExperienceConfiguration>\n}>)