Directly
This commit is contained in:
@@ -1,51 +0,0 @@
|
||||
import { setClientPackageForAuthenticatedRequests } from "@/next/http";
|
||||
import log from "@/next/log";
|
||||
import { VerticallyCentered } from "@ente/shared/components/Container";
|
||||
import EnteSpinner from "@ente/shared/components/EnteSpinner";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
import { LS_KEYS, getData, setData } from "@ente/shared/storage/localStorage";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
/**
|
||||
* Parse credentials passed as query parameters by one of our client apps, save
|
||||
* them to local storage, and then redirect to the passkeys listing.
|
||||
*/
|
||||
const Page: React.FC = () => {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
const clientPackage = urlParams.get("client");
|
||||
if (clientPackage) {
|
||||
// TODO-PK: mobile is not passing it. is that expected?
|
||||
localStorage.setItem("clientPackage", clientPackage);
|
||||
setClientPackageForAuthenticatedRequests(clientPackage);
|
||||
HTTPService.setHeaders({
|
||||
"X-Client-Package": clientPackage,
|
||||
});
|
||||
}
|
||||
|
||||
const token = urlParams.get("token");
|
||||
if (!token) {
|
||||
log.error("Missing accounts token");
|
||||
router.push("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
const user = getData(LS_KEYS.USER) || {};
|
||||
user.token = token;
|
||||
setData(LS_KEYS.USER, user);
|
||||
|
||||
router.push("/passkeys");
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<VerticallyCentered>
|
||||
<EnteSpinner />
|
||||
</VerticallyCentered>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
@@ -41,20 +41,26 @@ const Page: React.FC = () => {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const refreshPasskeys = async () => {
|
||||
try {
|
||||
setPasskeys(await getPasskeys());
|
||||
} catch (e) {
|
||||
log.error("Failed to fetch passkeys", e);
|
||||
setDialogBoxAttributesV2({
|
||||
title: t("ERROR"),
|
||||
content: t("passkey_fetch_failed"),
|
||||
close: {},
|
||||
useEffect(() => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
const clientPackage = urlParams.get("client");
|
||||
if (clientPackage) {
|
||||
// TODO-PK: mobile is not passing it. is that expected?
|
||||
localStorage.setItem("clientPackage", clientPackage);
|
||||
setClientPackageForAuthenticatedRequests(clientPackage);
|
||||
HTTPService.setHeaders({
|
||||
"X-Client-Package": clientPackage,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const token = urlParams.get("token");
|
||||
if (!token) {
|
||||
log.error("Missing accounts token");
|
||||
router.push("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!getToken()) {
|
||||
router.push("/login");
|
||||
return;
|
||||
@@ -64,6 +70,23 @@ const Page: React.FC = () => {
|
||||
void refreshPasskeys();
|
||||
}, []);
|
||||
|
||||
const refreshPasskeys = async () => {
|
||||
try {
|
||||
setPasskeys(await getPasskeys());
|
||||
} catch (e) {
|
||||
log.error("Failed to fetch passkeys", e);
|
||||
showPasskeyFetchFailedErrorDialog();
|
||||
}
|
||||
};
|
||||
|
||||
const showPasskeyFetchFailedErrorDialog = () => {
|
||||
setDialogBoxAttributesV2({
|
||||
title: t("ERROR"),
|
||||
content: t("passkey_fetch_failed"),
|
||||
close: {},
|
||||
});
|
||||
};
|
||||
|
||||
const handleSelectPasskey = (passkey: Passkey) => {
|
||||
setSelectedPasskey(passkey);
|
||||
setShowPasskeyDrawer(true);
|
||||
|
||||
@@ -428,7 +428,6 @@ const UtilitySection: React.FC<UtilitySectionProps> = ({ closeSidebar }) => {
|
||||
const router = useRouter();
|
||||
const appContext = useContext(AppContext);
|
||||
const {
|
||||
appName,
|
||||
setDialogMessage,
|
||||
startLoading,
|
||||
watchFolderView,
|
||||
@@ -473,7 +472,7 @@ const UtilitySection: React.FC<UtilitySectionProps> = ({ closeSidebar }) => {
|
||||
closeSidebar();
|
||||
|
||||
try {
|
||||
await openAccountsManagePasskeysPage(appName);
|
||||
await openAccountsManagePasskeysPage();
|
||||
} catch (e) {
|
||||
log.error("failed to redirect to accounts page", e);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ some operating system restrictions.
|
||||
|
||||
## Getting to the passkeys manager
|
||||
|
||||
As of Feb 2024, Ente clients have a button to navigate to a WebView of Ente
|
||||
As of Jun 2024, Ente clients have a button to navigate to a WebView of Ente
|
||||
Accounts. Ente Accounts allows users to add and manage their registered
|
||||
passkeys.
|
||||
passkeys, and later authenticate with them as a second factor.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
@@ -55,12 +55,10 @@ used.** This restriction is a byproduct of the enablement for automatic login.
|
||||
### Automatically logging into Ente Accounts
|
||||
|
||||
Clients open a WebView with the URL
|
||||
`https://accounts.ente.io/passkeys/handoff?token=<accountsToken>`. This page
|
||||
will parse the token for usage in subsequent Accounts-related API calls.
|
||||
`https://accounts.ente.io/passkeys?token=<accountsToken>`.
|
||||
|
||||
If the token is valid, the user will be automatically redirected to the passkeys
|
||||
management page. Otherwise, they will be required to login with their Ente
|
||||
credentials.
|
||||
If the token is valid, the user will be show a list of their passkeys, and they
|
||||
can edit / delete them, or add new ones.
|
||||
|
||||
## Registering a WebAuthn credential
|
||||
|
||||
@@ -128,7 +126,7 @@ func (u *PasskeyUser) WebAuthnCredentials() []webauthn.Credential {
|
||||
"publicKey": {
|
||||
"rp": {
|
||||
"name": "Ente",
|
||||
"id": "accounts.ente.io"
|
||||
"id": "ente.io"
|
||||
},
|
||||
"user": {
|
||||
"name": "james@example.org",
|
||||
@@ -335,15 +333,12 @@ if (passkeySessionID) {
|
||||
}
|
||||
```
|
||||
|
||||
The client should redirect the user to Accounts with this session ID to prompt
|
||||
credential authentication. We use Accounts as the central WebAuthn hub since it
|
||||
is needed anyways to service credential authentication from mobile clients, so
|
||||
we use the same flow for other (web, desktop) clients too.
|
||||
The client should redirect the user to the Ente Accounts web app with this
|
||||
session ID to prompt credential authentication. We use Accounts as the central
|
||||
WebAuthn hub since it allows us to handle mobile and desktop clients too.
|
||||
|
||||
```tsx
|
||||
window.location.href = `${accountsAppURL()}/passkeys/verify?passkeySessionID=${passkeySessionID}&clientPackage=io.ente.photos.web&redirect=${
|
||||
window.location.origin
|
||||
}/passkeys/finish`;
|
||||
window.location.href = `${accountsAppURL()}/passkeys/verify?passkeySessionID=${passkeySessionID}&clientPackage=io.ente.photos.web&redirect=${window.location.origin}/passkeys/finish`;
|
||||
```
|
||||
|
||||
### Requesting publicKey options (begin)
|
||||
@@ -367,7 +362,7 @@ window.location.href = `${accountsAppURL()}/passkeys/verify?passkeySessionID=${p
|
||||
"publicKey": {
|
||||
"challenge": "dF-mmdZSBxP6Z7OhZrmQ4h-k-BkuuX6ERnW_ckYdkvc",
|
||||
"timeout": 300000,
|
||||
"rpId": "accounts.ente.io",
|
||||
"rpId": "ente.io",
|
||||
"allowCredentials": [
|
||||
{
|
||||
"type": "public-key",
|
||||
|
||||
@@ -69,11 +69,12 @@ export const redirectUserToPasskeyVerificationFlow = (
|
||||
*
|
||||
* @param appName The {@link AppName} of the app which is calling this function.
|
||||
*/
|
||||
export const openAccountsManagePasskeysPage = async (appName: AppName) => {
|
||||
// check if the user has passkey recovery enabled
|
||||
export const openAccountsManagePasskeysPage = async () => {
|
||||
// Check if the user has passkey recovery enabled
|
||||
const recoveryEnabled = await isPasskeyRecoveryEnabled();
|
||||
if (!recoveryEnabled) {
|
||||
// let's create the necessary recovery information
|
||||
// If not, enable it for them by creating the necessary recovery
|
||||
// information to prevent them from getting locked out.
|
||||
const recoveryKey = await getRecoveryKey();
|
||||
|
||||
const resetSecret = await generateEncryptionKey();
|
||||
@@ -91,11 +92,12 @@ export const openAccountsManagePasskeysPage = async (appName: AppName) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Redirect to the Ente Accounts app where they can view and add and manage
|
||||
// their passkeys.
|
||||
const token = await getAccountsToken();
|
||||
const client = clientPackageName[appName];
|
||||
const params = new URLSearchParams({ token, client });
|
||||
const params = new URLSearchParams({ token });
|
||||
|
||||
window.open(`${accountsAppURL()}/passkeys/handoff?${params.toString()}`);
|
||||
window.open(`${accountsAppURL()}/passkeys?${params.toString()}`);
|
||||
};
|
||||
|
||||
export const isPasskeyRecoveryEnabled = async () => {
|
||||
|
||||
Reference in New Issue
Block a user