diff --git a/examples/nextjs-spa/README.md b/examples/nextjs-spa/README.md index 1032d565b..30915da2e 100644 --- a/examples/nextjs-spa/README.md +++ b/examples/nextjs-spa/README.md @@ -43,20 +43,23 @@ browser! #### OAuth2 Login/Consent page -This example provides a working Login/Consent page using the Ory Elements UserAuthCard and UserConsentCard. +This example provides a working Login/Consent page using the Ory Elements +UserAuthCard and UserConsentCard. -To use the Consent page, the NextJS application will need a Ory API Token set as an environment variable. +To use the Consent page, the NextJS application will need a Ory API Token set as +an environment variable. ``` export NEXT_ADMIN_ORY_API_KEY=ory_pat_xxxxx ``` -The `NEXT_PUBLIC_ORY_SDK_URL` will be used for admin API calls as well since Ory Network projects expose both endpoint under the same URL. +The `NEXT_PUBLIC_ORY_SDK_URL` will be used for admin API calls as well since Ory +Network projects expose both endpoint under the same URL. -Take a look at the Ory Documentation to configure your Ory Network project to use this NextJS application as a custom consent UI. +Take a look at the Ory Documentation to configure your Ory Network project to +use this NextJS application as a custom consent UI. https://www.ory.sh/docs/oauth2-oidc/custom-login-consent/flow#consent - ### Using and Modifying the Example If you want to re-use this example in your own project, you can do so by diff --git a/examples/nextjs-spa/src/app/api/consent/route.ts b/examples/nextjs-spa/src/app/api/consent/route.ts index 69175ab07..bdf7e85aa 100644 --- a/examples/nextjs-spa/src/app/api/consent/route.ts +++ b/examples/nextjs-spa/src/app/api/consent/route.ts @@ -1,3 +1,6 @@ +// Copyright © 2023 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + import { oryIdentity, oryOAuth } from "@/pkg/sdk" import { redirect } from "next/navigation" import { NextRequest, NextResponse } from "next/server" diff --git a/examples/nextjs-spa/src/app/api/middleware.ts b/examples/nextjs-spa/src/app/api/middleware.ts index 306255868..8ce600dc2 100644 --- a/examples/nextjs-spa/src/app/api/middleware.ts +++ b/examples/nextjs-spa/src/app/api/middleware.ts @@ -1,3 +1,6 @@ +// Copyright © 2023 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + import csrf from "edge-csrf" import { NextResponse } from "next/server" import type { NextRequest } from "next/server" diff --git a/examples/nextjs-spa/tsconfig.json b/examples/nextjs-spa/tsconfig.json index a11e86445..bd893271e 100644 --- a/examples/nextjs-spa/tsconfig.json +++ b/examples/nextjs-spa/tsconfig.json @@ -2,11 +2,7 @@ "compilerOptions": { "target": "ESNext", "useDefineForClassFields": true, - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": false, "skipLibCheck": true, "strict": true, @@ -21,9 +17,7 @@ "incremental": true, "baseUrl": ".", "paths": { - "@/*": [ - "./src/*" - ] + "@/*": ["./src/*"] }, "plugins": [ { @@ -31,13 +25,6 @@ } ] }, - "exclude": [ - "node_modules" - ], - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ] + "exclude": ["node_modules"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"] } diff --git a/src/react-components/ory/helpers/common.tsx b/src/react-components/ory/helpers/common.tsx index dd7e8c85e..b92387ec2 100644 --- a/src/react-components/ory/helpers/common.tsx +++ b/src/react-components/ory/helpers/common.tsx @@ -1,5 +1,5 @@ import { JSX } from "react" - +import { isArray, isString, mergeWith } from "lodash" import { colorSprinkle } from "../../../theme" import { ButtonLink, CustomHref } from "../../button-link" import { Message } from "../../message" @@ -59,7 +59,21 @@ export function CustomOnSubmit( event.preventDefault() const form = event.currentTarget const formData = new FormData(form) - let body: Type = Object.fromEntries(formData.entries()) as Type + + let body: Type = {} as Type + for (const [key, value] of formData) { + body = mergeWith( + body, + { [key]: value }, + (objValue: unknown, srcValue: unknown) => { + if (isString(objValue) && isString(srcValue)) { + return [objValue, srcValue] + } else if (isArray(objValue) && isString(srcValue)) { + return objValue.concat(srcValue) + } + }, + ) + } // We need the method specified from the name and value of the submit button. // when multiple submit buttons are present, the clicked one's value is used. @@ -68,12 +82,12 @@ export function CustomOnSubmit( event.nativeEvent as unknown as { submitter: HTMLInputElement } ).submitter body = { - ...body, + ...(body as Type), ...{ [method.name]: method.value }, } } - callback && callback({ body, event }) + callback && callback({ body: body as Type, event }) - return body + return body as Type } diff --git a/src/react-components/ory/user-consent-card.spec.tsx b/src/react-components/ory/user-consent-card.spec.tsx index e3b784307..242c0f54c 100644 --- a/src/react-components/ory/user-consent-card.spec.tsx +++ b/src/react-components/ory/user-consent-card.spec.tsx @@ -72,10 +72,16 @@ test("ory consent card login flow with custom onSubmit", async ({ mount }) => { } }) - await consentComponent.submitForm("button[value='allow']") + await consentComponent.submitForm( + "button[name='consent_action'][value=accept]", + ) expect(submitBody).toBeTruthy() expect(submitBody).toEqual({ + remember: "1", + consent_challenge: "", + consent_action: "accept", + _csrf: defaults.csrfToken, grant_scope: defaults.requested_scope, }) }) diff --git a/src/react-components/ory/user-consent-card.tsx b/src/react-components/ory/user-consent-card.tsx index 22c4331ff..5b9ea5d52 100644 --- a/src/react-components/ory/user-consent-card.tsx +++ b/src/react-components/ory/user-consent-card.tsx @@ -3,11 +3,7 @@ import { Button } from "../button" import { Card } from "../card" import { Typography } from "../typography" -import { - OAuth2Client, - OAuth2ConsentRequest, - AcceptOAuth2ConsentRequest, -} from "@ory/client" +import { OAuth2Client, OAuth2ConsentRequest } from "@ory/client" import { Checkbox } from "../checkbox" import { Divider } from "../divider" @@ -75,7 +71,6 @@ export const UserConsentCard = ({ })} {...(onSubmit && { onSubmit: (event: React.FormEvent) => { - console.log("Submitting Consent page") CustomOnSubmit(event, onSubmit) }, })}