bland-finland-95044
01/16/2025, 9:10 PMbrief-egg-61061
01/16/2025, 9:10 PMbrief-egg-61061
01/16/2025, 9:10 PMtypescript import Head from "next/head" import Image from "next/image" import styles from "../styles/Home.module.css" import { useEffect, useState } from "react" import { useRouter } from "next/router" import { Configuration, FrontendApi, Session, Identity } from "@ory/client" import { edgeConfig } from "@ory/integrations/next" const ory = new FrontendApi(new Configuration(edgeConfig)) // Returns either the email or the username depending on the user's Identity Schema const getUserName = (identity: Identity) => identity.traits.email || identity.traits.username const Home = () => { const router = useRouter() const [session, setSession] = useState<Session | undefined>() const [logoutUrl, setLogoutUrl] = useState<string | undefined>() useEffect(() => { ory .toSession() .then(({ data }) => { // User has a session! setSession(data) // Create a logout url ory.createBrowserLogoutFlow().then(({ data }) => { setLogoutUrl(data.logout_url) }) }) .catch(() => { // Redirect to login page return router.push(edgeConfig.basePath + "/ui/login") }) }, [router]) if (!session) { // Still loading return null } // ... rest of the component code } export default Home
This example shows how to integrate Ory Elements for recovery in a Next.js application. It includes the necessary imports from Ory client and integrations, sets up the Ory frontend API client, and implements a basic session check and logout functionality. To specifically handle the recovery flow, you would typically create a separate recovery page component. The recovery page would use Ory Elements to render the recovery form and handle the recovery process. For more detailed information on implementing the recovery flow with Ory Elements in Next.js, you can refer to the following resources: 1. [Custom user interface with Ory Elements](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-ory-elements#recovery-page) 2. [Integration basics](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#create-recovery-flow) These resources provide more comprehensive examples and explanations on how to implement the recovery flow using Ory Elements in a Next.js application.bland-finland-95044
01/17/2025, 5:43 PM"use client";
import { Form, TextInput, Button, InlineNotification, Stack, Loading } from '@carbon/react'
import type { RecoveryFlow, UiNode, UiNodeInputAttributes, UpdateRecoveryFlowBody } from "@ory/client"
import { filterNodesByGroups, isUiNodeInputAttributes } from '@ory/integrations/ui';
import { useSearchParams, useRouter } from "next/navigation"
import { useState, useEffect } from "react"
import ory from "@/lib/ory"
function Recovery() {
const [flow, setFlow] = useState<RecoveryFlow>()
const [error, setError] = useState<string>()
const router = useRouter()
const searchParams = useSearchParams()
const flowId = searchParams.get("flow")
const returnTo = searchParams.get("return_to")
const user = searchParams.get("user")
const handleSubmit = (email: string) => {
if (!flow) {
return
}
const submitData: UpdateRecoveryFlowBody = {
email,
method: "code",
csrf_token: flow.ui.nodes.find(
(node) => node.attributes.name === "csrf_token"
)?.attributes.value as string,
}
// FIX: remove
// console.log(flow)
// console.log(submitData)
ory
.updateRecoveryFlow({
flow: String(flow.id),
updateRecoveryFlowBody: submitData,
})
.then(({ data }) => {
setFlow(data)
setError(undefined)
})
.catch((error) => {
console.error(error)
if (error.response?.status === 400) {
setFlow(error.response.data)
// setError(error.response.data.ui.messages?.[0]?.text)
}
})
}
useEffect(() => {
if (flow) {
return
}
if (flowId) {
ory
.getRecoveryFlow({ id: String(flowId) })
.then(({ data }) => {
setFlow(data)
// if (user) {
// handleSubmit({ email: user })
// }
})
.catch((error) => {
// TODO: handle
console.error(error)
setError(error.message)
router.push("/auth/recovery")
})
return
}
ory
.createBrowserRecoveryFlow({
returnTo: returnTo ? String(returnTo) : undefined,
})
.then(({ data }) => {
setFlow(data)
router.push(`/auth/recovery?flow=${data.id}`)
// if (user) {
// handleSubmit({ email: user })
// }
})
.catch((error) => {
console.error(error)
setError(error.message)
})
}, [flowId, router, returnTo, flow, user])
// eslint-disable-next-line consistent-return
const mapUINode = (node: UiNode, _: number) => {
// other node types are also supported
// if (isUiNodeTextAttributes(node.attributes)) {
// if (isUiNodeImageAttributes(node.attributes)) {
// if (isUiNodeAnchorAttributes(node.attributes)) {
if (isUiNodeInputAttributes(node.attributes)) {
const attrs = node.attributes as UiNodeInputAttributes
const nodeType = attrs.type
// FIX: remove
console.log(attrs)
switch (nodeType) {
case "button":
case "submit":
return (
<Button type="submit" className="w-full" name={attrs.name} value={attrs.value}>
{attrs.name}
{/* Reset Password */}
</Button>
// <button
// type={attrs.type as "submit" | "reset" | "button" | undefined}
// name={attrs.name}
// value={attrs.value}
// />
)
// case "text":
// return (<div>{attrs.</div>)
default:
return (
<TextInput
id="email"
name={attrs.name}
type={attrs.type}
labelText=""
placeholder="Enter email"
autoComplete={
attrs.autocomplete || attrs.name === "identifier"
? "username"
: ""
}
required={attrs.required}
disabled={attrs.disabled}
defaultValue={attrs.value}
/>
// <input
// name={attrs.name}
// type={attrs.type}
// autoComplete={
// attrs.autocomplete || attrs.name === "identifier"
// ? "username"
// : ""
// }
// defaultValue={attrs.value}
// required={attrs.required}
// disabled={attrs.disabled}
// />
)
}
}
}
if (!flow) {
return <Loading />
}
return (
<div className="container max-w-md mx-auto p-6">
<Stack gap={7}>
<Form
method={flow.ui.method}
action={flow.ui.action}
onSubmit={(e) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
// FIX:remove. Figure out how to get token into form so we can get it here?
const body = Object.fromEntries(formData) as unknown as UpdateRecoveryFlowBody;
handleSubmit(formData.get("email") as string)
}}>
<Stack gap={7}>
{filterNodesByGroups({
nodes: flow.ui.nodes,
// we will also map default fields here such as csrf_token
groups: ["default", "code"],
}).map((node, idx) => mapUINode(node, idx))}
{flow.state === "sent_email" && (
<InlineNotification
kind="success"
title="Success"
subtitle="Recovery email has been sent. Please check your inbox."
hideCloseButton
/>
)}
{error && (
<InlineNotification
kind="error"
title="Error"
subtitle={error}
hideCloseButton
/>
)}
<Button
kind="ghost"
onClick={() => { router.push("/auth/login"); }}
className="w-full"
>
Back to Login
</Button>
</Stack>
</Form>
</Stack>
</div>
)
}
export default Recovery
brief-egg-61061
01/17/2025, 5:43 PMbrief-egg-61061
01/17/2025, 5:43 PM