Hope you are doing well and staying safe!
I am trying to create a LinkedIn based authentication using typescript, but since long I am failing. My code is successfully able to give me login option using LinkedIn but when I login, it just gives me blank page. And, I have no idea why.
Also, I see no API getting called in LinkedIn Analytics.
Below is the typescript for authentication for your reference-
// pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth";
import type { NextAuthOptions } from "next-auth";
const authOptions: NextAuthOptions = {
providers: [
{
id: "linkedin",
name: "LinkedIn",
type: "oauth",
version: "2.0",
idToken: true,
authorization: {
url: "https://www.linkedin.com/oauth/v2/authorization",
params: {
scope: "openid profile email",
response_type: "code",
},
},
token: {
url: "https://www.linkedin.com/oauth/v2/accessToken",
async request(context) {
const { params } = context;
// ✅ Declare constants outside the URLSearchParams block
const hardcodedClientId = "44ddgfdsgdsgsdfdssdfu";
const hardcodedClientSecret = "WPL_AP1.fefefsdfsaYdada5.qyKbDw==";
const body = new URLSearchParams({
grant_type: "authorization_code",
code: params.code!,
// redirect_uri: "http://localhost:3000/api/auth/callback/linkedin",
redirect_uri: "https://28fa-2401-4900-7085-a56e-8de6-d45c-2e3f-e535.ngrok-free.app/api/auth/callback/linkedin",
client_id: hardcodedClientId,
client_secret: hardcodedClientSecret,
code_verifier: params.code_verifier!,
});
const res = await fetch("https://www.linkedin.com/oauth/v2/accessToken", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: body.toString(),
});
if (!res.ok) {
const errorText = await res.text();
console.error("❌ LinkedIn Token Exchange Error (Raw):", errorText);
console.error("🔐 Sent Client ID:", hardcodedClientId);
console.error("🔐 Sent Client Secret:", hardcodedClientSecret ? "✅ present" : "❌ missing");
throw new Error("Token exchange failed");
}
return res.json();
},
},
userinfo: {
url: "https://api.linkedin.com/v2/userinfo",
},
profile(profile) {
return {
id: profile.sub,
name: profile.name || `${profile.given_name} ${profile.family_name}`,
email: profile.email,
image: profile.picture,
};
},
clientId: "77thyyt4uur7nu",
clientSecret: "WPL_AP1.fefefsdfsaYdada5.qyKbDw==",
checks: ["pkce", "state"],
},
],
secret: "fHXNb4aKPzlDDugJBzxzXZ0781Z/tdsGZtxxxsderUo=",
debug: true,
};
export default NextAuth(authOptions);
Below is my LinkedIn developer configuration-
Generate a new Client Secret
OAuth 2.0 settings
Token time to live duration
Access token:
2 months
(5184000 seconds)
Authorized redirect URLs for your app
https://28fa-2401-4900-7085-a56e-8de6-d45c-2e3f-e535.ngrok-free.app/api/auth/callback/linkedin
OAuth 2.0 scopes
Scopes define what your app can do on a user's behalf.
The OAuth consent screen will display descriptions to end users as they are seen below. Some variation may occur if your app has a custom OAuth experience.
openid
Use your name and photo
profile
Use your name and photo
w_member_social
Create, modify, and delete posts, comments, and reactions on your behalf
email
Use the primary email address associated with your LinkedIn account
AND PRODUCTS-
Share on LinkedIn
Default Tier
Amplify your content by sharing it on LinkedIn
View docs
Endpoints
Sign In with LinkedIn using OpenID Connect
Standard Tier
Using the OpenID Connect standard
View docs
Endpoints