Ask for the picture the way providers actually offer it

Two assumptions, both mine, and either one was enough for an account to end
up with no picture. It was looked for only in the ID token, which a provider
is free to keep lean while holding the rest at userinfo — PocketID does
exactly that — and the endpoint was never asked. Then the image was fetched
with no credentials, while avatars are commonly behind the provider's own
login.

Userinfo is now asked when something is missing, and believed only when its
sub matches the token whose signature was checked. The access token rides
along with the picture request when, and only when, the picture lives at the
provider.

The third fault was the one that cost the time: it failed silently, so there
was no way to tell which of the two it was. Every outcome is a line in the
log now. And the test provider keeps its picture out of the token and behind
its door, so the check passing is evidence for both new paths.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-08-21 18:55:19 +02:00
1 parent a2b3f91e00
commit 1e2375f190
6 files changed
+152 -19

No files matched your search

+13 -3
View File
@@ -324,9 +324,19 @@ maintain; the account panel says so, and names the provider it signs in through.
The administration list marks every linked account, and says which of them have
no password here.
**The picture comes along.** If the provider sends a `picture` claim — PocketID
does — the server fetches it at each sign-in and stores it, encrypted, in a table
of its own. It is served back through the API, so nothing on the page talks to
**The picture comes along.** The server looks for a `picture` claim in the ID
token and, when it is not there, asks the provider's `userinfo` endpoint — a
provider is free to keep the token lean, and several do, PocketID among them.
The answer is used only if its `sub` matches the token whose signature was
checked. The picture is then fetched at each sign-in and stored, encrypted, in a
table of its own; if it lives at the provider, the access token goes with the
request, since avatars are often behind the provider's own login. That token is
never sent anywhere but to the provider itself.
Whatever happens, the server log says so in one line — stored, or exactly why
not: no claim, refused with a 401, not an image, too large, unreachable.
The picture is stored It is served back through the API, so nothing on the page talks to
the provider, no third-party cookie is needed for it to appear, and it keeps
working when the provider does not. The bytes are fetched once per account and
turned into an object URL, since an `<img src>` cannot carry a bearer token; the