LinkedIn: default to the scopes Community Management API grants

The approved app has no OpenID Connect product, so openid/profile/r_organization_admin
would fail. Use r_basicprofile + rw_organization_admin and read the member from /v2/me.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
metatroncubeswdev 2026-10-28 22:02:47 -04:00
parent 3aff447011
commit 1307146921
2 changed files with 11 additions and 7 deletions

View File

@ -54,24 +54,28 @@ export default {
console: "https://www.linkedin.com/developers/apps", console: "https://www.linkedin.com/developers/apps",
steps: [ steps: [
"Create an app at linkedin.com/developers/apps and link it to your company's LinkedIn Page (the Page admin must verify it).", "Create an app at linkedin.com/developers/apps and link it to your company's LinkedIn Page (the Page admin must verify it).",
"Products tab: add “Sign In with LinkedIn using OpenID Connect” and “Share on LinkedIn” (instant).", "Products tab: request “Community Management API” for posting to clients' company Pages (LinkedIn reviews it; your company Page must be verified).",
"For posting to clients' company Pages, also request “Community Management API” (LinkedIn reviews it; your company Page must be verified).",
"Auth tab: add the Redirect URL shown below, then copy the Client ID and Client Secret here.", "Auth tab: add the Redirect URL shown below, then copy the Client ID and Client Secret here.",
], ],
note: "Until Community Management API is approved, set Scopes to “openid profile w_member_social” so you can post to personal profiles only.", note: "Without Community Management API (personal profiles only): add “Sign In with LinkedIn using OpenID Connect” and “Share on LinkedIn”, and set Scopes to “openid profile w_member_social”.",
}, },
auth: { auth: {
authorizeUrl: "https://www.linkedin.com/oauth/v2/authorization", authorizeUrl: "https://www.linkedin.com/oauth/v2/authorization",
tokenUrl: "https://www.linkedin.com/oauth/v2/accessToken", tokenUrl: "https://www.linkedin.com/oauth/v2/accessToken",
scopes: ["openid", "profile", "w_member_social", "r_organization_admin", "w_organization_social", "r_organization_social"], // The scopes Community Management API grants (it can't be combined with the OpenID Connect product).
scopes: ["r_basicprofile", "w_member_social", "rw_organization_admin", "w_organization_social", "r_organization_social"],
}, },
formats: { TEXT: true, IMAGE: true, CAROUSEL: true, REELS: true }, formats: { TEXT: true, IMAGE: true, CAROUSEL: true, REELS: true },
limits: { caption: 3000, images: 20 }, limits: { caption: 3000, images: 20 },
targetLabel: "Post as", targetLabel: "Post as",
async identify(tokens) { async identify(tokens) {
if (/\bopenid\b/.test(tokens.scope || "")) {
const { data } = await request("LinkedIn", `${API}/v2/userinfo`, { token: tokens.accessToken }); const { data } = await request("LinkedIn", `${API}/v2/userinfo`, { token: tokens.accessToken });
return { id: data.sub, name: data.name, username: null, avatar: data.picture || null, url: null }; return { id: data.sub, name: data.name, username: null, avatar: data.picture || null, url: null };
}
const { data } = await request("LinkedIn", `${API}/v2/me`, { token: tokens.accessToken });
return { id: data.id, name: [data.localizedFirstName, data.localizedLastName].filter(Boolean).join(" ") || "LinkedIn member", username: null, avatar: null, url: null };
}, },
/** The person, plus every company Page they administer (needs Community Management API). */ /** The person, plus every company Page they administer (needs Community Management API). */

View File

@ -139,7 +139,7 @@ test("sign-in URLs carry the right client id, scopes and PKCE per platform", ()
test("LinkedIn: connect, list company Pages, post image to a company Page", async () => { test("LinkedIn: connect, list company Pages, post image to a company Page", async () => {
reset(); reset();
const { conn } = await connect("linkedin", "acme", { identify: [ const { conn } = await connect("linkedin", "acme", { identify: [
["GET", "https://api.linkedin.com/v2/userinfo", json({ sub: "abc", name: "Pat Lee", picture: "https://p" })], ["GET", "https://api.linkedin.com/v2/me", json({ id: "abc", localizedFirstName: "Pat", localizedLastName: "Lee" })],
["GET", "https://api.linkedin.com/rest/organizationAcls", json({ elements: [{ organization: "urn:li:organization:42" }] })], ["GET", "https://api.linkedin.com/rest/organizationAcls", json({ elements: [{ organization: "urn:li:organization:42" }] })],
["GET", "https://api.linkedin.com/rest/organizations/42", json({ localizedName: "Acme Co" })], ["GET", "https://api.linkedin.com/rest/organizations/42", json({ localizedName: "Acme Co" })],
] }); ] });