# How to Fix LinkedIn Authentication in NextAuth.js: A Custom Provider Setup Guide

### 👋 Introduction

While working on my **Resume Builder App** using `Next.js` and `NextAuth`, I encountered a major issue integrating **LinkedIn as a provider**. The default implementation of the `LinkedInProvider` from `next-auth/providers` simply didn't work as expected — especially when it came to retrieving email and profile data.

So I dug deep into the LinkedIn OAuth documentation and crafted a **fully working custom provider setup**, which I’m sharing in this blog post to help others facing similar issues.

### 🚫 The Problem: Default LinkedInProvider Didn’t Work

Here’s what the **default example** from the [NextAuth](https://next-auth.js.org/providers/linkedin) docs looked like:

```javascript
import LinkedInProvider from "next-auth/providers/linkedin";

providers: [
  LinkedInProvider({
    clientId: process.env.LINKEDIN_CLIENT_ID,
    clientSecret: process.env.LINKEDIN_CLIENT_SECRET
  })
]
```

This might work in *some* cases, but if you're expecting to get detailed **user profile fields** like name, email, and profile picture — you'll be disappointed.

### ✅ The Solution: Custom LinkedIn Provider

After reviewing the OAuth 2.0 specs and LinkedIn’s API docs, here’s the working solution I implemented:

```javascript
LinkedInProvider({
  clientId: process.env.LINKEDIN_CLIENT_ID ?? "",
  clientSecret: process.env.LINKEDIN_CLIENT_SECRET ?? "",
  client: { token_endpoint_auth_method: "client_secret_post" },
  scope: "r_liteprofile r_emailaddress",
  issuer: "https://www.linkedin.com",
  userinfo: {
    url: "https://api.linkedin.com/v2/userinfo",
  },
  token: {
    url: "https://www.linkedin.com/oauth/v2/accessToken",
  },
  wellKnown: "https://www.linkedin.com/oauth/.well-known/openid-configuration",
  authorization: {
    url: "https://www.linkedin.com/oauth/v2/authorization",
    params: {
      scope: "profile email openid",
      prompt: "consent",
      access_type: "offline",
      response_type: "code",
    },
  },
  jwks_endpoint: "https://www.linkedin.com/oauth/openid/jwks",
  async profile(profile) {
    return {
      id: profile.sub,
      name: profile.name,
      firstname: profile.given_name,
      lastname: profile.family_name,
      email: profile.email,
      image: profile.picture,
    };
  },
});
```

### 🛠️ Why This Works

* ✅ Explicit scopes (`r_emailaddress`, `r_liteprofile`) ensure proper permissions
    
* ✅ Manually defines `userinfo`, `token`, and `authorization` endpoints
    
* ✅ Provides a `profile()` function to format LinkedIn's response correctly
    
* ✅ Uses `client_secret_post` to avoid authorization issues with LinkedIn
    

### 🚀 Final Thoughts

OAuth can be frustrating — especially with providers like LinkedIn that don’t always play nicely out of the box. This custom provider setup saved my project, and I hope it helps yours too.

Feel free to check out my full project [here](https://resume-org.vercel.app/) and leave any feedback!

---

**Thanks for reading!**  
— Shubham (SKB)
