import { useState, useRef, useEffect } from "react"; import {Loader2, Lock, Binary, LogIn} from "lucide-react"; import { getData, type Data } from "@/data"; import Button from "@/components/Button"; import { Input } from "@/components/Input"; import PinCodeInput, { type PinCodeInputRef } from "@/components/PinCodeInput"; import { SegmentedTabs } from "@/components/SegmentedTabs"; import { PoweredByNetBird } from "@/components/PoweredByNetBird"; import { Card } from "@/components/Card"; import { Title } from "@/components/Title"; import { Description } from "@/components/Description"; import { Separator } from "@/components/Separator"; import { ErrorMessage } from "@/components/ErrorMessage"; import { Label } from "@/components/Label"; const data = getData(); // For testing, show all methods if none are configured const methods: NonNullable = data.methods && Object.keys(data.methods).length > 0 ? data.methods : { password:"password", pin: "pin", oidc: "/auth/oidc" }; function App() { useEffect(() => { document.title = "Authentication Required - NetBird Service"; }, []); const [error, setError] = useState(null); const [submitting, setSubmitting] = useState(null); const [pin, setPin] = useState(""); const [password, setPassword] = useState(""); const passwordRef = useRef(null); const pinRef = useRef(null); const [activeTab, setActiveTab] = useState<"password" | "pin">( methods.password ? "password" : "pin" ); const handleAuthError = (method: "password" | "pin", message: string) => { setError(message); setSubmitting(null); if (method === "password") { setPassword(""); setTimeout(() => passwordRef.current?.focus(), 200); } else { setPin(""); setTimeout(() => pinRef.current?.focus(), 200); } }; const submitCredentials = (method: "password" | "pin", value: string) => { setError(null); setSubmitting(method); const formData = new FormData(); if (method === "password") { formData.append(methods.password!, value); } else { formData.append(methods.pin!, value); } fetch(globalThis.location.href, { method: "POST", body: formData, redirect: "manual", }) .then((res) => { if (res.type === "opaqueredirect" || res.status === 0) { setSubmitting("redirect"); globalThis.location.reload(); } else { handleAuthError(method, "Authentication failed. Please try again."); } }) .catch(() => { handleAuthError(method, "An error occurred. Please try again."); }); }; const handlePinChange = (value: string) => { setPin(value); if (value.length === 6) { submitCredentials("pin", value); } }; const isPinComplete = pin.length === 6; const isPasswordEntered = password.length > 0; const isButtonDisabled = submitting !== null || (activeTab === "password" && !isPasswordEntered) || (activeTab === "pin" && !isPinComplete); const hasCredentialAuth = methods.password || methods.pin; const hasBothCredentials = methods.password && methods.pin; const buttonLabel = activeTab === "password" ? "Sign in" : "Submit"; if (submitting === "redirect") { return (
Authenticated Loading service...
); } return (
Authentication Required The service you are trying to access is protected. Please authenticate to continue.
{error && } {/* SSO Button */} {methods.oidc && ( )} {/* Separator */} {methods.oidc && hasCredentialAuth && } {/* Credential Authentication */} {hasCredentialAuth && (
{ e.preventDefault(); submitCredentials(activeTab, activeTab === "password" ? password : pin); }}> {hasBothCredentials && ( { setActiveTab(v as "password" | "pin"); setTimeout(() => { if (v === "password") { passwordRef.current?.focus(); } else { pinRef.current?.focus(); } }, 0); }} > Password PIN )}
{methods.password && (activeTab === "password" || !methods.pin) && ( <> {!hasBothCredentials && } setPassword(e.target.value)} /> )} {methods.pin && (activeTab === "pin" || !methods.password) && ( <> {!hasBothCredentials && } )}
)}
); } export default App;