prefill username in login

This commit is contained in:
miloschwartz
2026-02-05 16:55:00 -08:00
parent 165bbd3584
commit a8f6b6c1da
6 changed files with 55 additions and 17 deletions

View File

@@ -7,22 +7,35 @@ import { cache } from "react";
export const dynamic = "force-dynamic"; export const dynamic = "force-dynamic";
type Props = { type Props = {
searchParams: Promise<{ code?: string }>; searchParams: Promise<{ code?: string; user?: string }>;
}; };
function deviceRedirectSearchParams(params: {
code?: string;
user?: string;
}): string {
const search = new URLSearchParams();
if (params.code) search.set("code", params.code);
if (params.user) search.set("user", params.user);
const q = search.toString();
return q ? `?${q}` : "";
}
export default async function DeviceLoginPage({ searchParams }: Props) { export default async function DeviceLoginPage({ searchParams }: Props) {
const user = await verifySession({ forceLogin: true }); const user = await verifySession({ forceLogin: true });
const params = await searchParams; const params = await searchParams;
const code = params.code || ""; const code = params.code || "";
const defaultUser = params.user;
if (!user) { if (!user) {
const redirectDestination = code const redirectDestination = `/auth/login/device${deviceRedirectSearchParams({ code, user: params.user })}`;
? `/auth/login/device?code=${encodeURIComponent(code)}` const loginUrl = new URL("/auth/login", "http://x");
: "/auth/login/device"; loginUrl.searchParams.set("forceLogin", "true");
redirect( loginUrl.searchParams.set("redirect", redirectDestination);
`/auth/login?forceLogin=true&redirect=${encodeURIComponent(redirectDestination)}` if (defaultUser) loginUrl.searchParams.set("user", defaultUser);
); console.log("loginUrl", loginUrl.pathname + loginUrl.search);
redirect(loginUrl.pathname + loginUrl.search);
} }
const userName = user const userName = user
@@ -37,6 +50,7 @@ export default async function DeviceLoginPage({ searchParams }: Props) {
userEmail={user?.email || ""} userEmail={user?.email || ""}
userName={userName} userName={userName}
initialCode={code} initialCode={code}
userQueryParam={defaultUser}
/> />
); );
} }

View File

@@ -72,6 +72,8 @@ export default async function Page(props: {
searchParams.redirect = redirectUrl; searchParams.redirect = redirectUrl;
} }
const defaultUser = searchParams.user as string | undefined;
// Only use SmartLoginForm if NOT (OSS build OR org-only IdP enabled) // Only use SmartLoginForm if NOT (OSS build OR org-only IdP enabled)
const useSmartLogin = const useSmartLogin =
build === "saas" || (build === "enterprise" && env.flags.useOrgOnlyIdp); build === "saas" || (build === "enterprise" && env.flags.useOrgOnlyIdp);
@@ -151,6 +153,7 @@ export default async function Page(props: {
<SmartLoginForm <SmartLoginForm
redirect={redirectUrl} redirect={redirectUrl}
forceLogin={forceLogin} forceLogin={forceLogin}
defaultUser={defaultUser}
/> />
</CardContent> </CardContent>
</Card> </Card>
@@ -165,6 +168,7 @@ export default async function Page(props: {
(build === "saas" || env.flags.useOrgOnlyIdp) (build === "saas" || env.flags.useOrgOnlyIdp)
} }
searchParams={searchParams} searchParams={searchParams}
defaultUser={defaultUser}
/> />
)} )}

View File

