in packages/dashboard-app/src/components/installs/modal/login/index.tsx [16:301]
export default function LoginModal({ next }: { next: () => void }) {
const midway = window?.fig?.constants?.midway ?? false;
const [loginState, setLoginState] = useState<
"not started" | "loading" | "logged in"
>("not started");
const [tab, setTab] = useLocalStateZodDefault<"builderId" | "iam">(
"dashboard.loginTab",
z.enum(["builderId", "iam"]),
midway ? "iam" : "builderId",
);
// Since PKCE requires the ability to open a browser, we also support falling
// back to device code in case of an error.
const [loginMethod, setLoginMethod] = useState<"pkce" | "deviceCode">("pkce");
// used for pkce
const [currAuthRequestId, setAuthRequestId] = useAuthRequest();
const [pkceTimedOut, setPkceTimedOut] = useState(false);
useEffect(() => {
if (pkceTimedOut) return;
if (loginMethod === "pkce" && loginState === "loading") {
const timer = setTimeout(() => setPkceTimedOut(true), 4000);
return () => clearTimeout(timer);
}
}, [loginMethod, loginState, pkceTimedOut]);
// used for device code
const [loginCode, setLoginCode] = useState<string | null>(null);
const [loginUrl, setLoginUrl] = useState<string | null>(null);
const [copyToClipboardText, setCopyToClipboardText] = useState<
"Copy to clipboard" | "Copied!"
>("Copy to clipboard");
const [error, setError] = useState<string | null>(null);
const [completedOnboarding] = useLocalStateZodDefault(
"desktop.completedOnboarding",
z.boolean(),
false,
);
const [showProfileTab, setShowProfileTab] = useState(false);
const auth = useAuth();
const refreshAuth = useRefreshAuth();
useEffect(() => {
// Reset the auth request id so that we don't present the "OAuth cancelled" error
// to the user.
setAuthRequestId("");
}, [loginMethod, setAuthRequestId]);
async function handleLogin(startUrl?: string, region?: string) {
if (loginMethod === "pkce") {
handlePkceAuth(startUrl, region);
} else {
handleDeviceCodeAuth(startUrl, region);
}
}
async function handlePkceAuth(issuerUrl?: string, region?: string) {
setLoginState("loading");
setPkceTimedOut(false);
setError(null);
// We need to reset the auth request state before attempting, otherwise
// an expected auth request cancellation will be presented to the user
// as an error.
setAuthRequestId(undefined);
const init = await Auth.startPkceAuthorization({
issuerUrl,
region,
}).catch((err) => {
setLoginState("not started");
setError(err.message);
console.error(err);
});
if (!init) return;
setAuthRequestId(init.authRequestId);
Native.open(init.url).catch((err) => {
console.error(err);
setError(
"Failed to open the browser. As an alternative, try logging in with device code.",
);
});
await Auth.finishPkceAuthorization({
authRequestId: init.authRequestId,
})
.then(() => {
Internal.sendWindowFocusRequest({});
if (tab == "iam") {
setShowProfileTab(true);
} else {
refreshAuth();
next();
}
})
.catch((err) => {
// If this promise was originally for some older request attempt,
// then we should just ignore the error.
if (currAuthRequestId() === init.authRequestId) {
setLoginState("not started");
setError(err.message);
console.error(err);
}
});
}
async function handleDeviceCodeAuth(startUrl?: string, region?: string) {
setLoginState("loading");
setError(null);
setLoginUrl(null);
setCopyToClipboardText("Copy to clipboard");
await Auth.cancelPkceAuthorization().catch((err) => {
console.error(err);
});
const init = await Auth.builderIdStartDeviceAuthorization({
startUrl,
region,
}).catch((err) => {
setLoginState("not started");
setLoginCode(null);
setError(err.message);
console.error(err);
});
if (!init) return;
setLoginCode(init.code);
setLoginUrl(init.url);
await Auth.builderIdPollCreateToken(init)
.then(() => {
setLoginState("logged in");
Internal.sendWindowFocusRequest({});
refreshAuth();
next();
})
.catch((err) => {
setLoginState("not started");
setLoginCode(null);
setError(err.message);
console.error(err);
});
}
useEffect(() => {
setLoginState(auth.authed ? "logged in" : "not started");
}, [auth]);
useEffect(() => {
if (loginState !== "logged in" || showProfileTab) return;
next();
}, [loginState, showProfileTab, next]);
return showProfileTab ? (
<ProfileTab
next={() => {
refreshAuth();
setLoginState("logged in");
setShowProfileTab(false);
}}
back={() => {
setLoginState("not started");
setShowProfileTab(false);
}}
/>
) : (
<div className="flex flex-col items-center gap-8 gradient-q-secondary-light -m-10 pt-10 p-4 rounded-lg text-white">
<div className="flex flex-col items-center gap-8">
<Lockup />
{!completedOnboarding && (
<h2 className="text-xl text-white font-semibold select-none leading-none font-ember tracking-tight">
Sign in to get started
</h2>
)}
{completedOnboarding && tab == "builderId" && (
<div className="text-center flex flex-col">
<div className="font-ember font-bold">
CodeWhisperer is now Amazon Q
</div>
<Link href={Q_MIGRATION_URL} className="text-sm">
Read the announcement blog post
</Link>
</div>
)}
</div>
{error && (
<div className="flex flex-col items-center gap-2 w-full bg-red-200 border border-red-600 rounded py-2 px-2">
<p className="text-black dark:text-white font-semibold text-center">
Failed to login
</p>
<p className="text-black dark:text-white text-center">{error}</p>
{loginMethod === "pkce" && loginState === "loading" && (
<Button
variant="ghost"
className="self-center mx-auto text-black hover:bg-white/40"
onClick={() => {
setLoginMethod("deviceCode");
setLoginState("not started");
setError(null);
}}
>
Login with Device Code
</Button>
)}
</div>
)}
<div className="flex flex-col items-center gap-4 text-white text-sm">
{loginState === "loading" && loginMethod === "pkce" ? (
<>
<p className="text-center w-80">
Waiting for authentication in the browser to complete...
</p>
{pkceTimedOut && (
<div className="text-center w-80">
<p>Browser not opening?</p>
<Button
variant="ghost"
className="h-auto p-1 px-2 hover:bg-white/20 hover:text-white italic"
onClick={() => {
setLoginMethod("deviceCode");
setLoginState("not started");
}}
>
Try authenticating with device code
</Button>
</div>
)}
<Button
variant="glass"
className="self-center w-32"
onClick={() => {
setLoginState("not started");
}}
>
Back
</Button>
</>
) : loginState === "loading" &&
loginMethod === "deviceCode" &&
loginCode &&
loginUrl ? (
<>
<p className="text-center w-80">
Confirm code <span className="font-bold">{loginCode}</span> in the
login page at the following link:
</p>
<p className="text-center">{loginUrl}</p>
<Button
variant="ghost"
className="h-auto p-1 px-2 hover:bg-white/20 hover:text-white"
onClick={() => {
navigator.clipboard.writeText(loginUrl);
setCopyToClipboardText("Copied!");
}}
>
{copyToClipboardText}
</Button>
<Button
variant="glass"
className="self-center w-32"
onClick={() => {
setLoginState("not started");
setLoginCode(null);
}}
>
Back
</Button>
</>
) : (
<Tab
tab={tab}
handleLogin={handleLogin}
toggleTab={
tab === "builderId"
? () => setTab("iam")
: () => setTab("builderId")
}
signInText={completedOnboarding ? "Log back in" : "Sign in"}
/>
)}
</div>
</div>
);
}