LinkedIn Login Is Not an API Key: Using OpenID Connect the Right Way
Treat LinkedIn Sign In as standard OpenID Connect, not a permanent access token. Verify the ID token, key accounts by sub, and handle missing email gracefully.
28 Jan 2026, 22:07 UTC

The problem: treating a login button like an API key
Many teams integrate LinkedIn authentication by obtaining an access token and treating it as a permanent identity, or by scraping profile HTML when the token expires. Both approaches break the moment a member revokes consent, changes their email, or LinkedIn adjusts a product permission. The useful takeaway is that Sign In with LinkedIn using OpenID Connect is a standard protocol, not a bespoke data feed. Model it as OIDC—authorization code flow, minimal scopes, verified ID token—and keep LinkedIn-specific data out of your primary key.
Why access tokens and email make poor identity keys
An access token is a capability, not an identity. It is scoped to specific permissions, can expire, and can be revoked independently of the user's account. If you key local accounts by an access token, a routine token refresh or a consent change will orphan the user.
Email is equally fragile as a primary key. LinkedIn members can change their email address, withhold it during consent, or lack a verified address entirely. The stable identifier in an OIDC ID token is the sub claim—an opaque string issued by LinkedIn that remains consistent for the same member and app. Treat email as a contact claim that may be absent or outdated.
Requesting minimal OIDC scopes
When available, use the OpenID Connect scopes openid, profile, and email rather than legacy LinkedIn-specific scopes. The openid scope signals that you want an ID token; profile and email request the corresponding claims. Requesting more than you need increases review friction and user friction.
LinkedIn's product names, scope names, token lifetimes, and app-review requirements change over time. Do not hard-code undocumented endpoints or assume a scope behaves the same way across app configurations. Verify current scope names and availability in LinkedIn's developer documentation before release.
Worked example: the authorization code flow
Run this logic in your backend authentication service. You will need your LinkedIn app's client ID and client secret, and the exact redirect URI registered in the LinkedIn app console.
1. Redirect the member to LinkedIn
Construct the authorization URL server-side using the endpoint published in LinkedIn's OIDC metadata, then redirect the browser. Generate a random state value, store it in the server-side session, and include it in the request. If your OIDC library supports it, also generate and store a nonce to mitigate replay attacks.
BASE_URL_FROM_LINKEDIN_OIDC_METADATA
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fyour-app.example.com%2Fauth%2Flinkedin%2Fcallback
&state=RANDOM_STATE_STORED_IN_SESSION
&scope=openid%20profile%20email
Expected check: the returned state matches the session value. Risk: a missing or mismatched state allows cross-site request forgery; reject the callback immediately.
2. Exchange the code for tokens
LinkedIn redirects back to your callback with a short-lived authorization code. Exchange it server-side for an ID token and access token. Never expose the client secret or tokens to the browser.
3. Verify the ID token before trusting it
An ID token is a signed JWT. Before you read any claim, validate it against LinkedIn's published OIDC metadata and JWKS (JSON Web Key Set).
| Check | Why it matters |
|---|---|
iss | Confirms the token was issued by LinkedIn, not another provider. |
aud | Ensures the token was minted for your specific app. |
exp / iat | Rejects expired or not-yet-valid assertions. |
| Signature | Verifies the token has not been tampered with; check against LinkedIn's rotating JWKS. |
nonce | If you sent one, confirm it matches to prevent replay. |
Skipping any of these turns a login into an account-takeover risk.
4. Key the local account by sub
After validation, read the sub claim. Create or update your local user record using sub as the stable foreign key. Only populate the email field when the email claim is present and email_verified is true. Store minimal token metadata—such as expiry—if you need it, but never log raw tokens.
Trade-offs: an ID token is a snapshot, not a live feed
An ID token is a point-in-time assertion. It does not guarantee the member's current email, locale, or profile information. Some users decline the email scope, and others may not have a verified address. If your product needs fresh profile data, call LinkedIn's documented userinfo endpoint only when needed and cache sparingly.
Because LinkedIn's permissions, product names, and token behavior vary by app, partner program, and region, your integration must tolerate missing claims. Build a reconnect flow that prompts the user to grant consent again when LinkedIn returns an invalid_token or consent-related error. Do not assume an access token remains valid forever.
Actionable checklist
- Implement a single OIDC callback that validates
state,iss,aud,exp, and the token signature. - Store
subplus minimal token metadata; never log raw tokens or use email as the sole login identifier. - Offer a reconnect flow when scopes are revoked or tokens are rejected.
- Test three paths: a member with a verified email, a member who denies email, and a member whose email changes after first login.
- Before release, confirm current scope names, token lifetime, refresh behavior, and app-review requirements in LinkedIn's developer documentation.
Treating LinkedIn as a standard OIDC provider costs little upfront and saves the emergency migration later when a scope changes or a member updates their contact information.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.