@@ -29,6 +29,7 @@ type DashboardLoginFormProps = {
searchParams?: { searchParams?: {
[key: string]: string | string[] | undefined; [key: string]: string | string[] | undefined;
}; };
defaultUser?: string;
}; };
export default function DashboardLoginForm({ export default function DashboardLoginForm({
@@ -36,7 +37,8 @@ export default function DashboardLoginForm({
idps, idps,
forceLogin, forceLogin,
showOrgLogin, showOrgLogin,
searchParams searchParams,
defaultUser
}: DashboardLoginFormProps) { }: DashboardLoginFormProps) {
const router = useRouter(); const router = useRouter();
const { env } = useEnvContext(); const { env } = useEnvContext();
@@ -75,6 +77,7 @@ export default function DashboardLoginForm({
redirect={redirect} redirect={redirect}
idps={idps} idps={idps}
forceLogin={forceLogin} forceLogin={forceLogin}
defaultEmail={defaultUser}
onLogin={(redirectUrl) => { onLogin={(redirectUrl) => {
if (redirectUrl) { if (redirectUrl) {
const safe = cleanRedirect(redirectUrl); const safe = cleanRedirect(redirectUrl);

View File

@@ -55,12 +55,14 @@ type DeviceLoginFormProps = {
userEmail: string; userEmail: string;
userName?: string; userName?: string;
initialCode?: string; initialCode?: string;
userQueryParam?: string;
}; };
export default function DeviceLoginForm({ export default function DeviceLoginForm({
userEmail, userEmail,
userName, userName,
initialCode = "" initialCode = "",
userQueryParam
}: DeviceLoginFormProps) { }: DeviceLoginFormProps) {
const router = useRouter(); const router = useRouter();
const { env } = useEnvContext(); const { env } = useEnvContext();
@@ -219,9 +221,12 @@ export default function DeviceLoginForm({
const currentSearch = const currentSearch =
typeof window !== "undefined" ? window.location.search : ""; typeof window !== "undefined" ? window.location.search : "";
const redirectTarget = `/auth/login/device${currentSearch || ""}`; const redirectTarget = `/auth/login/device${currentSearch || ""}`;
router.push( const loginUrl = new URL("/auth/login", "http://x");
`/auth/login?forceLogin=true&redirect=${encodeURIComponent(redirectTarget)}` loginUrl.searchParams.set("forceLogin", "true");
); loginUrl.searchParams.set("redirect", redirectTarget);
if (userQueryParam)
loginUrl.searchParams.set("user", userQueryParam);
router.push(loginUrl.pathname + loginUrl.search);
router.refresh(); router.refresh();
} }
} }

View File

@@ -54,6 +54,7 @@ type LoginFormProps = {
idps?: LoginFormIDP[]; idps?: LoginFormIDP[];
orgId?: string; orgId?: string;
forceLogin?: boolean; forceLogin?: boolean;
defaultEmail?: string;
}; };
export default function LoginForm({ export default function LoginForm({
@@ -61,7 +62,8 @@ export default function LoginForm({
onLogin, onLogin,
idps, idps,
orgId, orgId,
forceLogin forceLogin,
defaultEmail
}: LoginFormProps) { }: LoginFormProps) {
const router = useRouter(); const router = useRouter();
@@ -116,7 +118,7 @@ export default function LoginForm({
const form = useForm({ const form = useForm({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: { defaultValues: {
email: "", email: defaultEmail ?? "",
password: "" password: ""
} }
}); });

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod"; import * as z from "zod";
@@ -42,6 +42,7 @@ const isValidEmail = (str: string): boolean => {
type SmartLoginFormProps = { type SmartLoginFormProps = {
redirect?: string; redirect?: string;
forceLogin?: boolean; forceLogin?: boolean;
defaultUser?: string;
}; };
type ViewState = type ViewState =
@@ -59,7 +60,8 @@ type ViewState =
export default function SmartLoginForm({ export default function SmartLoginForm({
redirect, redirect,
forceLogin forceLogin,
defaultUser
}: SmartLoginFormProps) { }: SmartLoginFormProps) {
const router = useRouter(); const router = useRouter();
const { lookup, loading, error } = useUserLookup(); const { lookup, loading, error } = useUserLookup();
@@ -72,10 +74,18 @@ export default function SmartLoginForm({
const form = useForm<z.infer<typeof identifierSchema>>({ const form = useForm<z.infer<typeof identifierSchema>>({
resolver: zodResolver(identifierSchema), resolver: zodResolver(identifierSchema),
defaultValues: { defaultValues: {
identifier: "" identifier: defaultUser ?? ""
} }
}); });
const hasAutoLookedUp = useRef(false);
useEffect(() => {
if (defaultUser?.trim() && !hasAutoLookedUp.current) {
hasAutoLookedUp.current = true;
void handleLookup({ identifier: defaultUser.trim() });
}
}, [defaultUser]);
const handleLookup = async (values: z.infer<typeof identifierSchema>) => { const handleLookup = async (values: z.infer<typeof identifierSchema>) => {
const identifier = values.identifier.trim(); const identifier = values.identifier.trim();
const isEmail = isValidEmail(identifier); const isEmail = isValidEmail(identifier);