K
Kinde2mo ago
top kek

User pictures from Google SSO (Enterprise Connection)

Hi all, it seems all the pictures from google SSO connections are blank gravatars. I tried asking AI but it makes stuff up (https://discord.com/channels/1070212618549219328/1396808784810344518) and didn’t really help can anyone help me with that? for the record: using the Typescript SDK
5 Replies
Abdelrahman Zaki - Kinde
Hi @freddie, we’ll investigate this and get back to you shortly. Appreciate your patience in the meantime. Let us know if there’s anything else you’d like us to look into.
top kek
top kekOP2mo ago
okay! that’s just that, id like to display the user’s pictures on there
Abdelrahman Zaki - Kinde
Hi @freddie, thanks for your patience. We’ve looked into this, when using Google Workspace via SAML, profile pictures are not included in the SAML assertion by default. That’s why you’re seeing blank gravatars. If you’d like to display user profile pictures, we recommend setting up Google as a social connection instead. This allows Kinde to access the profile picture directly from the user’s Google account during authentication. Let us know if you’d like help switching to that setup or if you have any other questions!
top kek
top kekOP2mo ago
i can’t, we depend on user groups and home realm discovery anything kinde or i can do to add them? like a custom workflow or something
Abdelrahman Zaki - Kinde
Thanks for the extra context, @freddie. Since you're relying on user groups and home realm discovery with Google Workspace SAML, I’ll check in with our expert team to see if there’s a workaround or custom setup that could help bring in profile pictures. We’ll get back to you as soon as we have more info. Appreciate your patience in the meantime! Hi @freddie, thanks again for your patience. There’s a possible workaround using a post-authentication workflow in Kinde. Here’s what it does: - Runs only when a new user signs in. - Fetches the user’s email from Kinde. - Uses that email to call the Google Admin SDK Directory API and fetch their profile picture. - Updates the user’s profile in Kinde with the fetched picture. To make this work, a few things are required: 1. You’ll need Google Workspace Admin access and the Admin SDK Directory API enabled in your Google Cloud project. 2. Your app must include a backend service that can authenticate with Google and generate an access token with permissions to call:
GET https://admin.googleapis.com/admin/directory/v1/users/[email protected]?fields=thumbnailPhotoUrl
GET https://admin.googleapis.com/admin/directory/v1/users/[email protected]?fields=thumbnailPhotoUrl
The token must have the right scopes and be authorized to access the Admin SDK. 3. In Kinde, create a Machine-to-Machine (M2M) app with the following scopes: - read:users - update:users 4. In Settings → Environment variables, set: - KINDE_WF_M2M_CLIENT_ID - KINDE_WF_M2M_CLIENT_SECRET (mark this as sensitive) 5. You also need to disable Gravatar fallback so custom profile pictures can be used: - Go to Settings > Applications > View details - Scroll to Authentication experience - Turn off Use Gravatar fallback, then save Here’s a sample post-authentication workflow:
import {
onPostAuthenticationEvent,
WorkflowSettings,
WorkflowTrigger,
createKindeAPI,
fetch,
} from "@kinde/infrastructure";

export const workflowSettings: WorkflowSettings = {
id: "postAuthentication",
name: "GoogleWorkspacePhotoSync",
failurePolicy: { action: "stop" },
trigger: WorkflowTrigger.PostAuthentication,
bindings: {
"kinde.env": {},
"kinde.fetch": {},
url: {},
},
};

export default async function handlePostAuth(event: onPostAuthenticationEvent) {
const isNewKindeUser = event.context.auth.isNewUserRecordCreated;
if (!isNewKindeUser) return;

const kindeAPI = await createKindeAPI(event);
const userId = event.context.user.id;

const { user } = await kindeAPI.get({ endpoint: `user?id=${userId}` });
const email = user.preferred_email;

const { accessToken } = await fetch("https://your-token-service.example.com/token").then(r => r.json());

const res = await fetch(`https://admin.googleapis.com/admin/directory/v1/users/${email}?fields=thumbnailPhotoUrl`, {
headers: { Authorization: `Bearer ${accessToken}` },
});

let pictureUrl = "";
if (res.ok) {
const { thumbnailPhotoUrl } = await res.json();
pictureUrl = thumbnailPhotoUrl;
}

await kindeAPI.patch({ endpoint: `user?id=${userId}`, params: { picture: pictureUrl } });
}
import {
onPostAuthenticationEvent,
WorkflowSettings,
WorkflowTrigger,
createKindeAPI,
fetch,
} from "@kinde/infrastructure";

export const workflowSettings: WorkflowSettings = {
id: "postAuthentication",
name: "GoogleWorkspacePhotoSync",
failurePolicy: { action: "stop" },
trigger: WorkflowTrigger.PostAuthentication,
bindings: {
"kinde.env": {},
"kinde.fetch": {},
url: {},
},
};

export default async function handlePostAuth(event: onPostAuthenticationEvent) {
const isNewKindeUser = event.context.auth.isNewUserRecordCreated;
if (!isNewKindeUser) return;

const kindeAPI = await createKindeAPI(event);
const userId = event.context.user.id;

const { user } = await kindeAPI.get({ endpoint: `user?id=${userId}` });
const email = user.preferred_email;

const { accessToken } = await fetch("https://your-token-service.example.com/token").then(r => r.json());

const res = await fetch(`https://admin.googleapis.com/admin/directory/v1/users/${email}?fields=thumbnailPhotoUrl`, {
headers: { Authorization: `Bearer ${accessToken}` },
});

let pictureUrl = "";
if (res.ok) {
const { thumbnailPhotoUrl } = await res.json();
pictureUrl = thumbnailPhotoUrl;
}

await kindeAPI.patch({ endpoint: `user?id=${userId}`, params: { picture: pictureUrl } });
}
Let me know if you'd like help getting this up and running or adapting it to your setup.

Did you find this page helpful?