Fix authenticated media loading for Tauri/WebKit environments

- Add useAuthenticatedMediaUrl hook that fetches media with auth headers
- Add AuthenticatedMedia, AuthenticatedAvatarImage components
- Update downloadMedia and downloadEncryptedMedia to accept access token
- Update all media components to pass access token for auth media
- Fix 401 errors on Matrix servers requiring authenticated media (v1.11+)

This fixes media loading in Tauri desktop apps where service workers
may not work reliably for cross-origin authenticated requests.
This commit is contained in:
2026-01-23 22:31:37 +11:00
parent 94f8466d1c
commit 54f5fa95a6
19 changed files with 330 additions and 38 deletions

View File

@@ -296,18 +296,39 @@ export const mxcUrlToHttp = (
useAuthentication
);
export const downloadMedia = async (src: string): Promise<Blob> => {
// this request is authenticated by service worker
const res = await fetch(src, { method: 'GET' });
/**
* Check if a URL is an authenticated media endpoint.
*/
export const isAuthenticatedMediaUrl = (url: string): boolean =>
url.includes('/_matrix/client/v1/media/download') ||
url.includes('/_matrix/client/v1/media/thumbnail');
/**
* Downloads media with optional authentication.
* For authenticated media URLs, the access token is required.
*/
export const downloadMedia = async (src: string, accessToken?: string | null): Promise<Blob> => {
const needsAuth = isAuthenticatedMediaUrl(src);
const headers: HeadersInit = {};
if (needsAuth && accessToken) {
headers.Authorization = `Bearer ${accessToken}`;
}
const res = await fetch(src, { method: 'GET', headers });
if (!res.ok) {
throw new Error(`Failed to download media: ${res.status}`);
}
const blob = await res.blob();
return blob;
};
export const downloadEncryptedMedia = async (
src: string,
decryptContent: (buf: ArrayBuffer) => Promise<Blob>
decryptContent: (buf: ArrayBuffer) => Promise<Blob>,
accessToken?: string | null
): Promise<Blob> => {
const encryptedContent = await downloadMedia(src);
const encryptedContent = await downloadMedia(src, accessToken);
const decryptedContent = await decryptContent(await encryptedContent.arrayBuffer());
return decryptedContent;