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

View File

@ -13,8 +13,8 @@ import { accountsAppURL, apiOrigin } from "@ente/shared/network/api";
import { getToken } from "@ente/shared/storage/localStorage/helpers";
/**
* Redirect user to Ente accounts app to authenticate using their second factor,
* a passkey they've configured.
* Construct a redirect URL to take the user to Ente accounts app to
* authenticate using their second factor, a passkey they've configured.
*
* On successful verification, the accounts app will redirect back to our
* `/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
* verification session.
*/
export const redirectUserToPasskeyVerificationFlow = (
export const passkeyVerificationRedirectURL = (
appName: AppName,
passkeySessionID: string,
) => {
@ -40,25 +40,35 @@ export const redirectUserToPasskeyVerificationFlow = (
redirect,
recover,
});
const url = `${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
// even if we move the passkey creation and authentication inline to within
// the Photos web app.
//
// 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
// open 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.
return `${accountsAppURL()}/passkeys/verify?${params.toString()}`;
};
/**
* Open or redirect to a passkey verification URL previously constructed using
* {@link passkeyVerificationRedirectURL}.
*
* @param url The URL to redirect to or open in the system browser.
*
* [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
* even if we move the passkey creation and authentication inline to within the
* Photos web app.
*
* 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 open
* 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);
else window.location.href = url;
};