Google Photos requires the Authorization header, OneDrive forbids it
Downloading a user photo, two clouds, two opposite behaviors on the same HTTP header. The kind of detail that costs a full day of debugging.
Same need: download a user's photo. On Google Photos, the request fails without the Authorization header. On OneDrive, it fails with it.

A full day of debugging to understand this, even though everything is documented. Just documented on each side separately, and nobody connects the dots. The contrast deserves its own post.
Google Photos: the URL is never enough
The Google Photos Picker API returns a baseUrl for each selected media item. These URLs look self-sufficient; they are not: downloading the actual bytes requires the OAuth token in the header, like any API call. And they expire after about an hour: cache the files, never the URLs.
GET {baseUrl}=d
Authorization: Bearer ya29.a0...
Without the header: authentication error, even with a fresh baseUrl. The URL identifies the resource, the header proves the access right. Two separate things.
OneDrive: the URL is the access right
Microsoft Graph returns a @microsoft.graph.downloadUrl property for each file. It is pre-signed: the access proof is encoded in the URL itself, like a signed S3 or GCS URL. Short-lived, no additional authentication expected.
And that is where it breaks: if your HTTP client mechanically adds the Authorization header (a global Dio interceptor, say), the request fails. The pre-signed download endpoint rejects requests carrying extra authentication.
// The well-meaning global interceptor...
dio.interceptors.add(AuthInterceptor()); // adds Authorization everywhere
// ...specifically breaks OneDrive downloadUrls.
// Fix: a bare client for pre-signed URLs.
final bareDio = Dio();
await bareDio.download(microsoftDownloadUrl, path);
The general lesson
The reflex "I add the token everywhere, worst case it gets ignored" is wrong. An auth header is not neutral: on a pre-signed URL, it conflicts with the signature.
The right question for every download URL: who carries the access proof?
- Proof in the header → bare URL, token required (Google Photos).
- Proof in the URL → header forbidden or ignored, and your global interceptor is a liability (OneDrive, signed S3/GCS).
Concretely, in an app aggregating several clouds: two HTTP clients. One authenticated for API calls, one bare for pre-signed URLs. The single global interceptor is the bug waiting to happen.
Takeaways
- Google Photos:
baseUrl+ mandatoryAuthorizationheader for the bytes. - OneDrive: pre-signed
@microsoft.graph.downloadUrl,Authorizationheader fatal. - Classify every download URL: proof in the header or proof in the URL. The HTTP client follows.
Has your global auth interceptor ever broken a signed URL?