Skip to content

feat: optionally download the profile picture with the access token - #180

Open
heudev wants to merge 1 commit into
NodeBB:mainfrom
heudev:feat/token-protected-avatar
Open

feat: optionally download the profile picture with the access token#180
heudev wants to merge 1 commit into
NodeBB:mainfrom
heudev:feat/token-protected-avatar

Conversation

@heudev

@heudev heudev commented Aug 12, 2026

Copy link
Copy Markdown

Adds an opt-in "Download the picture using the access token instead of storing its URL" option next to the existing "Picture" sync toggle.

Why

updateProfile stores the picture claim verbatim as the user's avatar URL. That works when the provider returns a publicly reachable image, but some return an API endpoint that is only readable with the access token. Microsoft's OIDC userinfo endpoint returns:

"picture": "https://graph.microsoft.com/v1.0/me/photo/$value"

NodeBB stores that string, the browser then requests it anonymously, Microsoft answers 401, and the user is left with a broken avatar that silently falls back to the letter icon. Nothing in the logs indicates a problem, because from the plugin's point of view the sync succeeded.

What changed

  • getUserProfile now carries the access token on the profile object. It was already available as an argument and simply discarded.
  • syncPictureViaToken fetches the picture with an Authorization: Bearer header and hands the bytes to user.uploadCroppedPicture, so the avatar ends up hosted by the forum instead of pointing at a URL that only worked during the login request.
  • When the download succeeds, picture is dropped from the fields passed to user.updateProfile, otherwise the raw URL would immediately overwrite the freshly uploaded avatar.

The option is off by default and gated behind the existing syncPicture toggle, so no existing strategy changes behaviour.

Two deliberate limits:

  • The download runs only while the user has no uploaded avatar. This keeps it from re-uploading a new copy on every single login (which would grow the uploads directory indefinitely) and from overwriting an avatar the user has chosen themselves.
  • Failures are non-fatal: a non-OK response is logged at verbose, an exception at warn, and the login proceeds. A missing profile picture should never block authentication.

Testing

Verified on a live NodeBB 4.14.10 forum against a Microsoft strategy with User.Read in scope — note that the OIDC scopes alone are not enough to read /me/photo/$value. Before: picture was stored as the Graph URL and the avatar did not render. After: the image is fetched and stored under /assets/uploads/profile/uid-<uid>/…, and both picture and uploadedpicture point at it. Subsequent logins leave it untouched. npx eslint . is clean.

…s token

Some providers serve the picture claim from an endpoint that requires
the access token, so storing the URL leaves users with an avatar that
resolves to 401. Microsoft Graph's /me/photo/$value is one such case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant