| 263 | } |
| 264 | |
| 265 | func resolveProviderPicture(ctx context.Context, issuerURL string, token *oauth2.Token, rawPicture string) (string, error) { |
| 266 | if !isMicrosoftIdentityIssuer(issuerURL) { |
| 267 | return rawPicture, nil |
| 268 | } |
| 269 | // For Microsoft Entra, the `picture` claim is NOT directly renderable: |
| 270 | // - Often empty (Entra doesn't include it unless configured as an optional claim). |
| 271 | // - A bare user GUID. |
| 272 | // - Or `https://graph.microsoft.com/v1.0/me/photo/$value` — a Graph API endpoint |
| 273 | // that requires a bearer token; the browser can't fetch it directly. |
| 274 | // So regardless of what the claim contains, the right thing to do is call Graph |
| 275 | // with our access token and embed the returned image bytes as a data: URL. |
| 276 | // If Graph fetch succeeds, prefer it; otherwise fall back to the raw claim only |
| 277 | // if it's a renderable non-Graph URL (rare but possible for hybrid setups). |
| 278 | if token != nil && token.AccessToken != "" { |
| 279 | picture, err := fetchMicrosoftGraphPhotoDataURL(ctx, token) |
| 280 | if err == nil && picture != "" { |
| 281 | return picture, nil |
| 282 | } |
| 283 | // Graph fetch failed or returned no photo — log-by-returning-err semantics are |
| 284 | // preserved below only when the claim is unusable, so we don't surface transient |
| 285 | // Graph errors to the caller when we have a usable fallback. |
| 286 | if err != nil && (rawPicture == "" || isMicrosoftGraphURL(rawPicture)) { |
| 287 | return "", err |
| 288 | } |
| 289 | } |
| 290 | // No token or Graph failed: fall back to the claim ONLY if it's renderable AND |
| 291 | // not a Graph API URL (which the browser can never load without auth). |
| 292 | if isRenderableImageSrc(rawPicture) && !isMicrosoftGraphURL(rawPicture) { |
| 293 | return rawPicture, nil |
| 294 | } |
| 295 | return "", nil |
| 296 | } |
| 297 | |
| 298 | // isMicrosoftGraphURL detects URLs pointing at Microsoft Graph, which require a |
| 299 | // bearer token and therefore cannot be used as a browser-renderable <img src>. |