Skip to content

Commit

Permalink
TMP: Kratos configured and basic registration and verifications flows…
Browse files Browse the repository at this point in the history
… are set
  • Loading branch information
JoblersTune committed Mar 1, 2024
1 parent b14c6fd commit 854b608
Show file tree
Hide file tree
Showing 13 changed files with 334 additions and 143 deletions.
32 changes: 32 additions & 0 deletions packages/frontend/app/lib/kratos_checks.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// todo remove session id logic since its actually a token and being handled differently
import { redirect } from '@remix-run/node'
import axios from 'axios'

export async function requireSession(cookieHeader?: string | null) {
console.log('COOKIES: ', cookieHeader)

try {
const session = await axios.get(`http://kratos:4433/sessions/whoami`, {
headers: {
cookie: cookieHeader
},
withCredentials: true
})

console.log('SESSION DATA: ', session.data)
console.log(
'VERIFIABLE ADDRESSES: ',
session.data.identity.verifiable_addresses
)

if (session.status !== 200 || !session.data?.active) {
// does active here mean it is a legit logged in session?
// Redirect to auth if there's no valid session
throw redirect('/auth')
}

return session
} catch {
throw redirect('/auth')
}
}
6 changes: 6 additions & 0 deletions packages/frontend/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { messageStorage, type Message } from './lib/message.server'
import tailwind from './styles/tailwind.css'
import { getOpenPaymentsUrl } from './shared/utils'
import { PublicEnv, type PublicEnvironment } from './PublicEnv'
import { requireSession } from './lib/kratos_checks.server'

