Handle retrying redirects

This commit is contained in:
Manav Rathi 2024-06-13 11:15:47 +05:30
parent 9086d37a7c
commit cd76da836d
No known key found for this signature in database
2 changed files with 60 additions and 34 deletions

View File

@ -45,7 +45,10 @@ import { useRouter } from "next/router";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { getSRPAttributes } from "../api/srp"; import { getSRPAttributes } from "../api/srp";
import { PAGES } from "../constants/pages"; import { PAGES } from "../constants/pages";
import { redirectUserToPasskeyVerificationFlow } from "../services/passkey"; import {
openPasskeyVerificationURL,
passkeyVerificationRedirectURL,
} from "../services/passkey";
import { appHomeRoute } from "../services/redirect"; import { appHomeRoute } from "../services/redirect";
import { import {
configureSRP, configureSRP,
@ -61,6 +64,9 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
const [srpAttributes, setSrpAttributes] = useState<SRPAttributes>(); const [srpAttributes, setSrpAttributes] = useState<SRPAttributes>();
const [keyAttributes, setKeyAttributes] = useState<KeyAttributes>(); const [keyAttributes, setKeyAttributes] = useState<KeyAttributes>();
const [user, setUser] = useState<User>(); const [user, setUser] = useState<User>();
const [passkeyVerificationURL, setPasskeyVerificationURL] = useState<
string | undefined
>();
const router = useRouter(); const router = useRouter();
useEffect(() => { useEffect(() => {
@ -168,10 +174,12 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
isTwoFactorPasskeysEnabled: true, isTwoFactorPasskeysEnabled: true,
}); });
InMemoryStore.set(MS_KEYS.REDIRECT_URL, PAGES.ROOT); InMemoryStore.set(MS_KEYS.REDIRECT_URL, PAGES.ROOT);
redirectUserToPasskeyVerificationFlow( const url = passkeyVerificationRedirectURL(
appName, appName,
passkeySessionID, passkeySessionID,
); );
setPasskeyVerificationURL(url);
openPasskeyVerificationURL(url);
throw Error(CustomError.TWO_FACTOR_ENABLED); throw Error(CustomError.TWO_FACTOR_ENABLED);
} else if (twoFactorSessionID) { } else if (twoFactorSessionID) {
const sessionKeyAttributes = const sessionKeyAttributes =
@ -266,13 +274,23 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
); );
} }
return ( if (passkeyVerificationURL) {
<VerifyingPasskey // We reach this case only when running in the desktop app, because in
email={user?.email} // the web app we already would've redirected to passkeyVerificationURL.
onRecover={redirectToRecoverPage} //
onLogout={logout} // See: [Note: Passkey verification in the desktop app]
/>
); return (
<VerifyingPasskey
email={user?.email}
onRetry={() =>
openPasskeyVerificationURL(passkeyVerificationURL)
}
onRecover={redirectToRecoverPage}
onLogout={logout}
/>
);
}
// TODO: Handle the case when user is not present, or exclude that // TODO: Handle the case when user is not present, or exclude that
// possibility using types. // possibility using types.
@ -319,9 +337,7 @@ const PasswordHeader: React.FC<React.PropsWithChildren> = ({ children }) => {
const PasskeyHeader: React.FC<React.PropsWithChildren> = ({ children }) => { const PasskeyHeader: React.FC<React.PropsWithChildren> = ({ children }) => {
return ( return (
<Header_> <Header_>
<Typography variant="h3" > <Typography variant="h3">{"Passkey"}</Typography>
{"Passkey"}
</Typography>
<Typography color="text.faint">{children}</Typography> <Typography color="text.faint">{children}</Typography>
</Header_> </Header_>
); );

View File

@ -13,8 +13,8 @@ import { accountsAppURL, apiOrigin } from "@ente/shared/network/api";
import { getToken } from "@ente/shared/storage/localStorage/helpers"; import { getToken } from "@ente/shared/storage/localStorage/helpers";
/** /**
* Redirect user to Ente accounts app to authenticate using their second factor, * Construct a redirect URL to take the user to Ente accounts app to
* a passkey they've configured. * authenticate using their second factor, a passkey they've configured.
* *
* On successful verification, the accounts app will redirect back to our * On successful verification, the accounts app will redirect back to our
* `/passkeys/finish` page. * `/passkeys/finish` page.
@ -24,7 +24,7 @@ import { getToken } from "@ente/shared/storage/localStorage/helpers";
* @param passkeySessionID An identifier provided by museum for this passkey * @param passkeySessionID An identifier provided by museum for this passkey
* verification session. * verification session.
*/ */
export const redirectUserToPasskeyVerificationFlow = ( export const passkeyVerificationRedirectURL = (
appName: AppName, appName: AppName,
passkeySessionID: string, passkeySessionID: string,
) => { ) => {
@ -40,25 +40,35 @@ export const redirectUserToPasskeyVerificationFlow = (
redirect, redirect,
recover, recover,
}); });
const url = `${accountsAppURL()}/passkeys/verify?${params.toString()}`; return `${accountsAppURL()}/passkeys/verify?${params.toString()}`;
// [Note: Passkey verification in the desktop app] };
//
// Our desktop app bundles the web app and serves it over a custom protocol. /**
// Passkeys are tied to origins, and will not work with this custom protocol * Open or redirect to a passkey verification URL previously constructed using
// even if we move the passkey creation and authentication inline to within * {@link passkeyVerificationRedirectURL}.
// the Photos web app. *
// * @param url The URL to redirect to or open in the system browser.
// Thus, passkey creation and authentication in the desktop app works the *
// same way it works in the mobile app - the system browser is invoked to * [Note: Passkey verification in the desktop app]
// open accounts.ente.io. *
// * Our desktop app bundles the web app and serves it over a custom protocol.
// - For passkey creation, this is a one-way open. Passkeys get created at * Passkeys are tied to origins, and will not work with this custom protocol
// accounts.ente.io, and that's it. * even if we move the passkey creation and authentication inline to within the
// * Photos web app.
// - For passkey verification, the flow is two-way. We register a custom *
// protocol and provide that as a return path redirect. Passkey * Thus, passkey creation and authentication in the desktop app works the same
// authentication happens at accounts.ente.io, and on success there is * way it works in the mobile app - the system browser is invoked to open
// redirected back to the desktop app. * accounts.ente.io.
*
* - For passkey creation, this is a one-way open. Passkeys get created at
* accounts.ente.io, and that's it.
*
* - For passkey verification, the flow is two-way. We register a custom
* protocol and provide that as a return path redirect. Passkey
* authentication happens at accounts.ente.io, and on success there is
* redirected back to the desktop app.
*/
export const openPasskeyVerificationURL = (url: string) => {
if (globalThis.electron) window.open(url); if (globalThis.electron) window.open(url);
else window.location.href = url; else window.location.href = url;
}; };