From 7fd2e58fc8380f9a506ffb9f919a981c7fd344c9 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 26 Nov 2024 06:48:19 +0530 Subject: [PATCH 1/8] Typed fetch --- web/apps/cast/src/services/pair.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/web/apps/cast/src/services/pair.ts b/web/apps/cast/src/services/pair.ts index cc30ef1d1c..59d02851fc 100644 --- a/web/apps/cast/src/services/pair.ts +++ b/web/apps/cast/src/services/pair.ts @@ -1,7 +1,10 @@ import { boxSealOpen, generateKeyPair } from "@/base/crypto/libsodium"; +import { clientPackageHeader, ensureOk } from "@/base/http"; import log from "@/base/log"; +import { apiURL } from "@/base/origins"; import { wait } from "@/utils/promise"; import castGateway from "@ente/shared/network/cast"; +import { z } from "zod"; export interface Registration { /** A pairing code shown on the screen. A client can use this to connect. */ @@ -107,8 +110,7 @@ export const getCastData = async (registration: Registration) => { // The client will send us the encrypted payload using our public key that // we registered with museum. - const encryptedCastData = await castGateway.getCastData(pairingCode); - if (!encryptedCastData) return; + const encryptedCastData = await getEncryptedCastData(pairingCode); // Decrypt it using the private key of the pair and return the plaintext // payload, which'll be a JSON object containing the data we need to start a @@ -123,3 +125,16 @@ export const getCastData = async (registration: Registration) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return JSON.parse(atob(decryptedCastData)); }; + +/** + * Fetch encrypted cast data corresponding to the given {@link code} from + * remote. + */ +const getEncryptedCastData = async (code: string) => { + const res = await fetch(await apiURL(`/cast/cast-data/${code}`), { + headers: clientPackageHeader(), + }); + ensureOk(res); + return z.object({ encCastData: z.string() }).parse(await res.json()) + .encCastData; +}; From 2b36a3e82acffc13e8e3854d5867264860465eb0 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 26 Nov 2024 07:03:40 +0530 Subject: [PATCH 2/8] Doc and rearrange --- web/packages/base/crypto/libsodium.ts | 99 ++++++++++++++++----------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/web/packages/base/crypto/libsodium.ts b/web/packages/base/crypto/libsodium.ts index 177a28ce33..de70f4e99a 100644 --- a/web/packages/base/crypto/libsodium.ts +++ b/web/packages/base/crypto/libsodium.ts @@ -663,6 +663,64 @@ export async function completeChunkHashing(hashState: sodium.StateAddress) { return hashString; } +/** + * Generate a new public/private keypair for use with the boxSeal* functions, + * and return their base64 string representations. + * + * These keys are suitable for being used with the {@link boxSeal} and + * {@link boxSealOpen} functions. + */ +export const generateKeyPair = async () => { + await sodium.ready; + const keyPair = sodium.crypto_box_keypair(); + return { + publicKey: await toB64(keyPair.publicKey), + privateKey: await toB64(keyPair.privateKey), + }; +}; + +/** + * Public key encryption. + * + * Encrypt the given {@link data} using the given {@link publicKey}. + * + * This function performs asymmetric (public-key) encryption. To decrypt the + * result, use {@link boxSealOpen}. + * + * @param data The input data to encrypt, represented as a base64 string. + * + * @param publicKey The public key to use for encryption (as a base64 string). + * + * @returns The encrypted data (as a base64 string). + */ +export const boxSeal = async (data: string, publicKey: string) => { + await sodium.ready; + return toB64( + sodium.crypto_box_seal(await fromB64(data), await fromB64(publicKey)), + ); +}; + +/** + * Decrypt the result of {@link boxSeal}. + * + * All parameters, and the result, are base64 string representations of the + * underlying data. + */ +export const boxSealOpen = async ( + input: string, + publicKey: string, + secretKey: string, +) => { + await sodium.ready; + return toB64( + sodium.crypto_box_seal_open( + await fromB64(input), + await fromB64(publicKey), + await fromB64(secretKey), + ), + ); +}; + export async function deriveKey( passphrase: string, salt: string, @@ -732,47 +790,6 @@ export async function generateSaltToDeriveKey() { return await toB64(sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES)); } -/** - * Generate a new public/private keypair, and return their base64 - * representations. - */ -export const generateKeyPair = async () => { - await sodium.ready; - const keyPair = sodium.crypto_box_keypair(); - return { - publicKey: await toB64(keyPair.publicKey), - privateKey: await toB64(keyPair.privateKey), - }; -}; - -export async function boxSealOpen( - input: string, - publicKey: string, - secretKey: string, -) { - await sodium.ready; - return await toB64( - sodium.crypto_box_seal_open( - await fromB64(input), - await fromB64(publicKey), - await fromB64(secretKey), - ), - ); -} - -/** - * Encrypt the given {@link input} using the given {@link publicKey}. - * - * This function performs asymmetric (public-key) encryption. To decrypt the - * result, use {@link boxSealOpen}. - */ -export async function boxSeal(input: string, publicKey: string) { - await sodium.ready; - return await toB64( - sodium.crypto_box_seal(await fromB64(input), await fromB64(publicKey)), - ); -} - export async function generateSubKey( key: string, subKeyLength: number, From e36aad9f7c19fc5083db82e3d20e3e57325c9c98 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 26 Nov 2024 07:25:57 +0530 Subject: [PATCH 3/8] Forward --- web/packages/base/crypto/ente-impl.ts | 6 +++++ web/packages/base/crypto/index.ts | 39 ++++++++++++++++++++++----- web/packages/base/crypto/libsodium.ts | 4 +-- web/packages/base/crypto/worker.ts | 15 +++-------- 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/web/packages/base/crypto/ente-impl.ts b/web/packages/base/crypto/ente-impl.ts index 82b5e41152..a04c0fb0ca 100644 --- a/web/packages/base/crypto/ente-impl.ts +++ b/web/packages/base/crypto/ente-impl.ts @@ -83,3 +83,9 @@ export const _decryptMetadataJSON = async (r: { }, r.keyB64, ); + +export const _generateKeyPair = libsodium.generateKeyPair; + +export const _boxSeal = libsodium.boxSeal; + +export const _boxSealOpen = libsodium.boxSealOpen; diff --git a/web/packages/base/crypto/index.ts b/web/packages/base/crypto/index.ts index f3f69e4565..8ad977b882 100644 --- a/web/packages/base/crypto/index.ts +++ b/web/packages/base/crypto/index.ts @@ -1,6 +1,9 @@ /** * @file Higher level functions that use the ontology of Ente's requirements. * + * For more detailed documentation of specific functions, see the corresponding + * function in `libsodium.ts`. + * * [Note: Crypto code hierarchy] * * 1. @/base/crypto (Crypto API for our code) @@ -172,8 +175,6 @@ export const encryptThumbnail = (data: BytesOrB64, key: BytesOrB64) => /** * Encrypt the given data using chunked streaming encryption, but process all * the chunks in one go. - * - * For more details, see {@link encryptStreamBytes} in `libsodium.ts`. */ export const encryptStreamBytes = async (data: Uint8Array, key: BytesOrB64) => inWorker() @@ -182,8 +183,6 @@ export const encryptStreamBytes = async (data: Uint8Array, key: BytesOrB64) => /** * Prepare for chunked streaming encryption using {@link encryptStreamChunk}. - * - * For more details, see {@link initChunkEncryption} in `libsodium.ts`. */ export const initChunkEncryption = async (key: BytesOrB64) => inWorker() @@ -192,8 +191,6 @@ export const initChunkEncryption = async (key: BytesOrB64) => /** * Encrypt a chunk as part of a chunked streaming encryption. - * - * For more details, see {@link encryptStreamChunk} in `libsodium.ts`. */ export const encryptStreamChunk = async ( data: Uint8Array, @@ -346,3 +343,33 @@ export const decryptMetadataJSON = (r: { inWorker() ? ei._decryptMetadataJSON(r) : sharedCryptoWorker().then((w) => w.decryptMetadataJSON(r)); + +/** + * Generate a new public/private keypair for use with the boxSeal* functions. + */ +export const generateKeyPair = async () => + inWorker() + ? ei._generateKeyPair() + : sharedCryptoWorker().then((w) => w.generateKeyPair()); + +/** + * Public key encryption. + */ +export const boxSeal = async (data: string, publicKey: string) => + inWorker() + ? ei._boxSeal(data, publicKey) + : sharedCryptoWorker().then((w) => w.boxSeal(data, publicKey)); + +/** + * Decrypt the result of {@link boxSeal}. + */ +export const boxSealOpen = async ( + encryptedData: string, + publicKey: string, + secretKey: string, +) => + inWorker() + ? ei._boxSealOpen(encryptedData, publicKey, secretKey) + : sharedCryptoWorker().then((w) => + w.boxSealOpen(encryptedData, publicKey, secretKey), + ); diff --git a/web/packages/base/crypto/libsodium.ts b/web/packages/base/crypto/libsodium.ts index de70f4e99a..d10bd10a78 100644 --- a/web/packages/base/crypto/libsodium.ts +++ b/web/packages/base/crypto/libsodium.ts @@ -707,14 +707,14 @@ export const boxSeal = async (data: string, publicKey: string) => { * underlying data. */ export const boxSealOpen = async ( - input: string, + encryptedData: string, publicKey: string, secretKey: string, ) => { await sodium.ready; return toB64( sodium.crypto_box_seal_open( - await fromB64(input), + await fromB64(encryptedData), await fromB64(publicKey), await fromB64(secretKey), ), diff --git a/web/packages/base/crypto/worker.ts b/web/packages/base/crypto/worker.ts index 265dbe1c68..a5a16fc767 100644 --- a/web/packages/base/crypto/worker.ts +++ b/web/packages/base/crypto/worker.ts @@ -33,6 +33,9 @@ export class CryptoWorker { decryptStreamChunk = ei._decryptStreamChunk; decryptMetadataJSON_New = ei._decryptMetadataJSON_New; decryptMetadataJSON = ei._decryptMetadataJSON; + generateKeyPair = ei._generateKeyPair; + boxSeal = ei._boxSeal; + boxSealOpen = ei._boxSealOpen; // TODO: -- AUDIT BELOW -- @@ -93,18 +96,6 @@ export class CryptoWorker { return libsodium.generateSaltToDeriveKey(); } - async generateKeyPair() { - return libsodium.generateKeyPair(); - } - - async boxSealOpen(input: string, publicKey: string, secretKey: string) { - return libsodium.boxSealOpen(input, publicKey, secretKey); - } - - async boxSeal(input: string, publicKey: string) { - return libsodium.boxSeal(input, publicKey); - } - async generateSubKey( key: string, subKeyLength: number, From a59bb780ee71ee2532b25a07afbd083dc7bcc5ad Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 26 Nov 2024 07:29:06 +0530 Subject: [PATCH 4/8] Use --- web/apps/cast/src/services/pair.ts | 2 +- .../photos/src/components/Collections/AlbumCastDialog.tsx | 2 +- web/packages/base/crypto/index.ts | 2 +- web/packages/base/crypto/libsodium.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/apps/cast/src/services/pair.ts b/web/apps/cast/src/services/pair.ts index 59d02851fc..37701fbced 100644 --- a/web/apps/cast/src/services/pair.ts +++ b/web/apps/cast/src/services/pair.ts @@ -1,4 +1,4 @@ -import { boxSealOpen, generateKeyPair } from "@/base/crypto/libsodium"; +import { boxSealOpen, generateKeyPair } from "@/base/crypto"; import { clientPackageHeader, ensureOk } from "@/base/http"; import log from "@/base/log"; import { apiURL } from "@/base/origins"; diff --git a/web/apps/photos/src/components/Collections/AlbumCastDialog.tsx b/web/apps/photos/src/components/Collections/AlbumCastDialog.tsx index 7b33518870..28b82836ad 100644 --- a/web/apps/photos/src/components/Collections/AlbumCastDialog.tsx +++ b/web/apps/photos/src/components/Collections/AlbumCastDialog.tsx @@ -1,6 +1,6 @@ import { TitledMiniDialog } from "@/base/components/MiniDialog"; import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator"; -import { boxSeal } from "@/base/crypto/libsodium"; +import { boxSeal } from "@/base/crypto"; import log from "@/base/log"; import type { Collection } from "@/media/collection"; import { photosDialogZIndex } from "@/new/photos/components/utils/z-index"; diff --git a/web/packages/base/crypto/index.ts b/web/packages/base/crypto/index.ts index 8ad977b882..a333c5ef92 100644 --- a/web/packages/base/crypto/index.ts +++ b/web/packages/base/crypto/index.ts @@ -345,7 +345,7 @@ export const decryptMetadataJSON = (r: { : sharedCryptoWorker().then((w) => w.decryptMetadataJSON(r)); /** - * Generate a new public/private keypair for use with the boxSeal* functions. + * Generate a new public/private keypair. */ export const generateKeyPair = async () => inWorker() diff --git a/web/packages/base/crypto/libsodium.ts b/web/packages/base/crypto/libsodium.ts index d10bd10a78..45caed99eb 100644 --- a/web/packages/base/crypto/libsodium.ts +++ b/web/packages/base/crypto/libsodium.ts @@ -664,8 +664,8 @@ export async function completeChunkHashing(hashState: sodium.StateAddress) { } /** - * Generate a new public/private keypair for use with the boxSeal* functions, - * and return their base64 string representations. + * Generate a new public/private keypair for use with public-key encryption + * functions, and return their base64 string representations. * * These keys are suitable for being used with the {@link boxSeal} and * {@link boxSealOpen} functions. From 139d21949c4995f209ff5db7ab99cb76479fca5e Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 26 Nov 2024 08:35:06 +0530 Subject: [PATCH 5/8] Fix --- web/apps/cast/src/services/pair.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/web/apps/cast/src/services/pair.ts b/web/apps/cast/src/services/pair.ts index 37701fbced..8bdb446f4b 100644 --- a/web/apps/cast/src/services/pair.ts +++ b/web/apps/cast/src/services/pair.ts @@ -3,6 +3,7 @@ import { clientPackageHeader, ensureOk } from "@/base/http"; import log from "@/base/log"; import { apiURL } from "@/base/origins"; import { wait } from "@/utils/promise"; +import { nullToUndefined } from "@/utils/transform"; import castGateway from "@ente/shared/network/cast"; import { z } from "zod"; @@ -111,6 +112,7 @@ export const getCastData = async (registration: Registration) => { // The client will send us the encrypted payload using our public key that // we registered with museum. const encryptedCastData = await getEncryptedCastData(pairingCode); + if (!encryptedCastData) return; // Decrypt it using the private key of the pair and return the plaintext // payload, which'll be a JSON object containing the data we need to start a @@ -127,14 +129,19 @@ export const getCastData = async (registration: Registration) => { }; /** - * Fetch encrypted cast data corresponding to the given {@link code} from - * remote. + * Fetch encrypted cast data corresponding to the given {@link code} from remote + * if a client has already paired using it. */ const getEncryptedCastData = async (code: string) => { const res = await fetch(await apiURL(`/cast/cast-data/${code}`), { headers: clientPackageHeader(), }); ensureOk(res); - return z.object({ encCastData: z.string() }).parse(await res.json()) - .encCastData; + return z + .object({ + // encCastData will be null if pairing hasn't happened yet for the + // given code. + encCastData: z.string().nullish().transform(nullToUndefined), + }) + .parse(await res.json()).encCastData; }; From 72fb7e730779b57641ad568ec1e897310bd84b82 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 26 Nov 2024 08:37:36 +0530 Subject: [PATCH 6/8] Dialog tweaks --- .../Collections/AlbumCastDialog.tsx | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/web/apps/photos/src/components/Collections/AlbumCastDialog.tsx b/web/apps/photos/src/components/Collections/AlbumCastDialog.tsx index 28b82836ad..fd3b82b0df 100644 --- a/web/apps/photos/src/components/Collections/AlbumCastDialog.tsx +++ b/web/apps/photos/src/components/Collections/AlbumCastDialog.tsx @@ -188,21 +188,23 @@ export const AlbumCastDialog: React.FC = ({ )} {view == "pin" && ( <> - - - ), - }} - values={{ url: "cast.ente.io" }} - /> - - {t("enter_cast_pin_code")} + + + + ), + }} + values={{ url: "cast.ente.io" }} + /> + + {t("enter_cast_pin_code")} + = ({ buttonText={t("pair_device_to_tv")} submitButtonProps={{ sx: { mt: 1, mb: 2 } }} /> - From 8cd0e6186fb1a1e181979b4c496cf5fe79c6bd2f Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 26 Nov 2024 09:12:27 +0530 Subject: [PATCH 7/8] Re --- web/apps/cast/src/pages/index.tsx | 19 +++++++----- web/apps/cast/src/services/pair.ts | 46 +++++++++++++++++++++-------- web/packages/shared/network/cast.ts | 10 ------- 3 files changed, 45 insertions(+), 30 deletions(-) diff --git a/web/apps/cast/src/pages/index.tsx b/web/apps/cast/src/pages/index.tsx index c3782be674..c89313d10e 100644 --- a/web/apps/cast/src/pages/index.tsx +++ b/web/apps/cast/src/pages/index.tsx @@ -9,8 +9,8 @@ import { getCastData, register } from "services/pair"; import { advertiseOnChromecast } from "../services/chromecast-receiver"; export default function Index() { - const [publicKeyB64, setPublicKeyB64] = useState(); - const [privateKeyB64, setPrivateKeyB64] = useState(); + const [publicKey, setPublicKey] = useState(); + const [privateKey, setPrivateKey] = useState(); const [pairingCode, setPairingCode] = useState(); const router = useRouter(); @@ -18,8 +18,8 @@ export default function Index() { useEffect(() => { if (!pairingCode) { void register().then((r) => { - setPublicKeyB64(r.publicKeyB64); - setPrivateKeyB64(r.privateKeyB64); + setPublicKey(r.publicKey); + setPrivateKey(r.privateKey); setPairingCode(r.pairingCode); }); } else { @@ -31,14 +31,17 @@ export default function Index() { }, [pairingCode]); useEffect(() => { - if (!publicKeyB64 || !privateKeyB64 || !pairingCode) return; + if (!publicKey || !privateKey || !pairingCode) return; const pollTick = async () => { - const registration = { publicKeyB64, privateKeyB64, pairingCode }; try { // TODO: // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const data = await getCastData(registration); + const data = await getCastData({ + publicKey, + privateKey, + pairingCode, + }); if (!data) { // No one has connected yet. return; @@ -58,7 +61,7 @@ export default function Index() { const interval = setInterval(pollTick, 2000); return () => clearInterval(interval); - }, [publicKeyB64, privateKeyB64, pairingCode, router]); + }, [publicKey, privateKey, pairingCode, router]); return ( diff --git a/web/apps/cast/src/services/pair.ts b/web/apps/cast/src/services/pair.ts index 8bdb446f4b..945436a638 100644 --- a/web/apps/cast/src/services/pair.ts +++ b/web/apps/cast/src/services/pair.ts @@ -4,16 +4,21 @@ import log from "@/base/log"; import { apiURL } from "@/base/origins"; import { wait } from "@/utils/promise"; import { nullToUndefined } from "@/utils/transform"; -import castGateway from "@ente/shared/network/cast"; import { z } from "zod"; export interface Registration { /** A pairing code shown on the screen. A client can use this to connect. */ pairingCode: string; - /** The public part of the keypair we registered with the server. */ - publicKeyB64: string; - /** The private part of the keypair we registered with the server. */ - privateKeyB64: string; + /** + * A base64 string representation of the public part of the keypair we + * registered with the server. + */ + publicKey: string; + /** + * A base64 string representation of the private part of the keypair we + * registered with the server. + */ + privateKey: string; } /** @@ -78,14 +83,13 @@ export interface Registration { */ export const register = async (): Promise => { // Generate keypair. - const { publicKey: publicKeyB64, privateKey: privateKeyB64 } = - await generateKeyPair(); + const { publicKey, privateKey } = await generateKeyPair(); // Register keypair with museum to get a pairing code. let pairingCode: string | undefined; while (true) { try { - pairingCode = await castGateway.registerDevice(publicKeyB64); + pairingCode = await registerDevice(publicKey); } catch (e) { log.error("Failed to register public key with server", e); } @@ -94,7 +98,25 @@ export const register = async (): Promise => { await wait(10000); } - return { pairingCode, publicKeyB64, privateKeyB64 }; + return { pairingCode, publicKey, privateKey }; +}; + +/** + * Register the given {@link publicKey} with remote. + * + * @returns A device code that can be used to pair with us. + */ +const registerDevice = async (publicKey: string) => { + const res = await fetch(await apiURL("/cast/device-info/"), { + method: "POST", + headers: clientPackageHeader(), + body: JSON.stringify({ + publicKey, + }), + }); + ensureOk(res); + return z.object({ deviceCode: z.string() }).parse(await res.json()) + .deviceCode; }; /** @@ -107,7 +129,7 @@ export const register = async (): Promise => { * See: [Note: Pairing protocol]. */ export const getCastData = async (registration: Registration) => { - const { pairingCode, publicKeyB64, privateKeyB64 } = registration; + const { pairingCode, publicKey, privateKey } = registration; // The client will send us the encrypted payload using our public key that // we registered with museum. @@ -119,8 +141,8 @@ export const getCastData = async (registration: Registration) => { // slideshow for some collection. const decryptedCastData = await boxSealOpen( encryptedCastData, - publicKeyB64, - privateKeyB64, + publicKey, + privateKey, ); // TODO: diff --git a/web/packages/shared/network/cast.ts b/web/packages/shared/network/cast.ts index 226696371f..3c45b0aec3 100644 --- a/web/packages/shared/network/cast.ts +++ b/web/packages/shared/network/cast.ts @@ -56,16 +56,6 @@ class CastGateway { return resp.data.publicKey; } - public async registerDevice(publicKey: string): Promise { - const resp = await HTTPService.post( - await apiURL("/cast/device-info/"), - { - publicKey: publicKey, - }, - ); - return resp.data.deviceCode; - } - public async publishCastPayload( code: string, castPayload: string, From 76c65a92e3d7f6a5692b5a88b2ffac4d9af3ef7d Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 26 Nov 2024 09:14:41 +0530 Subject: [PATCH 8/8] Re --- web/apps/accounts/src/services/passkey.ts | 4 ++-- web/apps/cast/src/services/pair.ts | 6 +++--- web/packages/accounts/services/passkey.ts | 4 ++-- web/packages/base/http.ts | 8 +++++--- web/packages/gallery/services/download.ts | 12 ++++++------ 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/web/apps/accounts/src/services/passkey.ts b/web/apps/accounts/src/services/passkey.ts index 632cd590cc..fa6eae5f1a 100644 --- a/web/apps/accounts/src/services/passkey.ts +++ b/web/apps/accounts/src/services/passkey.ts @@ -5,7 +5,7 @@ import { toB64URLSafeNoPaddingString, } from "@/base/crypto/libsodium"; import { isDevBuild } from "@/base/env"; -import { clientPackageHeader, ensureOk, HTTPError } from "@/base/http"; +import { ensureOk, HTTPError, publicRequestHeaders } from "@/base/http"; import { apiURL } from "@/base/origins"; import { TwoFactorAuthorizationResponse } from "@/base/types/credentials"; import { nullToUndefined } from "@/utils/transform"; @@ -412,7 +412,7 @@ export const beginPasskeyAuthentication = async ( const url = await apiURL("/users/two-factor/passkeys/begin"); const res = await fetch(url, { method: "POST", - headers: clientPackageHeader(), + headers: publicRequestHeaders(), body: JSON.stringify({ sessionID: passkeySessionID }), }); if (!res.ok) { diff --git a/web/apps/cast/src/services/pair.ts b/web/apps/cast/src/services/pair.ts index 945436a638..613bce6aa0 100644 --- a/web/apps/cast/src/services/pair.ts +++ b/web/apps/cast/src/services/pair.ts @@ -1,5 +1,5 @@ import { boxSealOpen, generateKeyPair } from "@/base/crypto"; -import { clientPackageHeader, ensureOk } from "@/base/http"; +import { ensureOk, publicRequestHeaders } from "@/base/http"; import log from "@/base/log"; import { apiURL } from "@/base/origins"; import { wait } from "@/utils/promise"; @@ -109,7 +109,7 @@ export const register = async (): Promise => { const registerDevice = async (publicKey: string) => { const res = await fetch(await apiURL("/cast/device-info/"), { method: "POST", - headers: clientPackageHeader(), + headers: publicRequestHeaders(), body: JSON.stringify({ publicKey, }), @@ -156,7 +156,7 @@ export const getCastData = async (registration: Registration) => { */ const getEncryptedCastData = async (code: string) => { const res = await fetch(await apiURL(`/cast/cast-data/${code}`), { - headers: clientPackageHeader(), + headers: publicRequestHeaders(), }); ensureOk(res); return z diff --git a/web/packages/accounts/services/passkey.ts b/web/packages/accounts/services/passkey.ts index 8269f95036..9f5687aa50 100644 --- a/web/packages/accounts/services/passkey.ts +++ b/web/packages/accounts/services/passkey.ts @@ -1,7 +1,7 @@ import { clientPackageName, isDesktop } from "@/base/app"; import { sharedCryptoWorker } from "@/base/crypto"; import { encryptToB64, generateEncryptionKey } from "@/base/crypto/libsodium"; -import { clientPackageHeader, HTTPError } from "@/base/http"; +import { HTTPError, publicRequestHeaders } from "@/base/http"; import log from "@/base/log"; import { accountsAppOrigin, apiURL } from "@/base/origins"; import { TwoFactorAuthorizationResponse } from "@/base/types/credentials"; @@ -229,7 +229,7 @@ export const checkPasskeyVerificationStatus = async ( const url = await apiURL("/users/two-factor/passkeys/get-token"); const params = new URLSearchParams({ sessionID }); const res = await fetch(`${url}?${params.toString()}`, { - headers: clientPackageHeader(), + headers: publicRequestHeaders(), }); if (!res.ok) { if (res.status == 404 || res.status == 410) diff --git a/web/packages/base/http.ts b/web/packages/base/http.ts index 89cdd73d76..fe6941a8aa 100644 --- a/web/packages/base/http.ts +++ b/web/packages/base/http.ts @@ -15,10 +15,12 @@ export const authenticatedRequestHeaders = async () => ({ }); /** - * Return a headers object with "X-Client-Package" header set to the client - * package name of the current app. + * Return headers that should be passed alongwith (almost) all unauthenticated + * `fetch` calls that we make to our API servers. + * + * - The client package name. */ -export const clientPackageHeader = () => ({ +export const publicRequestHeaders = () => ({ "X-Client-Package": clientPackageName, }); diff --git a/web/packages/gallery/services/download.ts b/web/packages/gallery/services/download.ts index f53348afac..44243932a3 100644 --- a/web/packages/gallery/services/download.ts +++ b/web/packages/gallery/services/download.ts @@ -8,7 +8,7 @@ import { } from "@/base/crypto"; import { authenticatedRequestHeaders, - clientPackageHeader, + publicRequestHeaders, retryEnsuringHTTPOk, } from "@/base/http"; import { ensureAuthToken } from "@/base/local-user"; @@ -617,7 +617,7 @@ const photos_downloadThumbnail = async (file: EnteFile) => { return fetch( `${customOrigin}/files/preview/${file.id}?${params.toString()}`, { - headers: clientPackageHeader(), + headers: publicRequestHeaders(), }, ); } else { @@ -676,7 +676,7 @@ const photos_downloadFile = async (file: EnteFile): Promise => { return fetch( `${customOrigin}/files/download/${file.id}?${params.toString()}`, { - headers: clientPackageHeader(), + headers: publicRequestHeaders(), }, ); } else { @@ -714,7 +714,7 @@ const publicAlbums_downloadThumbnail = async ( return fetch( `${customOrigin}/public-collection/files/preview/${file.id}?${params.toString()}`, { - headers: clientPackageHeader(), + headers: publicRequestHeaders(), }, ); } else { @@ -722,7 +722,7 @@ const publicAlbums_downloadThumbnail = async ( `https://public-albums.ente.io/preview/?fileID=${file.id}`, { headers: { - ...clientPackageHeader(), + ...publicRequestHeaders(), "X-Auth-Access-Token": accessToken, ...(accessTokenJWT ? { "X-Auth-Access-Token-JWT": accessTokenJWT } @@ -758,7 +758,7 @@ const publicAlbums_downloadFile = async ( `https://public-albums.ente.io/download/?fileID=${file.id}`, { headers: { - ...clientPackageHeader(), + ...publicRequestHeaders(), "X-Auth-Access-Token": accessToken, ...(accessTokenJWT ? { "X-Auth-Access-Token-JWT": accessTokenJWT }