Skip to main content

LinkedIn

This guide explains our LinkedIn integration: scopes, OAuth 2.0 Authorization Code flow, data persisted, how we determine the author URN, and how publishing works.

Quick links: Frontend β€’ Backend

Permissions and scopes​

Default scopes requested:

  • w_member_social (posting via UGC)
  • openid profile (basic member profile via OIDC userinfo)

Backend​

Token and profile flow​

  1. Start: liAuthStart
  • Requires Firebase ID token; verifies to get uid.
  • Stores oauthStates/{state} with { uid, provider: 'linkedin', returnTo }.
  • Redirects to https://www.linkedin.com/oauth/v2/authorization with response_type=code, client_id, redirect_uri, scope, state.
  1. Callback: liAuthCallback
  • Exchanges code for tokens at POST https://www.linkedin.com/oauth/v2/accessToken with form fields grant_type=authorization_code, code, redirect_uri, client_id, client_secret.
  • Retrieves a compact profile using GET https://api.linkedin.com/v2/userinfo (preferred) or falls back to GET https://api.linkedin.com/v2/me.
  • Computes authorUrn as urn:li:person:{id}.
  • Persists to users/{uid}/integrations/linkedin/{personId} (where personId from profile):
    • accountId: Unique identifier (same as person ID)
    • accessToken, tokenType='bearer', scope, expiresIn, expiresAt, updatedAt
    • profile (platformId/displayName/username/avatarUrl)
    • authorUrn
    • enabled_for_repost: (optional) boolean flag for automatic reposting
  • Redirects to returnTo?linkedin=connected if provided.

Multi-Account Support​

Users can connect multiple LinkedIn accounts. Each account is stored separately with a unique accountId.

Storage path: users/{uid}/integrations/linkedin/{accountId}

Where accountId is the LinkedIn person ID.

Account selection:

  • If user has one account: Automatically selected, no accountId required in API requests
  • If user has multiple accounts: accountId is required in API requests (e.g., /v1/posts)
  • Missing required accountId returns 400 error with code missing_account

Publishing​

Endpoint: Cloud Function publishLinkedin (via orchestrator)

Flow:

  1. Resolve account: Determine accountId from request or auto-select if only one account exists
  2. Load users/{uid}/integrations/linkedin/{accountId}; ensure accessToken.
  3. Resolve/repair authorUrn via OIDC userinfo or /v2/me if missing; persist when repaired.
  4. Compose UGC Post for text:
author: urn:li:person:{id}
lifecycleState: PUBLISHED
specificContent:
com.linkedin.ugc.ShareContent:
shareCommentary: { text }
shareMediaCategory: NONE
visibility:
com.linkedin.ugc.MemberNetworkVisibility: PUBLIC
  1. POST https://api.linkedin.com/v2/ugcPosts with Bearer token and X-Restli-Protocol-Version: 2.0.0.
  2. On success, store result id (x-restli-id header or response body) and record the user post.

Errors are logged with structured fields; function returns 400 with upstream body on LinkedIn API errors.

Frontend​

Hooks/services under apps/web/src/features/integrations initiate connect and fan‑out publishing through the orchestrator.