From 5e4e84558ca8fc0876b9240f7a3452cf64adfa1e Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Mon, 22 Dec 2025 13:37:51 +0100 Subject: [PATCH] feat: Add OIDC compliant 'picture' attribute mapping --- .../discord/DiscordIdentityProvider.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/main/java/org/keycloak/social/discord/DiscordIdentityProvider.java b/src/main/java/org/keycloak/social/discord/DiscordIdentityProvider.java index b14fc3e..e346ab4 100755 --- a/src/main/java/org/keycloak/social/discord/DiscordIdentityProvider.java +++ b/src/main/java/org/keycloak/social/discord/DiscordIdentityProvider.java @@ -18,6 +18,7 @@ package org.keycloak.social.discord; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.ws.rs.core.Response; import org.jboss.logging.Logger; import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider; @@ -32,6 +33,7 @@ import org.keycloak.services.ErrorPageException; import org.keycloak.services.messages.Messages; import java.util.Set; +import java.util.regex.Pattern; /** * @author Hiroyuki Wada @@ -45,9 +47,13 @@ public class DiscordIdentityProvider extends AbstractOAuth2IdentityProviderDiscord returns an avatar hash (or null), but OIDC expects a direct URL + * to the image in the 'picture' claim.

+ * @param user The context where the 'picture' attribute will be set + * @param profile The raw Discord user profile JSON containing the 'avatar' hash + */ + private void setUserPicture(BrokeredIdentityContext user, JsonNode profile) { + if (user.getId() == null || !DISCORD_ID_PATTERN.matcher(user.getId()).matches()) { + return; + } + + String avatarHash = getJsonProperty(profile, "avatar"); + if (avatarHash == null || avatarHash.isEmpty() || !AVATAR_HASH_PATTERN.matcher(avatarHash).matches()) { + return; + } + String extension = "png"; + if (avatarHash.startsWith("a_")) { + extension = "gif"; + } + String finalURL = String.format(USER_PICTURE_URL, user.getId(), avatarHash, extension); + user.setUserAttribute("picture", finalURL); + if (profile instanceof ObjectNode objectNodeProfile) { + objectNodeProfile.put("picture", finalURL); + } + } + @Override protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) { log.debug("doGetFederatedIdentity()");