Vectrum
Sign In
81 lines · 3.4 KB · TypeScript
import type { Metadata } from "next";
import { startTwoFactorSetupAction } from "@/app/actions/settings";
import { BRAND } from "@/components/brand";
import { LockIcon } from "@/components/icons";
import { SettingsRow, SettingsSection } from "@/components/metal";
import { QrCode } from "@/components/qr-code";
import { PlateBody, PlateFooter, PlateSection } from "@/components/settings-plate";
import { Button, Label, PageTitle } from "@/components/ui";
import { requireViewer } from "@/lib/auth";
import { formatDate } from "@/lib/format";
import { groupSecret, otpauthUri } from "@/lib/totp";
import { twoFactorStatus } from "@/lib/two-factor";
import { DisableTwoFactorForm, EnableTwoFactorForm, RegenerateRecoveryCodesForm } from "./forms";

export const metadata: Metadata = { title: "Two-factor authentication" };

export default async function SecuritySettingsPage() {
  const viewer = await requireViewer("/settings/security");
  const status = await twoFactorStatus(viewer.id);

  return (
    <div>
      <PageTitle
        title="Two-factor authentication"
        description="Ask for a code from an authenticator app on your phone when you sign in, besides your password."
      />
      <div className="flex flex-col gap-7">
        {status.enabled ? (
          <>
            <SettingsSection title="Authenticator app">
              <SettingsRow
                name={
                  <span className="flex items-center gap-2">
                    Two-factor authentication <Label tone="success">On</Label>
                  </span>
                }
                detail={status.enabledAt ? `Turned on ${formatDate(status.enabledAt)}` : undefined}
              />
              <SettingsRow
                name="Recovery codes"
                detail={`${status.recoveryCodesLeft} unused. Each one signs you in once if you can’t use your app.`}
              />
            </SettingsSection>
            <PlateSection title="New recovery codes">
              <RegenerateRecoveryCodesForm />
            </PlateSection>
            <PlateSection title="Turn off two-factor authentication">
              <DisableTwoFactorForm />
            </PlateSection>
          </>
        ) : status.pendingSecret ? (
          <PlateSection title="Set up your authenticator app">
            <EnableTwoFactorForm
              secret={groupSecret(status.pendingSecret)}
              qr={<QrCode value={otpauthUri(status.pendingSecret, BRAND.name, viewer.username)} label="QR code for your authenticator app" />}
            />
          </PlateSection>
        ) : (
          <PlateSection title="Authenticator app">
            <form action={startTwoFactorSetupAction}>
              <PlateBody className="max-w-2xl">
                <p className="flex items-center gap-2 type-title text-ink">
                  Two-factor authentication <Label>Off</Label>
                </p>
                <p className="type-body text-ink2">
                  With it on, someone who learns your password still can’t sign in without your phone. Git over SSH and HTTPS, API
                  tokens and connected apps keep working as they are.
                </p>
              </PlateBody>
              <PlateFooter>
                <Button variant="primary" icon={<LockIcon />}>
                  Set Up Authenticator App
                </Button>
              </PlateFooter>
            </form>
          </PlateSection>
        )}
      </div>
    </div>
  );
}