From c0e31a716eb11066816e305f4e417745dc2017df Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Fri, 15 Nov 2024 16:37:45 +0100 Subject: [PATCH] chore: synchronize workspaces --- .../nextjs-app-router/app/auth/login/page.tsx | 6 +- .../app/auth/recovery/page.tsx | 29 +++++ .../app/auth/registration/page.tsx | 27 ++++ .../app/auth/verification/page.tsx | 29 +++++ .../custom-card-header.tsx} | 2 +- examples/nextjs-app-router/ory.config.ts | 3 + examples/nextjs-pages-router/ory.config.ts | 3 + .../nextjs-pages-router/pages/auth/login.tsx | 29 +++++ .../pages/auth/recovery.tsx | 29 +++++ .../pages/auth/verification.tsx | 29 +++++ packages/nextjs/src/app/flow.ts | 64 ++++++++++ packages/nextjs/src/app/index.ts | 4 + packages/nextjs/src/app/login.ts | 96 ++++---------- packages/nextjs/src/app/recovery.ts | 53 ++++++++ packages/nextjs/src/app/registration.ts | 53 ++++++++ packages/nextjs/src/app/verification.ts | 53 ++++++++ packages/nextjs/src/pages/client.ts | 21 +-- packages/nextjs/src/pages/flow.ts | 65 ++++++++++ packages/nextjs/src/pages/index.ts | 3 + packages/nextjs/src/pages/login.ts | 22 ++++ packages/nextjs/src/pages/recovery.ts | 16 +++ packages/nextjs/src/pages/registration.ts | 120 +++--------------- packages/nextjs/src/pages/utils.ts | 36 ++++++ packages/nextjs/src/pages/verification.ts | 16 +++ packages/nextjs/src/utils/utils.ts | 8 ++ 25 files changed, 626 insertions(+), 190 deletions(-) create mode 100644 examples/nextjs-app-router/app/auth/recovery/page.tsx create mode 100644 examples/nextjs-app-router/app/auth/registration/page.tsx create mode 100644 examples/nextjs-app-router/app/auth/verification/page.tsx rename examples/nextjs-app-router/{app/auth/login/card-header.tsx => components/custom-card-header.tsx} (73%) create mode 100644 examples/nextjs-pages-router/pages/auth/login.tsx create mode 100644 examples/nextjs-pages-router/pages/auth/recovery.tsx create mode 100644 examples/nextjs-pages-router/pages/auth/verification.tsx create mode 100644 packages/nextjs/src/app/flow.ts create mode 100644 packages/nextjs/src/app/recovery.ts create mode 100644 packages/nextjs/src/app/registration.ts create mode 100644 packages/nextjs/src/app/verification.ts create mode 100644 packages/nextjs/src/pages/flow.ts create mode 100644 packages/nextjs/src/pages/login.ts create mode 100644 packages/nextjs/src/pages/recovery.ts create mode 100644 packages/nextjs/src/pages/verification.ts diff --git a/examples/nextjs-app-router/app/auth/login/page.tsx b/examples/nextjs-app-router/app/auth/login/page.tsx index f8d6479a4..eceeaaccc 100644 --- a/examples/nextjs-app-router/app/auth/login/page.tsx +++ b/examples/nextjs-app-router/app/auth/login/page.tsx @@ -6,7 +6,7 @@ import { getLoginFlow, OryPageParams } from "@ory/nextjs/app" import { enhanceConfig } from "@ory/nextjs" import config from "@/ory.config" -import CardHeader from "@/app/auth/login/card-header" +import CustomCardHeader from "@/components/custom-card-header" export default async function LoginPage(props: OryPageParams) { const flow = await getLoginFlow(props.searchParams) @@ -20,9 +20,7 @@ export default async function LoginPage(props: OryPageParams) { flow={flow} config={enhanceConfig(config)} components={{ - Card: { - Header: CardHeader, - }, + Card: {}, }} /> ) diff --git a/examples/nextjs-app-router/app/auth/recovery/page.tsx b/examples/nextjs-app-router/app/auth/recovery/page.tsx new file mode 100644 index 000000000..67718d65e --- /dev/null +++ b/examples/nextjs-app-router/app/auth/recovery/page.tsx @@ -0,0 +1,29 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { Recovery } from "@ory/elements-react/theme" +import { getRecoveryFlow, OryPageParams } from "@ory/nextjs/app" +import { enhanceConfig } from "@ory/nextjs" + +import config from "@/ory.config" +import CustomCardHeader from "@/components/custom-card-header" + +export default async function RecoveryPage(props: OryPageParams) { + const flow = await getRecoveryFlow(props.searchParams) + + if (!flow) { + return null + } + + return ( + + ) +} diff --git a/examples/nextjs-app-router/app/auth/registration/page.tsx b/examples/nextjs-app-router/app/auth/registration/page.tsx new file mode 100644 index 000000000..bfbf218ae --- /dev/null +++ b/examples/nextjs-app-router/app/auth/registration/page.tsx @@ -0,0 +1,27 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { Registration } from "@ory/elements-react/theme" +import { getRegistrationFlow, OryPageParams } from "@ory/nextjs/app" +import { enhanceConfig } from "@ory/nextjs" + +import config from "@/ory.config" +import CustomCardHeader from "@/components/custom-card-header" + +export default async function RegistrationPage(props: OryPageParams) { + const flow = await getRegistrationFlow(props.searchParams) + + if (!flow) { + return null + } + + return ( + + ) +} diff --git a/examples/nextjs-app-router/app/auth/verification/page.tsx b/examples/nextjs-app-router/app/auth/verification/page.tsx new file mode 100644 index 000000000..057303455 --- /dev/null +++ b/examples/nextjs-app-router/app/auth/verification/page.tsx @@ -0,0 +1,29 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { Verification } from "@ory/elements-react/theme" +import { getVerificationFlow, OryPageParams } from "@ory/nextjs/app" +import { enhanceConfig } from "@ory/nextjs" + +import config from "@/ory.config" +import CustomCardHeader from "@/components/custom-card-header" + +export default async function VerificationPage(props: OryPageParams) { + const flow = await getVerificationFlow(props.searchParams) + + if (!flow) { + return null + } + + return ( + + ) +} diff --git a/examples/nextjs-app-router/app/auth/login/card-header.tsx b/examples/nextjs-app-router/components/custom-card-header.tsx similarity index 73% rename from examples/nextjs-app-router/app/auth/login/card-header.tsx rename to examples/nextjs-app-router/components/custom-card-header.tsx index f6ea27eeb..31683ac6a 100644 --- a/examples/nextjs-app-router/app/auth/login/card-header.tsx +++ b/examples/nextjs-app-router/components/custom-card-header.tsx @@ -3,6 +3,6 @@ "use client" -export default function CardHeader() { +export default function CustomCardHeader() { return
My custom card header
} diff --git a/examples/nextjs-app-router/ory.config.ts b/examples/nextjs-app-router/ory.config.ts index d07568cbf..30dbd2fd2 100644 --- a/examples/nextjs-app-router/ory.config.ts +++ b/examples/nextjs-app-router/ory.config.ts @@ -7,6 +7,9 @@ const config: OryConfig = { override: { applicationName: "NextJS app router example", loginUiPath: "/auth/login", + registrationUiPath: "/auth/registration", + recoveryUiPath: "/auth/recovery", + verificationUiPath: "/auth/verification", }, } diff --git a/examples/nextjs-pages-router/ory.config.ts b/examples/nextjs-pages-router/ory.config.ts index 7130533dd..8ec4d57f1 100644 --- a/examples/nextjs-pages-router/ory.config.ts +++ b/examples/nextjs-pages-router/ory.config.ts @@ -6,7 +6,10 @@ import type { OryConfig } from "@ory/nextjs" const config: OryConfig = { override: { applicationName: "NextJS pages router example", + loginUiPath: "/auth/login", registrationUiPath: "/auth/registration", + recoveryUiPath: "/auth/recovery", + verificationUiPath: "/auth/verification", }, } diff --git a/examples/nextjs-pages-router/pages/auth/login.tsx b/examples/nextjs-pages-router/pages/auth/login.tsx new file mode 100644 index 000000000..40cf6f60b --- /dev/null +++ b/examples/nextjs-pages-router/pages/auth/login.tsx @@ -0,0 +1,29 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 +"use client" +import { Login } from "@ory/elements-react/theme" +import { useLoginFlow } from "@ory/nextjs/pages" +import { enhanceConfig } from "@ory/nextjs" +import "@ory/elements-react/theme/styles.css" + +import config from "@/ory.config" + +export default function LoginPage() { + const flow = useLoginFlow() + + if (!flow) { + return null + } + + return ( +
+ +
+ ) +} diff --git a/examples/nextjs-pages-router/pages/auth/recovery.tsx b/examples/nextjs-pages-router/pages/auth/recovery.tsx new file mode 100644 index 000000000..315e8d066 --- /dev/null +++ b/examples/nextjs-pages-router/pages/auth/recovery.tsx @@ -0,0 +1,29 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 +"use client" +import { Recovery } from "@ory/elements-react/theme" +import { useRecoveryFlow } from "@ory/nextjs/pages" +import { enhanceConfig } from "@ory/nextjs" +import "@ory/elements-react/theme/styles.css" + +import config from "@/ory.config" + +export default function RecoveryPage() { + const flow = useRecoveryFlow() + + if (!flow) { + return null + } + + return ( +
+ +
+ ) +} diff --git a/examples/nextjs-pages-router/pages/auth/verification.tsx b/examples/nextjs-pages-router/pages/auth/verification.tsx new file mode 100644 index 000000000..1b9837f3f --- /dev/null +++ b/examples/nextjs-pages-router/pages/auth/verification.tsx @@ -0,0 +1,29 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 +"use client" +import { Verification } from "@ory/elements-react/theme" +import { useVerificationFlow } from "@ory/nextjs/pages" +import { enhanceConfig } from "@ory/nextjs" +import "@ory/elements-react/theme/styles.css" + +import config from "@/ory.config" + +export default function VerificationPage() { + const flow = useVerificationFlow() + + if (!flow) { + return null + } + + return ( +
+ +
+ ) +} diff --git a/packages/nextjs/src/app/flow.ts b/packages/nextjs/src/app/flow.ts new file mode 100644 index 000000000..08a64411c --- /dev/null +++ b/packages/nextjs/src/app/flow.ts @@ -0,0 +1,64 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { redirect, RedirectType } from "next/navigation" +import { FlowType, handleFlowError } from "@ory/client-fetch" + +import { getPublicUrl, onRedirect } from "./utils" +import { QueryParams } from "../types" +import { guessPotentiallyProxiedOrySdkUrl } from "../utils/sdk" +import { onValidationError } from "../utils/utils" +import { rewriteJsonResponse } from "../utils/rewrite" +import * as runtime from "@ory/client-fetch/src/runtime" + +export async function getFlow( + params: QueryParams | Promise, + fetchFlowRaw: () => Promise>, + flowType: FlowType, +): Promise { + const p = await params + + // Guess our own public url using Next.js helpers. We need the hostname, port, and protocol. + const knownProxiedUrl = await getPublicUrl() + + const onRestartFlow = () => { + return redirect( + new URL( + "/self-service/" + + flowType.toString() + + "/browser?" + + params.toString(), + guessPotentiallyProxiedOrySdkUrl({ + knownProxiedUrl, + }), + ).toString(), + RedirectType.replace, + ) + } + + if (!p["flow"]) { + onRestartFlow() + return + } + + try { + const rawResponse = await fetchFlowRaw() + return await rawResponse + .value() + .then( + (v: T): T => + rewriteJsonResponse( + v, + guessPotentiallyProxiedOrySdkUrl({ knownProxiedUrl }), + ), + ) + } catch (error) { + const errorHandler = handleFlowError({ + onValidationError, + onRestartFlow, + onRedirect: onRedirect, + }) + await errorHandler(error) + return null + } +} diff --git a/packages/nextjs/src/app/index.ts b/packages/nextjs/src/app/index.ts index b233b6e63..18e19f5bd 100644 --- a/packages/nextjs/src/app/index.ts +++ b/packages/nextjs/src/app/index.ts @@ -3,4 +3,8 @@ "use server" export { getLoginFlow } from "./login" +export { getRegistrationFlow } from "./registration" +export { getRecoveryFlow } from "./recovery" +export { getVerificationFlow } from "./verification" + export type { OryPageParams } from "./utils" diff --git a/packages/nextjs/src/app/login.ts b/packages/nextjs/src/app/login.ts index 5bc317c04..34b8451ec 100644 --- a/packages/nextjs/src/app/login.ts +++ b/packages/nextjs/src/app/login.ts @@ -1,42 +1,38 @@ // Copyright © 2024 Ory Corp // SPDX-License-Identifier: Apache-2.0 -import { redirect, RedirectType } from "next/navigation" -import { FlowType, handleFlowError, LoginFlow } from "@ory/client-fetch" +import { FlowType, LoginFlow } from "@ory/client-fetch" -import { getPublicUrl, onRedirect } from "./utils" import { initOverrides, QueryParams } from "../types" -import { toFlowParams } from "./utils" -import { guessPotentiallyProxiedOrySdkUrl } from "../utils/sdk" -import { onValidationError } from "../utils/utils" -import { rewriteJsonResponse } from "../utils/rewrite" import { serverSideFrontendClient } from "./client" - -// const factory = getFlowFactory({ -// redirectToBrowserEndpoint, -// onRedirect, -// toFlowParams, -// flowType: FlowType.Login, -// fetchFlow: newOryFrontendClient().getLoginFlowRaw, -// }) +import { getFlow } from "./flow" +import { toFlowParams } from "./utils" /** * Use this method in an app router page to fetch an existing login flow or to create a new one. This method works with server-side rendering. * * ``` - * import { useLoginFlow, newFrontendClient } from "@ory/nextjs" - * import { Login } from "@ory/elements/headless/flows/login" + * import { Login } from "@ory/elements-react/theme" + * import { getLoginFlow, OryPageParams } from "@ory/nextjs/app" + * import { enhanceConfig } from "@ory/nextjs" * - * const client = newFrontendClient() + * import config from "@/ory.config" + * import CardHeader from "@/app/auth/login/card-header" * - * export default async function LoginPage({ searchParams }: PageProps) { - * const flow = await useLoginFlow(searchParams, client) + * export default async function LoginPage(props: OryPageParams) { + * const flow = await getLoginFlow(props.searchParams) + * + * if (!flow) { + * return null + * } * * return ( * * ) @@ -48,52 +44,10 @@ import { serverSideFrontendClient } from "./client" export async function getLoginFlow( params: QueryParams | Promise, ): Promise { - const p = await params - - // Guess our own public url using Next.js helpers. We need the hostname, port, and protocol. - const knownProxiedUrl = await getPublicUrl() - - const onRestartFlow = () => { - return redirect( - new URL( - "/self-service/" + - FlowType.Login.toString() + - "/browser?" + - params.toString(), - guessPotentiallyProxiedOrySdkUrl({ - knownProxiedUrl, - }), - ).toString(), - RedirectType.replace, - ) - } - - if (!p["flow"]) { - onRestartFlow() - return - } - - try { - const resp = await serverSideFrontendClient.getLoginFlowRaw( - await toFlowParams(p), - initOverrides, - ) - - return await resp - .value() - .then((v) => - rewriteJsonResponse( - v, - guessPotentiallyProxiedOrySdkUrl({ knownProxiedUrl }), - ), - ) - } catch (error) { - const errorHandler = handleFlowError({ - onValidationError, - onRestartFlow, - onRedirect: onRedirect, - }) - await errorHandler(error) - return null - } + const p = await toFlowParams(await params) + return getFlow( + params, + () => serverSideFrontendClient.getLoginFlowRaw(p, initOverrides), + FlowType.Login, + ) } diff --git a/packages/nextjs/src/app/recovery.ts b/packages/nextjs/src/app/recovery.ts new file mode 100644 index 000000000..c25dd9026 --- /dev/null +++ b/packages/nextjs/src/app/recovery.ts @@ -0,0 +1,53 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 +import { FlowType, RecoveryFlow } from "@ory/client-fetch" + +import { initOverrides, QueryParams } from "../types" +import { serverSideFrontendClient } from "./client" +import { getFlow } from "./flow" +import { toFlowParams } from "./utils" + +/** + * Use this method in an app router page to fetch an existing recovery flow or to create a new one. This method works with server-side rendering. + * + * ``` + * import { Recovery } from "@ory/elements-react/theme" + * import { getRecoveryFlow, OryPageParams } from "@ory/nextjs/app" + * import { enhanceConfig } from "@ory/nextjs" + * + * import config from "@/ory.config" + * import CardHeader from "@/app/auth/recovery/card-header" + * + * export default async function RecoveryPage(props: OryPageParams) { + * const flow = await getRecoveryFlow(props.searchParams) + * + * if (!flow) { + * return null + * } + * + * return ( + * + * ) + * } + * ``` + * + * @param params The query parameters of the request. + */ +export async function getRecoveryFlow( + params: QueryParams | Promise, +): Promise { + const p = await toFlowParams(await params) + return getFlow( + params, + () => serverSideFrontendClient.getRecoveryFlowRaw(p, initOverrides), + FlowType.Recovery, + ) +} diff --git a/packages/nextjs/src/app/registration.ts b/packages/nextjs/src/app/registration.ts new file mode 100644 index 000000000..f9f4337e0 --- /dev/null +++ b/packages/nextjs/src/app/registration.ts @@ -0,0 +1,53 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 +import { FlowType, RegistrationFlow } from "@ory/client-fetch" + +import { initOverrides, QueryParams } from "../types" +import { serverSideFrontendClient } from "./client" +import { getFlow } from "./flow" +import { toFlowParams } from "./utils" + +/** + * Use this method in an app router page to fetch an existing registration flow or to create a new one. This method works with server-side rendering. + * + * ``` + * import { Registration } from "@ory/elements-react/theme" + * import { getRegistrationFlow, OryPageParams } from "@ory/nextjs/app" + * import { enhanceConfig } from "@ory/nextjs" + * + * import config from "@/ory.config" + * import CardHeader from "@/app/auth/registration/card-header" + * + * export default async function RegistrationPage(props: OryPageParams) { + * const flow = await getRegistrationFlow(props.searchParams) + * + * if (!flow) { + * return null + * } + * + * return ( + * + * ) + * } + * ``` + * + * @param params The query parameters of the request. + */ +export async function getRegistrationFlow( + params: QueryParams | Promise, +): Promise { + const p = await toFlowParams(await params) + return getFlow( + params, + () => serverSideFrontendClient.getRegistrationFlowRaw(p, initOverrides), + FlowType.Registration, + ) +} diff --git a/packages/nextjs/src/app/verification.ts b/packages/nextjs/src/app/verification.ts new file mode 100644 index 000000000..db007dc56 --- /dev/null +++ b/packages/nextjs/src/app/verification.ts @@ -0,0 +1,53 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 +import { FlowType, VerificationFlow } from "@ory/client-fetch" + +import { initOverrides, QueryParams } from "../types" +import { serverSideFrontendClient } from "./client" +import { getFlow } from "./flow" +import { toFlowParams } from "./utils" + +/** + * Use this method in an app router page to fetch an existing verification flow or to create a new one. This method works with server-side rendering. + * + * ``` + * import { Verification } from "@ory/elements-react/theme" + * import { getVerificationFlow, OryPageParams } from "@ory/nextjs/app" + * import { enhanceConfig } from "@ory/nextjs" + * + * import config from "@/ory.config" + * import CardHeader from "@/app/auth/verification/card-header" + * + * export default async function VerificationPage(props: OryPageParams) { + * const flow = await getVerificationFlow(props.searchParams) + * + * if (!flow) { + * return null + * } + * + * return ( + * + * ) + * } + * ``` + * + * @param params The query parameters of the request. + */ +export async function getVerificationFlow( + params: QueryParams | Promise, +): Promise { + const p = await toFlowParams(await params) + return getFlow( + params, + () => serverSideFrontendClient.getVerificationFlowRaw(p, initOverrides), + FlowType.Verification, + ) +} diff --git a/packages/nextjs/src/pages/client.ts b/packages/nextjs/src/pages/client.ts index 74f70667f..9cc0359d9 100644 --- a/packages/nextjs/src/pages/client.ts +++ b/packages/nextjs/src/pages/client.ts @@ -4,12 +4,15 @@ import { Configuration, FrontendApi } from "@ory/client-fetch" import { guessPotentiallyProxiedOrySdkUrl } from "../utils/sdk" -export const clientSideFrontendClient = new FrontendApi( - new Configuration({ - headers: { - Accept: "application/json", - }, - credentials: "include", - basePath: guessPotentiallyProxiedOrySdkUrl(), - }), -) +export const clientSideFrontendClient = () => + new FrontendApi( + new Configuration({ + headers: { + Accept: "application/json", + }, + credentials: "include", + basePath: guessPotentiallyProxiedOrySdkUrl({ + knownProxiedUrl: window.location.origin, + }), + }), + ) diff --git a/packages/nextjs/src/pages/flow.ts b/packages/nextjs/src/pages/flow.ts new file mode 100644 index 000000000..ebc27cb2c --- /dev/null +++ b/packages/nextjs/src/pages/flow.ts @@ -0,0 +1,65 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { FlowType, handleFlowError } from "@ory/client-fetch" +import { useEffect, useState } from "react" +import { useRouter } from "next/router" +import { useSearchParams } from "next/navigation" +import { handleRestartFlow, onValidationError, useOnRedirect } from "./utils" +import { toValue } from "../utils/utils" +import * as runtime from "@ory/client-fetch/src/runtime" + +interface Flow { + id: string +} + +export function createUseFlowFactory( + flowType: FlowType, + createFlow: (params: URLSearchParams) => Promise>, + getFlow: (id: string) => Promise>, +): () => T | null | void { + return () => { + const [flow, setFlow] = useState() + const router = useRouter() + const searchParams = useSearchParams() + const onRestartFlow = handleRestartFlow(searchParams, flowType) + const onRedirect = useOnRedirect() + + const errorHandler = handleFlowError({ + onValidationError, + onRestartFlow, + onRedirect, + }) + + const handleSetFlow = async (flow: T) => { + setFlow(flow) + + // Use the router to update the `flow` search parameter only + await router.replace({ + query: { flow: flow.id }, + }) + return + } + + useEffect(() => { + const id = searchParams.get("flow") + + // If the router is not ready yet, or we already have a flow, do nothing. + if (!router.isReady || flow !== undefined) { + return + } + + if (!id) { + createFlow(searchParams) + .then(toValue) + .then(handleSetFlow) + .catch(errorHandler) + return + } + + getFlow(id).then(toValue).then(handleSetFlow).catch(errorHandler) + }, [searchParams, router, router.isReady, flow]) + + return flow + } +} diff --git a/packages/nextjs/src/pages/index.ts b/packages/nextjs/src/pages/index.ts index 2e705f95f..e2a8c6aeb 100644 --- a/packages/nextjs/src/pages/index.ts +++ b/packages/nextjs/src/pages/index.ts @@ -3,3 +3,6 @@ "use client" export { useRegistrationFlow } from "./registration" +export { useVerificationFlow } from "./verification" +export { useRecoveryFlow } from "./recovery" +export { useLoginFlow } from "./login" diff --git a/packages/nextjs/src/pages/login.ts b/packages/nextjs/src/pages/login.ts new file mode 100644 index 000000000..4a848a23a --- /dev/null +++ b/packages/nextjs/src/pages/login.ts @@ -0,0 +1,22 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { clientSideFrontendClient } from "./client" +import { createUseFlowFactory } from "./flow" +import { FlowType } from "@ory/client-fetch" + +export const useLoginFlow = createUseFlowFactory( + FlowType.Login, + (params: URLSearchParams) => { + return clientSideFrontendClient().createBrowserLoginFlowRaw({ + refresh: params.get("refresh") === "true", + aal: params.get("aal") ?? undefined, + returnTo: params.get("return_to") ?? undefined, + cookie: params.get("cookie") ?? undefined, + loginChallenge: params.get("login_challenge") ?? undefined, + organization: params.get("organization") ?? undefined, + via: params.get("via") ?? undefined, + }) + }, + (id) => clientSideFrontendClient().getLoginFlowRaw({ id }), +) diff --git a/packages/nextjs/src/pages/recovery.ts b/packages/nextjs/src/pages/recovery.ts new file mode 100644 index 000000000..81671580d --- /dev/null +++ b/packages/nextjs/src/pages/recovery.ts @@ -0,0 +1,16 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { clientSideFrontendClient } from "./client" +import { createUseFlowFactory } from "./flow" +import { FlowType } from "@ory/client-fetch" + +export const useRecoveryFlow = createUseFlowFactory( + FlowType.Recovery, + (params: URLSearchParams) => { + return clientSideFrontendClient().createBrowserRecoveryFlowRaw({ + returnTo: params.get("return_to") ?? undefined, + }) + }, + (id) => clientSideFrontendClient().getRecoveryFlowRaw({ id }), +) diff --git a/packages/nextjs/src/pages/registration.ts b/packages/nextjs/src/pages/registration.ts index fedede0c2..1d3d9c4c3 100644 --- a/packages/nextjs/src/pages/registration.ts +++ b/packages/nextjs/src/pages/registration.ts @@ -1,110 +1,20 @@ // Copyright © 2024 Ory Corp // SPDX-License-Identifier: Apache-2.0 -import { useEffect, useState } from "react" -import { useRouter } from "next/router" -import { useSearchParams } from "next/navigation" -import { - FlowType, - handleFlowError, - OnRedirectHandler, - RegistrationFlow, - ApiResponse, -} from "@ory/client-fetch" - import { clientSideFrontendClient } from "./client" -import { rewriteJsonResponse } from "../utils/rewrite" -import { guessPotentiallyProxiedOrySdkUrl } from "../utils/sdk" - -export function toValue(res: ApiResponse): Promise { - // Remove all undefined values from the response (array and object) using lodash: - // Remove all (nested) undefined values from the response using lodash - return res.value().then((v) => rewriteJsonResponse(v)) -} - -export function onValidationError(value: T): T { - return value -} - -const toBrowserEndpointRedirect = ( - params: URLSearchParams, - flowType: FlowType, -) => - guessPotentiallyProxiedOrySdkUrl({ - knownProxiedUrl: window.location.origin, - }) + - "/self-service/" + - flowType.toString() + - "/browser?" + - new URLSearchParams(params).toString() - -const handleRestartFlow = - (searchParams: URLSearchParams, flowType: FlowType) => () => { - window.location.assign(toBrowserEndpointRedirect(searchParams, flowType)) - } - -function useOnRedirect(): OnRedirectHandler { - const router = useRouter() - return (url: string, external: boolean) => { - if (external) { - window.location.assign(url) - } else { - router.push(url) - } - } -} - -export function useRegistrationFlow(): RegistrationFlow | null | void { - const [flow, setFlow] = useState() - const router = useRouter() - const searchParams = useSearchParams() - const onRestartFlow = handleRestartFlow(searchParams, FlowType.Registration) - const onRedirect = useOnRedirect() - - const errorHandler = handleFlowError({ - onValidationError, - onRestartFlow, - onRedirect, - }) - - const handleSetFlow = (flow: RegistrationFlow) => { - setFlow(flow) - - // Use the router to update the `flow` search parameter only - return router.replace({ - query: { flow: flow.id }, +import { createUseFlowFactory } from "./flow" +import { FlowType } from "@ory/client-fetch" + +export const useRegistrationFlow = createUseFlowFactory( + FlowType.Registration, + (params: URLSearchParams) => { + return clientSideFrontendClient().createBrowserRegistrationFlowRaw({ + returnTo: params.get("return_to") ?? undefined, + loginChallenge: params.get("registration_challenge") ?? undefined, + afterVerificationReturnTo: + params.get("after_verification_return_to") ?? undefined, + organization: params.get("organization") ?? undefined, }) - } - - useEffect(() => { - const id = searchParams.get("flow") - - // If the router is not ready yet, or we already have a flow, do nothing. - if (!router.isReady || flow !== undefined) { - return - } - - if (!id) { - clientSideFrontendClient - .createBrowserRegistrationFlowRaw({ - returnTo: searchParams.get("return_to") ?? undefined, - loginChallenge: searchParams.get("login_challenge") ?? undefined, - afterVerificationReturnTo: - searchParams.get("after_verification_return_to") ?? undefined, - organization: searchParams.get("organization") ?? undefined, - }) - .then(toValue) - .then(handleSetFlow) - .catch(errorHandler) - return - } - - clientSideFrontendClient - .getRegistrationFlowRaw({ id }) - .then(toValue) - .then(handleSetFlow) - .catch(errorHandler) - }, [searchParams, router, router.isReady, flow]) - - return flow -} + }, + (id) => clientSideFrontendClient().getRegistrationFlowRaw({ id }), +) diff --git a/packages/nextjs/src/pages/utils.ts b/packages/nextjs/src/pages/utils.ts index b85af654c..5cbf7e8d9 100644 --- a/packages/nextjs/src/pages/utils.ts +++ b/packages/nextjs/src/pages/utils.ts @@ -1,2 +1,38 @@ // Copyright © 2024 Ory Corp // SPDX-License-Identifier: Apache-2.0 + +import { FlowType, OnRedirectHandler } from "@ory/client-fetch" +import { guessPotentiallyProxiedOrySdkUrl } from "../utils/sdk" +import { useRouter } from "next/router" + +export function onValidationError(value: T): T { + return value +} + +export const toBrowserEndpointRedirect = ( + params: URLSearchParams, + flowType: FlowType, +) => + guessPotentiallyProxiedOrySdkUrl({ + knownProxiedUrl: window.location.origin, + }) + + "/self-service/" + + flowType.toString() + + "/browser?" + + new URLSearchParams(params).toString() + +export const handleRestartFlow = + (searchParams: URLSearchParams, flowType: FlowType) => () => { + window.location.assign(toBrowserEndpointRedirect(searchParams, flowType)) + } + +export function useOnRedirect(): OnRedirectHandler { + const router = useRouter() + return (url: string, external: boolean) => { + if (external) { + window.location.assign(url) + } else { + router.push(url) + } + } +} diff --git a/packages/nextjs/src/pages/verification.ts b/packages/nextjs/src/pages/verification.ts new file mode 100644 index 000000000..5c1a513d8 --- /dev/null +++ b/packages/nextjs/src/pages/verification.ts @@ -0,0 +1,16 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import { clientSideFrontendClient } from "./client" +import { createUseFlowFactory } from "./flow" +import { FlowType } from "@ory/client-fetch" + +export const useVerificationFlow = createUseFlowFactory( + FlowType.Verification, + (params: URLSearchParams) => { + return clientSideFrontendClient().createBrowserVerificationFlowRaw({ + returnTo: params.get("return_to") ?? undefined, + }) + }, + (id) => clientSideFrontendClient().getVerificationFlowRaw({ id }), +) diff --git a/packages/nextjs/src/utils/utils.ts b/packages/nextjs/src/utils/utils.ts index d0f63a02e..598f2cb54 100644 --- a/packages/nextjs/src/utils/utils.ts +++ b/packages/nextjs/src/utils/utils.ts @@ -6,6 +6,8 @@ import { serialize, SerializeOptions } from "cookie" import { FlowParams, OryConfig, QueryParams } from "../types" import { guessCookieDomain } from "./cookie" import { defaultForwardedHeaders } from "./headers" +import { ApiResponse } from "@ory/client-fetch" +import { rewriteJsonResponse } from "./rewrite" export function onValidationError(value: T): T { return value @@ -75,3 +77,9 @@ export function joinUrlPaths(baseUrl: string, relativeUrl: string): string { return new URL(relative.toString(), baseUrl).href } + +export function toValue(res: ApiResponse): Promise { + // Remove all undefined values from the response (array and object) using lodash: + // Remove all (nested) undefined values from the response using lodash + return res.value().then((v) => rewriteJsonResponse(v)) +}