From 6ec517012e0126d745d4a623629dc2d9a72d2e3b Mon Sep 17 00:00:00 2001 From: Aarnav Tale Date: Tue, 26 Mar 2024 11:11:12 -0400 Subject: [PATCH] feat: add sign in via api key --- app/routes/login.tsx | 57 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/app/routes/login.tsx b/app/routes/login.tsx index f9b8f63..c57cab8 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -1,10 +1,22 @@ -import { type LoaderFunctionArgs } from '@remix-run/node' -import { Link, useLoaderData } from '@remix-run/react' +import { type ActionFunctionArgs, json, type LoaderFunctionArgs, redirect } from '@remix-run/node' +import { Form, Link, useActionData, useLoaderData } from '@remix-run/react' import { useMemo } from 'react' +import { pull } from '~/utils/headscale' import { startOidc } from '~/utils/oidc' +import { commitSession, getSession } from '~/utils/sessions' export async function loader({ request }: LoaderFunctionArgs) { + const session = await getSession(request.headers.get('Cookie')) + if (session.has('hsApiKey')) { + return redirect('/machines', { + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Set-Cookie': await commitSession(session) + } + }) + } + const issuer = process.env.OIDC_ISSUER const id = process.env.OIDC_CLIENT_ID const secret = process.env.OIDC_CLIENT_SECRET @@ -31,8 +43,33 @@ export async function loader({ request }: LoaderFunctionArgs) { return data } +export async function action({ request }: ActionFunctionArgs) { + const formData = await request.formData() + const apiKey = String(formData.get('api-key')) + const session = await getSession(request.headers.get('Cookie')) + + // Test the API key + try { + await pull('v1/apikey', apiKey) + } catch (error) { + console.error(error) + return json({ + error: 'Invalid API key' + }) + } + + session.set('hsApiKey', apiKey) + return redirect('/machines', { + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Set-Cookie': await commitSession(session) + } + }) +} + export default function Page() { const data = useLoaderData() + const actionData = useActionData() const showOr = useMemo(() => data.oidc && data.apiKey, [data]) return ( @@ -40,7 +77,7 @@ export default function Page() {

Login

{data.apiKey ? ( - <> +

Enter an API key to authenticate with Headplane. You can generate one by running @@ -52,13 +89,25 @@ export default function Page() { in your terminal.

+ {actionData?.error ? ( +

{actionData.error}

+ ) : undefined} - + + +
) : undefined} {showOr ? (