export const meta: MetaFunction = () => [
{ title: 'Rafiki Admin' },
Expand All @@ -30,6 +31,11 @@ export const meta: MetaFunction = () => [
]

export const loader = async ({ request }: LoaderFunctionArgs) => {
console.log('URL: ', request.url)
const url = new URL(request.url)
if (!url.pathname.startsWith('/auth')) {
await requireSession(request.headers.get('cookie'))
}
const session = await messageStorage.getSession(request.headers.get('cookie'))
const message = session.get('message') as Message

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { version } from '../../../../package.json'
import { Form } from '@remix-run/react'
import { redirect, type ActionFunctionArgs } from '@remix-run/node'
import { Button } from '../components/ui'

export default function Auth() {
return (
Expand All @@ -19,10 +20,31 @@ export default function Auth() {
In this web application, you'll be able to manage peering
relationships, assets, and wallet addresses, among other settings.
</p>
<p>
<a href='https://rafiki.dev' className='font-semibold'>
https://rafiki.dev
</a>
</p>
<div>
<Form method="post">
<button type="submit" name="action" value="login">Login</button>
<button type="submit" name="action" value="register">Register</button>
<Form method='post'>
<Button
type='submit'
name='action'
value='login'
aria-label='Login'
className='mr-2'
>
Login
</Button>
<Button
type='submit'
name='action'
value='register'
aria-label='Register'
className='ml-2'
>
Register
</Button>
</Form>
</div>
</div>
Expand All @@ -36,15 +58,17 @@ export async function action({ request }: ActionFunctionArgs) {
const action = formData.get('action')

if (action === 'login') {
// TODO: Make an API call to Ory Kratos for login
return
return redirect('http://127.0.0.1:4433/self-service/login/browser', {
headers: {
Accept: 'text/html'
}
})
} else if (action === 'register') {
return redirect('http://localhost:4433/self-service/registration/browser',
{
headers: {
'Accept': 'text/html'
}
})
return redirect('http://127.0.0.1:4433/self-service/registration/browser', {
headers: {
Accept: 'text/html'
}
})
}
throw new Error('Invalid auth action')
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function Login() {
<div className='p-10 space-y-16'>
<h3 className='text-2xl pt-16'>Login to Rafiki Admin</h3>
<div className='space-y-8'>
<form method='post' action='/login'>
<form method='post' action='/auth/login'>
<input
type='text'
name='username'
Expand Down
102 changes: 102 additions & 0 deletions packages/frontend/app/routes/auth.registration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// This is a dummy page
// TODO: Integrate with Ory Kratos
import { redirect, type LoaderFunctionArgs } from '@remix-run/node'
import { Button } from '../components/ui'
import { useLoaderData } from '@remix-run/react'

interface FieldAttribute {
name: string
type: string
value?: string
required?: boolean
disabled?: boolean
node_type?: string
autocomplete?: string
}

interface Field {
type: string
group: string
attributes: FieldAttribute
messages: object[]
meta: object
}

export const loader = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url)
const flowId = url.searchParams.get('flow')
const cookies = request.headers.get('cookie')

if (!flowId) {
throw redirect('http://127.0.0.1:4433/self-service/registration/browser')
} else {
const response = await fetch(
`http://kratos:4433/self-service/registration/flows?id=${flowId}`,
{
headers: {
Cookie: cookies || ''
},
credentials: 'include'
}
)

const responseData = await response.json()
const formFields: Field[] = responseData.ui.nodes // returns an array of form fields -> there's also a oauth2_login_challenge here you should investigate
formFields.push({
type: 'input',
group: 'default',
attributes: {
name: 'method',
type: 'hidden',
disabled: false,
node_type: 'input',
value: 'password',
required: true
},
messages: [],
meta: {}
})
return { formFields, flowId }
}
}

export default function Registration() {
const { formFields } = useLoaderData<typeof loader>()
const { flowId } = useLoaderData<typeof loader>()
const actionUrl = `http://127.0.0.1:4433/self-service/registration?flow=${flowId}`
return (
<div className='pt-4 flex flex-col'>
<div className='flex flex-col rounded-md bg-offwhite px-6 text-center min-h-[calc(100vh-3rem)]'>
<div className='p-10 space-y-16'>
<h3 className='text-2xl pt-16'>Register for Rafiki Admin</h3>
<div className='space-y-8'>
<form method='post' action={actionUrl}>
<div className='p-4 space-y-3'>
{formFields.map((field, index) => {
const { attributes, type } = field
if (type === 'input' && attributes.type !== 'submit') {
return (
<input
key={index}
type={attributes.type}
name={attributes.name}
placeholder={attributes.type}
required={attributes.required}
disabled={attributes.disabled}
value={attributes.value}
/>
)
}
return null
})}
<Button type='submit' aria-label='Register' className='ml-2'>
Register
</Button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
121 changes: 121 additions & 0 deletions packages/frontend/app/routes/auth.verification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Button } from '../components/ui'
import { useLoaderData, Form } from '@remix-run/react'
import {
redirect,
type LoaderFunctionArgs,
type ActionFunctionArgs
} from '@remix-run/node'

export const loader = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url)
const flowId = url.searchParams.get('flow')
const cookies = request.headers.get('cookie')

if (!flowId) {
throw new Error('No verification flow ID found')
} else {
const response = await fetch(
`http://kratos:4433/self-service/verification/flows?id=${flowId}`,
{
headers: {
Cookie: cookies || ''
},
credentials: 'include'
}
)

const flowData = await response.json()

return { flowData }
}
}

export default function Verification() {
const { flowData } = useLoaderData<typeof loader>()
const actionUrl = flowData.ui.action

if (flowData.state === 'passed_challenge') {
return (
<div className='pt-4 flex flex-col'>
<div className='flex flex-col rounded-md bg-offwhite px-6 text-center min-h-[calc(100vh-3rem)]'>
<div className='p-10 space-y-16'>
<h3 className='text-2xl pt-16'>Success</h3>
<div className='space-y-8'>
{flowData.ui.messages.map((message) => {
return <p key={message.id}>{message.text}</p>
})}
</div>
<div className='space-y-8'>
<Form method='post' action={'/auth/verification'}>
<Button
type='submit'
name='action'
value='login'
aria-label='Login'
className='mr-2'
>
Login
</Button>
</Form>
</div>
</div>
</div>
</div>
)
} else {
return (
<div className='pt-4 flex flex-col'>
<div className='flex flex-col rounded-md bg-offwhite px-6 text-center min-h-[calc(100vh-3rem)]'>
<div className='p-10 space-y-16'>
<h3 className='text-2xl pt-16'>Verification in progress</h3>
<div className='space-y-8'>
{flowData.ui.messages.map((message) => {
return <p key={message.id}>{message.text}</p>
})}
</div>
<div className='space-y-8'>
<form method={flowData.ui.method} action={actionUrl}>
<div className='p-4 space-y-3'>
{flowData.ui.nodes.map((field, index) => {
const { attributes, type } = field
if (type === 'input' && attributes.type !== 'submit') {
return (
<input
key={index}
type={attributes.type}
name={attributes.name}
placeholder={attributes.name}
required={attributes.required}
disabled={attributes.disabled}
value={attributes.value}
/>
)
}
return null
})}
<Button type='submit' aria-label='Register' className='ml-2'>
Verify
</Button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
}

export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData()
const action = formData.get('action')

if (action === 'login') {
return redirect('http://127.0.0.1:4433/self-service/login/browser', {
headers: {
Accept: 'text/html'
}
})
}
throw new Error('Invalid auth action')
}
8 changes: 4 additions & 4 deletions packages/frontend/app/routes/callback.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
redirect,
json,
type LoaderArgs,
type ActionArgs
type LoaderFunctionArgs,
type ActionFunctionArgs
} from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import { useEffect, useRef } from 'react'
Expand Down Expand Up @@ -39,7 +39,7 @@ export default function Callback() {
)
}

export const loader = async ({ request }: LoaderArgs) => {
export const loader = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url)
const authorizationCode = url.searchParams.get('code')
if (!authorizationCode) {
Expand All @@ -48,7 +48,7 @@ export const loader = async ({ request }: LoaderArgs) => {
return json({ authorizationCode })
}

export const action = async ({ request }: ActionArgs) => {
export const action = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData()
const authorizationCode = formData.get('code')

Expand Down
Loading

0 comments on commit 854b608

Please sign in to comment.