1
0
Fork 0
mirror of https://codeberg.org/MarkusThielker/next-ory.git synced 2025-04-16 23:51:18 +00:00
next-ory/authentication/src/app/flow/login/page.tsx
2024-12-07 23:59:13 +01:00

175 lines
7.2 KiB
TypeScript

'use client';
import React, { useCallback, useEffect, useState } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Flow, HandleError, kratos, LogoutLink } from '@/ory';
import Link from 'next/link';
import { LoginFlow, UpdateLoginFlowBody } from '@ory/client';
import { Button } from '@/components/ui/button';
import { useRouter, useSearchParams } from 'next/navigation';
import Image from 'next/image';
import { Skeleton } from '@/components/ui/skeleton';
import { AxiosError } from 'axios';
export default function Login() {
const [flow, setFlow] = useState<LoginFlow>();
const router = useRouter();
const params = useSearchParams();
const flowId = params.get('flow') ?? undefined;
const aal = params.get('aal') ?? undefined;
const refresh = Boolean(params.get('refresh')) ? true : undefined;
const returnTo = params.get('return_to') ?? undefined;
const loginChallenge = params.get('login_challenge') ?? undefined;
const onLogout = LogoutLink([aal, refresh]);
const getFlow = useCallback((flowId: string) => {
return kratos
.getLoginFlow({ id: String(flowId) })
.then(({ data }) => setFlow(data))
.catch(handleError);
}, []);
const handleError = useCallback((error: AxiosError) => {
const handle = HandleError(getFlow, setFlow, '/flow/login', true, router);
return handle(error);
}, [getFlow]);
const createFlow = useCallback((aal: string | undefined, refresh: boolean | undefined, returnTo: string | undefined, loginChallenge: string | undefined) => {
kratos
.createBrowserLoginFlow({ aal, refresh, returnTo, loginChallenge })
.then(({ data }) => {
setFlow(data);
router.push(`?flow=${data.id}`);
})
.catch(handleError);
}, [handleError]);
const updateFlow = async (body: UpdateLoginFlowBody) => {
kratos
.updateLoginFlow({
flow: String(flow?.id),
updateLoginFlowBody: body,
})
.then(() => {
if (flow?.return_to) {
window.location.href = flow?.return_to;
return;
}
router.push('/');
})
.catch(handleError);
};
useEffect(() => {
if (flow) {
return;
}
if (flowId) {
getFlow(flowId).then();
return;
}
createFlow(aal, refresh, returnTo, loginChallenge);
}, [flowId, router, aal, refresh, returnTo, createFlow, loginChallenge, getFlow]);
return (
<Card className="flex flex-col items-center w-full max-w-sm p-4">
<Image className="mt-10 mb-4"
width="64"
height="64"
src="/mt-logo-orange.png"
alt="Markus Thielker Intranet"/>
<CardHeader className="flex flex-col items-center text-center space-y-4">
{
flow ?
<div className="flex flex-col space-y-4">
<CardTitle>{
(() => {
if (flow?.refresh) {
return 'Confirm Action';
} else if (flow?.requested_aal === 'aal2') {
return 'Two-Factor Authentication';
}
return 'Welcome';
})()}
</CardTitle>
<CardDescription className="max-w-xs">
Log in to the Intranet to access all locally hosted applications.
</CardDescription>
</div>
:
<div className="flex flex-col space-y-6">
<Skeleton className="h-6 w-full rounded-md"/>
<div className="flex flex-col space-y-2">
<Skeleton className="h-3 w-full rounded-md"/>
<Skeleton className="h-3 w-[250px] rounded-md"/>
</div>
</div>
}
</CardHeader>
<CardContent className="w-full">
{
flow
? <Flow flow={flow} onSubmit={updateFlow}/>
: (
<div className="flex flex-col space-y-4 mt-4">
<div className="flex flex-col space-y-2">
<Skeleton className="h-3 w-[80px] rounded-md"/>
<Skeleton className="h-8 w-full rounded-md"/>
</div>
<div className="flex flex-col space-y-2">
<Skeleton className="h-3 w-[80px] rounded-md"/>
<Skeleton className="h-8 w-full rounded-md"/>
</div>
<Button disabled>
<Skeleton className="h-4 w-[80px] rounded-md"/>
</Button>
</div>
)
}
</CardContent>
{
flow?.requested_aal === 'aal2' || flow?.requested_aal === 'aal3' || flow?.refresh ? (
<Button onClick={onLogout} variant="link">
Log out
</Button>
) : (
<div className="flex flex-col">
{
flow ?
<Button variant="link" asChild><Link
href={{ pathname: '/flow/recovery', query: { return_to: flow.return_to } }}
className="text-orange-600"
passHref>
Forgot your password?
</Link>
</Button>
:
<Skeleton className="h-3 w-[180px] rounded-md my-3.5"/>
}
{
flow ?
<Button variant="link" asChild disabled={!flow}>
<Link
href={{ pathname: '/flow/registration', query: { return_to: flow.return_to } }}
className="inline-flex space-x-2"
passHref>
Create an account
</Link>
</Button>
:
<Skeleton className="h-3 w-[180px] rounded-md my-3.5"/>
}
</div>
)
}
</Card>
);
}