Hi all
For Headlinker the community for indie recruiters i'm building right now, I set up a LinkedIn login capability, as all of my users do have LinkedIn.
It's very easy to setup to get the email, name and profile image but not quite easy to get the vanity url and headline.
So here is how to do it!
Prerequisites
You have NextAuth already setup in your app with basic LinkedIn.
Permissions
By default, when asking for login permissions on LinkedIn, you get r_liteprofile
and r_emailaddress
which gives you basically first name, last name, image and email.
We need to get access to r_basicprofile
to get back the vanity URL and the headline.
For this
- Go to your LinkedIn Developer apps page
- Select you app
- Go to
Products
- Ask for permissions on the
Advertising API
(Yes it's weird but it's in there)
- Answer the questions and be as specific as possible on why you need access to these informations
- Wait
It can take several hours to get access or you can even get refused.
If you manage though, you can go to next step.
Configure Next-Auth
No that you have access to those new fields, you need to update next-auth to actually retrieve those fields and save them in your database.
For this, you will need to replace
LinkedInProvider({
clientId: process.env.AUTH_LINKEDIN_CLIENT_ID as string,
clientSecret: process.env.AUTH_LINKEDIN_CLIENT_SECRET as string,
}),
by
{
...LinkedInProvider({
clientId: process.env.AUTH_LINKEDIN_CLIENT_ID as string,
clientSecret: process.env.AUTH_LINKEDIN_CLIENT_SECRET as string,
}),
authorization: {
url: 'https://www.linkedin.com/oauth/v2/authorization',
params: { scope: 'r_liteprofile r_basicprofile r_emailaddress' },
},
userinfo: {
url: 'https://api.linkedin.com/v2/me',
params: {
projection: `(id,localizedFirstName,localizedLastName,vanityName,localizedHeadline,profilePicture(displayImage~digitalmediaAsset:playableStreams))`,
},
},
async profile(profile, tokens) {
const emailResponse = await fetch('https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', {
headers: { Authorization: `Bearer ${tokens.access_token}` },
});
const emailData = await emailResponse.json();
return {
id: profile.id,
url: `https://www.linkedin.com/in/${profile.vanityName}`,
name: `${profile.localizedFirstName} ${profile.localizedLastName}`,
email: emailData?.elements?.[0]?.['handle~']?.emailAddress,
title: profile.localizedHeadline,
image: profile.profilePicture?.['displayImage~']?.elements?.[0]?.identifiers?.[0]?.identifier,
};
},
allowDangerousEmailAccountLinking: true,
Let's break this down
- First, change the scope and add
r_basicprofile
, this will give access to the corresponding fields - Then, change the projection in userinfo so that it includes the vanityName (martinratinaud) which we will use to build the vanity URL and the localizedHeadline
- Finally, map the profile to where you want to store it in your database -> for me, url and title
- Optionally, set
allowDangerousEmailAccountLinking
if you already have LinkedIn account in the database. If it's the case and you don't do it, users will get an error as they can't signin with different permissions. See https://next-auth.js.org/configuration/providers/oauth#allowdangerousemailaccountlinking-option regarding this matter.
Bonus
In order to find all the fields available you just need to empty userinfo.params.projection
and console.log in async profile
function.
Top comments (0)