diff --git a/package-lock.json b/package-lock.json index 0e49d72f..16f929b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26888,16 +26888,15 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -34757,7 +34756,7 @@ "version": "1.0.0-next.19", "license": "Apache License 2.0", "dependencies": { - "@ory/client-fetch": "^1.15.12", + "@ory/client-fetch": "~1.16.1", "@radix-ui/react-dropdown-menu": "2.1.2", "class-variance-authority": "0.7.0", "clsx": "2.1.1", @@ -35251,10 +35250,9 @@ } }, "packages/elements-react/node_modules/@ory/client-fetch": { - "version": "1.15.12", - "resolved": "https://registry.npmjs.org/@ory/client-fetch/-/client-fetch-1.15.12.tgz", - "integrity": "sha512-oY6v1ywYfFH6DtgZG/bWLkn0elBUDkxBk9ToaaX3gF5WmwN2I37JKElteZGV7GchzA9uCUJ5BmWgHvRnv3hj3A==", - "license": "Apache-2.0" + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@ory/client-fetch/-/client-fetch-1.16.1.tgz", + "integrity": "sha512-xQC9QnAu0EbDTD0VT74RJIszAFQ4cvJyXvN6nktsXebwFaotgoBI95b75S2+wCwvXYzqiY5pefZqrksfxuowjQ==" }, "packages/elements-react/node_modules/@types/react": { "version": "18.3.11", @@ -35622,7 +35620,7 @@ "version": "1.0.0-next.0", "license": "Apache License 2.0", "dependencies": { - "@ory/client-fetch": "^1.15.6", + "@ory/client-fetch": "~1.16.0", "cookie": "^1.0.1", "psl": "^1.15.0", "set-cookie-parser": "^2.7.1" @@ -36014,10 +36012,9 @@ } }, "packages/nextjs/node_modules/@ory/client-fetch": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@ory/client-fetch/-/client-fetch-1.15.10.tgz", - "integrity": "sha512-PKKi9XjjdCah0pVz/4WB3G41ttA4tuXuOLGrdAJR63iFKTiRtINocVCdXzmI9GzYI/kjhjKxwbRTs1pXCY6H7A==", - "license": "Apache-2.0" + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@ory/client-fetch/-/client-fetch-1.16.0.tgz", + "integrity": "sha512-jjSCXffX03MMKClsq0YoeoBqF+ATk1LEd5sQjOLVG9PuzIzuxKjRPYF8tKCLe0ca8GbgmSySqbEcE7bq7LS4fg==" }, "packages/nextjs/node_modules/cookie": { "version": "1.0.1", diff --git a/packages/elements-react/package.json b/packages/elements-react/package.json index bb37688e..49157459 100644 --- a/packages/elements-react/package.json +++ b/packages/elements-react/package.json @@ -36,7 +36,7 @@ "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "dependencies": { - "@ory/client-fetch": "^1.15.12", + "@ory/client-fetch": "~1.16.1", "@radix-ui/react-dropdown-menu": "2.1.2", "class-variance-authority": "0.7.0", "clsx": "2.1.1", diff --git a/packages/elements-react/src/components/form/form-resolver.test.tsx b/packages/elements-react/src/components/form/form-resolver.test.tsx index bf3d88b9..a9ad2aac 100644 --- a/packages/elements-react/src/components/form/form-resolver.test.tsx +++ b/packages/elements-react/src/components/form/form-resolver.test.tsx @@ -39,7 +39,20 @@ const wrapper = ({ children }: PropsWithChildren) => ( flow={ { active: "code", - ui: { nodes: [], action: "", method: "" }, + ui: { + nodes: [ + { + group: "code", + attributes: { + node_type: "input", + name: "code", + type: "text", + }, + }, + ], + action: "", + method: "", + }, } as unknown as LoginFlow // Fine, we're just testing the resolver } flowType={FlowType.Login} diff --git a/packages/elements-react/src/theme/default/components/card/footer.tsx b/packages/elements-react/src/theme/default/components/card/footer.tsx index 86afbb62..6f0db2a4 100644 --- a/packages/elements-react/src/theme/default/components/card/footer.tsx +++ b/packages/elements-react/src/theme/default/components/card/footer.tsx @@ -5,7 +5,7 @@ import { FlowType, UiNode, UiNodeInputAttributes } from "@ory/client-fetch" import { useOryFlow } from "@ory/elements-react" import { useFormContext } from "react-hook-form" import { useIntl } from "react-intl" -import { initFlowUrl, restartFlowUrl } from "../../utils/url" +import { initFlowUrl } from "../../utils/url" export function DefaultCardFooter() { const { flowType } = useOryFlow() diff --git a/packages/elements-react/src/theme/default/flows/error.tsx b/packages/elements-react/src/theme/default/flows/error.tsx index d450bd24..06fbc935 100644 --- a/packages/elements-react/src/theme/default/flows/error.tsx +++ b/packages/elements-react/src/theme/default/flows/error.tsx @@ -19,5 +19,9 @@ export function Error({ error, children, }: PropsWithChildren) { - return
{JSON.stringify(error) || children}
+ return ( +
+ {JSON.stringify(error) || children} +
+ ) } diff --git a/packages/elements-react/src/theme/default/utils/__tests__/url.spec.ts b/packages/elements-react/src/theme/default/utils/__tests__/url.spec.ts index 61492ec0..ef0fecd9 100644 --- a/packages/elements-react/src/theme/default/utils/__tests__/url.spec.ts +++ b/packages/elements-react/src/theme/default/utils/__tests__/url.spec.ts @@ -42,6 +42,7 @@ describe("url utils", () => { const flow = {} // Not sure how to mock this. + // eslint-disable-next-line @typescript-eslint/no-unused-expressions ;(window.location.href = "http://example.com?return_to=http://example.com/return"), expect(initFlowUrl(sdkUrl, flowType, flow)).toBe( diff --git a/packages/elements-react/src/util/i18n/index.ts b/packages/elements-react/src/util/i18n/index.ts index fa0da887..f04ecf4f 100644 --- a/packages/elements-react/src/util/i18n/index.ts +++ b/packages/elements-react/src/util/i18n/index.ts @@ -69,16 +69,16 @@ export const uiTextToFormattedMessage = ( new Date(value), new Date(), ), - [key + "_since_minutes"]: Math.abs( + [key + "_since_minutes"]: Math.ceil( (value - new Date().getTime() / 1000) / 60, - ).toFixed(2), + ).toFixed(0), [key + "_until"]: intl.formatDateTimeRange( new Date(), new Date(value), ), - [key + "_until_minutes"]: Math.abs( + [key + "_until_minutes"]: Math.ceil( (new Date().getTime() / 1000 - value) / 60, - ).toFixed(2), + ).toFixed(0), } } } diff --git a/packages/elements-react/src/util/internal.ts b/packages/elements-react/src/util/internal.ts new file mode 100644 index 00000000..5a033165 --- /dev/null +++ b/packages/elements-react/src/util/internal.ts @@ -0,0 +1,8 @@ +// Copyright © 2025 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +export function replaceWindowFlowId(flow: string) { + const url = new URL(window.location.href) + url.searchParams.set("flow", flow) + window.location.href = url.toString() +} diff --git a/packages/elements-react/src/util/onSubmitLogin.ts b/packages/elements-react/src/util/onSubmitLogin.ts index 3f97fc48..da35be27 100644 --- a/packages/elements-react/src/util/onSubmitLogin.ts +++ b/packages/elements-react/src/util/onSubmitLogin.ts @@ -11,6 +11,7 @@ import { import { OnSubmitHandlerProps } from "./submitHandler" import { OryFlowContainer } from "./flowContainer" import { frontendClient } from "./client" +import { replaceWindowFlowId } from "./internal" /** * Use this method to submit a login flow. This method is used in the `onSubmit` handler of the login form. @@ -49,8 +50,12 @@ export async function onSubmitLogin( }) .catch( handleFlowError({ - onRestartFlow: () => { - onRedirect(loginUrl(config), true) + onRestartFlow: (useFlowId?: string) => { + if (useFlowId) { + replaceWindowFlowId(useFlowId) + } else { + onRedirect(loginUrl(config), true) + } }, onValidationError: (body: LoginFlow) => { setFlowContainer({ diff --git a/packages/elements-react/src/util/onSubmitRecovery.ts b/packages/elements-react/src/util/onSubmitRecovery.ts index 61459626..6b915419 100644 --- a/packages/elements-react/src/util/onSubmitRecovery.ts +++ b/packages/elements-react/src/util/onSubmitRecovery.ts @@ -17,6 +17,7 @@ import { frontendClient } from "./client" import { OryClientConfiguration } from "./clientConfiguration" import { OryFlowContainer } from "./flowContainer" import { OnSubmitHandlerProps } from "./submitHandler" +import { replaceWindowFlowId } from "./internal" /** * Use this method to submit a recovery flow. This method is used in the `onSubmit` handler of the recovery form. @@ -24,7 +25,7 @@ import { OnSubmitHandlerProps } from "./submitHandler" * @param config - The configuration object. * @param flow - The flow object. * @param setFlowContainer - This method is used to update the flow container when a validation error occurs, for example. - * @param body- The form values to submit. + * @param body - The form values to submit. * @param onRedirect - This method is used to redirect the user to a different page. */ export async function onSubmitRecovery( @@ -66,8 +67,12 @@ export async function onSubmitRecovery( }) .catch( handleFlowError({ - onRestartFlow: () => { - onRedirect(recoveryUrl(config), true) + onRestartFlow: (useFlowId) => { + if (useFlowId) { + replaceWindowFlowId(useFlowId) + } else { + onRedirect(recoveryUrl(config), true) + } }, onValidationError: (body: RecoveryFlow | { error: GenericError }) => { if ("error" in body) { diff --git a/packages/elements-react/src/util/onSubmitRegistration.ts b/packages/elements-react/src/util/onSubmitRegistration.ts index e6633d7d..a7206fc9 100644 --- a/packages/elements-react/src/util/onSubmitRegistration.ts +++ b/packages/elements-react/src/util/onSubmitRegistration.ts @@ -12,6 +12,7 @@ import { import { OryFlowContainer } from "./flowContainer" import { OnSubmitHandlerProps } from "./submitHandler" import { frontendClient } from "./client" +import { replaceWindowFlowId } from "./internal" /** * Use this method to submit a registration flow. This method is used in the `onSubmit` handler of the registration form. @@ -60,8 +61,12 @@ export async function onSubmitRegistration( }) .catch( handleFlowError({ - onRestartFlow: () => { - onRedirect(registrationUrl(config), true) + onRestartFlow: (useFlowId) => { + if (useFlowId) { + replaceWindowFlowId(useFlowId) + } else { + onRedirect(registrationUrl(config), true) + } }, onValidationError: (body: RegistrationFlow) => { setFlowContainer({ diff --git a/packages/elements-react/src/util/onSubmitSettings.ts b/packages/elements-react/src/util/onSubmitSettings.ts index bf9ac6f9..c3bbb79b 100644 --- a/packages/elements-react/src/util/onSubmitSettings.ts +++ b/packages/elements-react/src/util/onSubmitSettings.ts @@ -14,6 +14,7 @@ import { import { OryFlowContainer } from "./flowContainer" import { OnSubmitHandlerProps } from "./submitHandler" import { frontendClient } from "./client" +import { replaceWindowFlowId } from "./internal" /** * Use this method to submit a settings flow. This method is used in the `onSubmit` handler of the settings form. @@ -64,8 +65,12 @@ export async function onSubmitSettings( }) .catch( handleFlowError({ - onRestartFlow: () => { - onRedirect(settingsUrl(config), true) + onRestartFlow: (useFlowId) => { + if (useFlowId) { + replaceWindowFlowId(useFlowId) + } else { + onRedirect(settingsUrl(config), true) + } }, onValidationError: (body: SettingsFlow) => { setFlowContainer({ diff --git a/packages/elements-react/src/util/onSubmitVerification.ts b/packages/elements-react/src/util/onSubmitVerification.ts index 5ebee301..fa82c5dd 100644 --- a/packages/elements-react/src/util/onSubmitVerification.ts +++ b/packages/elements-react/src/util/onSubmitVerification.ts @@ -11,6 +11,7 @@ import { import { OryFlowContainer } from "./flowContainer" import { OnSubmitHandlerProps } from "./submitHandler" import { frontendClient } from "./client" +import { replaceWindowFlowId } from "./internal" /** * Use this method to submit a verification flow. This method is used in the `onSubmit` handler of the verification form. @@ -49,8 +50,12 @@ export async function onSubmitVerification( ) .catch( handleFlowError({ - onRestartFlow: () => { - onRedirect(verificationUrl(config), true) + onRestartFlow: (useFlowId) => { + if (useFlowId) { + replaceWindowFlowId(useFlowId) + } else { + onRedirect(verificationUrl(config), true) + } }, onValidationError: (body: VerificationFlow) => { setFlowContainer({ diff --git a/packages/elements-react/src/util/ui/index.ts b/packages/elements-react/src/util/ui/index.ts index 8a352367..42533e55 100644 --- a/packages/elements-react/src/util/ui/index.ts +++ b/packages/elements-react/src/util/ui/index.ts @@ -107,8 +107,8 @@ type Entries = { * * This method the default, identifier_first, and profile groups. * - * @param nodes The nodes to extract the auth methods from - * @param excludeAuthMethods A list of auth methods to exclude + * @param nodes - The nodes to extract the auth methods from + * @param excludeAuthMethods - A list of auth methods to exclude */ export function nodesToAuthMethodGroups( nodes: Array, @@ -145,7 +145,7 @@ export function nodesToAuthMethodGroups( /** * Groups nodes by their group and returns an object with the groups and entries. * - * @param nodes + * @param nodes - The nodes to group */ export function useNodesGroups(nodes: UiNode[]) { const groupSorter = useGroupSorter() diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index b49c0bce..8968a5f4 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -8,7 +8,7 @@ "types": "./dist/index.d.ts", "private": false, "dependencies": { - "@ory/client-fetch": "^1.15.6", + "@ory/client-fetch": "~1.16.0", "cookie": "^1.0.1", "psl": "^1.15.0", "set-cookie-parser": "^2.7.1"