Skip to content

Commit a930d4f

Browse files
authored
feat(auth): custom Ory Elements login UI (#380)
1 parent 2265d52 commit a930d4f

30 files changed

Lines changed: 1042 additions & 41 deletions

.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
2929
### Ory Network configuration (required when AUTH_PROVIDER=ory)
3030
### SDK URL of the Ory Network project (or custom domain like https://auth.e2b.dev)
3131
# ORY_SDK_URL=https://your-project.projects.oryapis.com
32+
### Browser-facing Kratos public URL for the custom @ory/elements-react login
33+
### page. Self-hosted: the Kratos public endpoint (harness default :4433).
34+
### Ory Network: same value as ORY_SDK_URL.
35+
# NEXT_PUBLIC_ORY_SDK_URL=http://localhost:4433
36+
### Set to 1 to enable the custom @ory/elements-react login/registration UI
37+
### (staging/preview and local dev). Leave unset in production to keep /sign-in.
38+
# NEXT_PUBLIC_ORY_CUSTOM_UI=1
3239
### OAuth2 client credentials issued by Ory for this dashboard deployment
3340
# ORY_OAUTH2_CLIENT_ID=
3441
# ORY_OAUTH2_CLIENT_SECRET=

bun.lock

Lines changed: 111 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,8 @@
1717
"utils": "@/lib/utils",
1818
"hooks": "@/lib/hooks"
1919
},
20-
"iconLibrary": "lucide"
20+
"iconLibrary": "lucide",
21+
"registries": {
22+
"@svgl": "https://svgl.app/r/{name}.json"
23+
}
2124
}

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
"@opentelemetry/sdk-node": "^0.218.0",
6262
"@opentelemetry/semantic-conventions": "^1.36.0",
6363
"@ory/client-fetch": "^1.22.37",
64+
"@ory/elements-react": "^1.2.0",
65+
"@ory/nextjs": "^1.0.0-rc.1",
6466
"@radix-ui/react-avatar": "^1.1.4",
6567
"@radix-ui/react-checkbox": "^1.3.3",
6668
"@radix-ui/react-dialog": "^1.1.15",
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use client'
2+
3+
import { FlowType } from '@ory/client-fetch'
4+
import type { OryNodeButtonProps } from '@ory/elements-react'
5+
import { useOryFlow } from '@ory/elements-react'
6+
import { Button } from '@/ui/primitives/button'
7+
8+
export function OryButton({
9+
node,
10+
buttonProps,
11+
isSubmitting,
12+
}: OryNodeButtonProps) {
13+
const { flowType } = useOryFlow()
14+
const label = node.meta?.label?.text
15+
const loadingLabel =
16+
flowType === FlowType.Registration ? 'Signing up…' : 'Signing in…'
17+
18+
return (
19+
<Button {...buttonProps} loading={isSubmitting ? loadingLabel : undefined}>
20+
{label}
21+
</Button>
22+
)
23+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use client'
2+
3+
import { FlowType } from '@ory/client-fetch'
4+
import { useOryFlow } from '@ory/elements-react'
5+
6+
export function OryCardHeader() {
7+
const { flowType } = useOryFlow()
8+
const title = flowType === FlowType.Registration ? 'Sign up' : 'Sign in'
9+
10+
return <h1 className="mb-6">{title}</h1>
11+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
'use client'
2+
3+
import { FlowType } from '@ory/client-fetch'
4+
import { useOryFlow } from '@ory/elements-react'
5+
import Link from 'next/link'
6+
import type { PropsWithChildren } from 'react'
7+
8+
export function OryCard({ children }: PropsWithChildren) {
9+
return <div className="bg-bg flex w-full flex-col border p-6">{children}</div>
10+
}
11+
12+
export function OryCardFooter() {
13+
const { flowType } = useOryFlow()
14+
15+
if (flowType === FlowType.Login) {
16+
return (
17+
<p className="text-fg-secondary mt-6">
18+
Don't have an account?{' '}
19+
<Link href="/registration" className="text-fg underline">
20+
Sign up
21+
</Link>
22+
.
23+
</p>
24+
)
25+
}
26+
27+
if (flowType !== FlowType.Registration) {
28+
return null
29+
}
30+
31+
return (
32+
<div className="text-fg-secondary mt-6 flex flex-col gap-4">
33+
<p>
34+
Already have an account?{' '}
35+
<Link href="/login" className="text-fg underline">
36+
Sign in
37+
</Link>
38+
.
39+
</p>
40+
<p className="text-fg-tertiary">
41+
By signing up, you agree to our{' '}
42+
<Link
43+
href="/terms"
44+
target="_blank"
45+
className="text-fg-secondary underline"
46+
>
47+
Terms of Service
48+
</Link>{' '}
49+
and{' '}
50+
<Link
51+
href="/privacy"
52+
target="_blank"
53+
className="text-fg-secondary underline"
54+
>
55+
Privacy Policy
56+
</Link>
57+
.
58+
</p>
59+
</div>
60+
)
61+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use client'
2+
3+
import TextSeparator from '@/ui/text-separator'
4+
5+
export function OryDivider() {
6+
return <TextSeparator text="or" />
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use client'
2+
3+
import type { PropsWithChildren } from 'react'
4+
5+
export function OryFormGroup({ children }: PropsWithChildren) {
6+
return <div className="flex flex-col gap-4">{children}</div>
7+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use client'
2+
3+
import type { OryNodeInputProps } from '@ory/elements-react'
4+
import { Input } from '@/ui/primitives/input'
5+
6+
export function OryInput({ inputProps, node }: OryNodeInputProps) {
7+
const placeholder =
8+
node.attributes.name === 'identifier' || inputProps.type === 'email'
9+
? 'you@example.com'
10+
: inputProps.type === 'password'
11+
? '••••••••••••'
12+
: undefined
13+
14+
return <Input {...inputProps} {...(placeholder ? { placeholder } : {})} />
15+
}

0 commit comments

Comments
 (0)