diff options
| author | Leitwolf11 | 2026-08-30 21:45:01 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-08-30 21:45:01 +0300 |
| commit | 76c4c1876a86dbca0adf86750362b3581d6fc69d (patch) | |
| tree | 52bc17d6a977d6c1b13c26c1e016a0e00db99ca5 /apps/web/components | |
| parent | c89a3f20a0f477573c41b831d993850e0adb30e7 (diff) | |
| download | progmaweb-1.0.0.tar.gz | |
Add secure Google sign-in to Progmasoft Account. Use the server-side authorization-code flow with an exact account-domain callback, temporary hardened external cookies, verified provider email matching, canonical Account-name creation for new users, and no separate ViGet publisher identity. Add frontend entry points, provider failure guidance, locked Google authentication dependencies, service secret injection, and backend regression coverage without committing OAuth credentials.1.0.0
Diffstat (limited to 'apps/web/components')
| -rw-r--r-- | apps/web/components/AuthForm.tsx | 40 |
1 files changed, 37 insertions, 3 deletions
diff --git a/apps/web/components/AuthForm.tsx b/apps/web/components/AuthForm.tsx index 56fb78d..f32c49d 100644 --- a/apps/web/components/AuthForm.tsx +++ b/apps/web/components/AuthForm.tsx @@ -5,11 +5,12 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; -import { type FormEvent, useId, useState } from "react"; +import { type FormEvent, type MouseEvent, useId, useState } from "react"; type AuthMode = "login" | "register"; interface AuthFormProps { + initialMessage?: string; mode: AuthMode; } @@ -30,13 +31,34 @@ function firstProblemMessage(problem: ProblemDetails): string { return firstError ?? problem.title ?? "The request could not be completed."; } -export function AuthForm({ mode }: AuthFormProps) { +export function AuthForm({ initialMessage, mode }: AuthFormProps) { const router = useRouter(); const formId = useId(); const [pending, setPending] = useState(false); - const [message, setMessage] = useState<string | null>(null); + const [message, setMessage] = useState<string | null>(initialMessage ?? null); const [showPassword, setShowPassword] = useState(false); + function continueWithGoogle(event: MouseEvent<HTMLButtonElement>) { + const form = event.currentTarget.form; + const parameters = new URLSearchParams(); + + if (mode === "register") { + const accountName = form?.elements.namedItem("accountName"); + if (!(accountName instanceof HTMLInputElement)) { + setMessage("The Account name field could not be read."); + return; + } + if (!accountName.checkValidity()) { + accountName.reportValidity(); + return; + } + parameters.set("accountName", accountName.value); + } + + const query = parameters.size === 0 ? "" : `?${parameters.toString()}`; + window.open(`/api/v1/accounts/oauth/google/start${query}`, "_self"); + } + async function submit(event: FormEvent<HTMLFormElement>) { event.preventDefault(); setMessage(null); @@ -217,6 +239,18 @@ export function AuthForm({ mode }: AuthFormProps) { : "Sign in"} </button> + <div className="auth-divider" aria-hidden="true"> + <span>or</span> + </div> + <button + className="button google-auth-button" + type="button" + disabled={pending} + onClick={continueWithGoogle} + > + Continue with Google + </button> + <p className="form-switch"> {mode === "register" ? "Already have an account?" |