next-auth
next-auth copied to clipboard
Auth provider for Linkedin not working
Environment
System: OS: Linux 6.2 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish) CPU: (12) x64 AMD Ryzen 5 3600X 6-Core Processor Memory: 2.63 GB / 15.53 GB Container: Yes Shell: 5.1.16 - /bin/bash Binaries: Node: 20.4.0 - /usr/local/bin/node npm: 9.7.2 - /usr/local/bin/npm
Reproduction URL
https://github.com/nextauthjs/next-auth-example
Describe the issue
As per the documentation from Linkedin, I've set up a new LinkedIn app, and added "Sign In with LinkedIn using OpenID Connect" as a product.
At first I had some problems when not specifying scope.
providers: [ LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET, }) ], pages: { signIn: '/register-cv' },
This returns an 'unauthorized_scope_error' for r_emailaddress. Managed to fix that issue by providing scopes as per the documentation from Microsoft:
Authenticating Members New members logging in to your service for the first time will need to follow the Authenticating with OAuth 2.0 Guide. When requesting the authorization code in Step 2 of the OAuth 2.0 Guide, make sure you use the OpenID scope openid to get the ID Token. We are also introducing new scopes profile and email.
openid Required to indicate the application wants to use OIDC to authenticate the member. profile Required to retrieve the member's lite profile including their id, name, and profile picture. email Required to retrieve the member's email address. After successful authentication, you will receive the member's access token and ID token.
If your application does not have these permissions provisioned, you can request access through the Developer Portal. Select your app from My Apps, navigate to the Products tab, and request the Sign in with LinkedIn using OpenID Connect product.
Link: https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2
Updated scopes to match the documentation from Microsoft:
LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET, authorization: { params: { scope: 'openid profile email' } }})
Now getting this error:
https://next-auth.js.org/errors#oauth_callback_error unexpected iss value, expected undefined, got: https://www.linkedin.com { error: RPError: unexpected iss value, expected undefined, got: https://www.linkedin.com at Client.validateJWT (/home/deb/PhpstormProjects/cvmaker/node_modules/openid-client/lib/client.js:931:15) at Client.validateIdToken (/home/deb/PhpstormProjects/cvmaker/node_modules/openid-client/lib/client.js:766:60) at Client.callback (/home/deb/PhpstormProjects/cvmaker/node_modules/openid-client/lib/client.js:505:18) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async oAuthCallback (/home/deb/PhpstormProjects/cvmaker/node_modules/next-auth/core/lib/oauth/callback.js:109:16) at async Object.callback (/home/deb/PhpstormProjects/cvmaker/node_modules/next-auth/core/routes/callback.js:52:11) at async AuthHandler (/home/deb/PhpstormProjects/cvmaker/node_modules/next-auth/core/index.js:208:28) at async NextAuthApiHandler (/home/deb/PhpstormProjects/cvmaker/node_modules/next-auth/next/index.js:22:19) at async NextAuth._args$ (/home/deb/PhpstormProjects/cvmaker/node_modules/next-auth/next/index.js:108:14) { name: 'OAuthCallbackError', code: undefined }, providerId: 'linkedin', message: 'unexpected iss value, expected undefined, got: https://www.linkedin.com'
How to reproduce
1: Set up new app on Linkedin, add "Sign In with LinkedIn using OpenID Connect" as a product. 2: Add authentication provider for Linkedin:
import LinkedIn from "next-auth/providers/linkedin"
export const authOptions = {
secret: 'NOT SO SECRET ANYMORE',
providers: [
LinkedIn({
clientId: process.env.LINKEDIN_ID,
clientSecret: process.env.LINKEDIN_SECRET,
authorization: { params: { scope: 'openid profile email' } },
})
], pages: {
signIn: '/register-cv'
},
}
export default `NextAuth(authOptions);
SOLVED
After some debugging I managed to solve this. In case anyone else stumbles across this, since it's not well documented:
If you're using Linkedin API V2, you do need to add some custom parameters to the Linkedin provider. The original issue was that the expected iss was undefined. Simply add issuer to OAuthConfig for the Linkedin provider:
issuer: 'https://www.linkedin.com'
This triggers a new error: 'jwks_uri must be configured on the issuer'. Add jwks_endpoint to OAuthConfig:
jwks_endpoint: 'https://www.linkedin.com/oauth/openid/jwks'
Again, this triggers a new error, where nextauth complains that the profile id is missing. Linkedin API V2 use "sub" not "id". Solved by overriding the profile function in OAuthConfig:
return {
id: profile.sub,
name: profile.name,
firstname: profile.given_name,
lastname: profile.family_name,
email: profile.email,
image: profile.picture,
}
}
Complete working provider for Linkedin using Linkedin API V2:
LinkedIn({
clientId: process.env.LINKEDIN_ID,
clientSecret: process.env.LINKEDIN_SECRET,
authorization: { params: { scope: 'profile email openid' } },
issuer: 'https://www.linkedin.com',
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
}
},
})
This issue should be opened again, because every new app using SignIn with Linked will run into this issue (as I did).
Reason: Microsoft has deprecated the old API as of August 1, 2023!
- https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin
- https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2
I'd suggest to keep the current LinkedIn
provider as is (for all apps using the legacy API), but add a new LinkedInV2
provider for all new apps using the SignIn with OpenID Connect.
Deprecated API:
New API
Hi,
Fair point. Closed since I managed to solve it by setting some custom parameters, although I used quite some time on it. Reopening.
Either way: Did you manage to get it to work using the provided solution?
Yes, your solution works great. It saved me a lot of time digging deeper myself. :)
Is it still working for you guys? Even with the snippet posted, I'm still getting an error:
error: invalid_client, error_description: Client authentication failed
(I've double checked the client_id and client_secret multiple times already..
Is it still working for you guys? Even with the snippet posted, I'm still getting an error:
error: invalid_client, error_description: Client authentication failed
(I've double checked the client_id and client_secret multiple times already..
It's working. Can you please provide the complete code of [...nextauth.js]? Remember to remove ids and secrets.
Thanks for you reply :) I got one step 'further': I was using auth.js version 0.12.0. After updating to 0.17.0, I'm now getting a different error:
[mf:inf] GET /auth/csrf 200 OK (1ms)
Called .text() on an HTTP body which does not appear to be text. The body's Content-Type is "application/x-www-form-urlencoded". The result will probably be corrupted. Consider checking the Content-Type header before interpreting entities as text.
[auth][error][SignInError]: Read more at https://errors.authjs.dev#signinerror
[auth][cause]: OperationProcessingError: "response" is not a conform Authorization Server Metadata response
at processDiscoveryResponse (file:///tmp/tmp-17164-QJe0X481iy9Q/hqlw55rde5g.js:5277:11)
at getAuthorizationUrl (file:///tmp/tmp-17164-QJe0X481iy9Q/hqlw55rde5g.js:6903:23)
at async signin (file:///tmp/tmp-17164-QJe0X481iy9Q/hqlw55rde5g.js:6966:14)
at async AuthInternal (file:///tmp/tmp-17164-QJe0X481iy9Q/hqlw55rde5g.js:7144:27)
at async Auth (file:///tmp/tmp-17164-QJe0X481iy9Q/hqlw55rde5g.js:7240:28)
at async respond (file:///tmp/tmp-17164-QJe0X481iy9Q/hqlw55rde5g.js:17685:22)
at async Object.fetch (file:///tmp/tmp-17164-QJe0X481iy9Q/hqlw55rde5g.js:18172:13)
at async jsonError (file:///tmp/tmp-17164-QJe0X481iy9Q/hqlw55rde5g.js:18195:12)
at async jsonError2 (file:///tmp/tmp-17164-QJe0X481iy9Q/hqlw55rde5g.js:18373:12)
[auth][details]: {
"provider": "linkedin"
}
[mf:inf] POST /auth/signin/linkedin 200 OK (133ms)
[mf:inf] GET /auth/signin 200 OK (2ms)
My code:
SvelteKitAuth(async (event) => {
const authOptions = {
providers: [GitHub({
clientId: GITHUB_ID, clientSecret: GITHUB_SECRET
}),
Google({ clientId: GOOGLE_CLIENT_ID, clientSecret: GOOGLE_CLIENT_SECRET }),
Facebook({ clientId: FACEBOOK_CLIENT_ID, clientSecret: FACEBOOK_CLIENT_SECRET }),
LinkedIn({
clientId: LINKEDIN_CLIENT_ID,
clientSecret: LINKEDIN_CLIENT_SECRET,
authorization: { params: { scope: 'profile email openid' } },
issuer: 'https://www.linkedin.com',
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
}
},
})
],
secret: AUTH_SECRET,
trustHost: true,
...
}
```
I'm using "next-auth": "^4.23.2"
- not sure what that auth.js 0.17.0 dependency is.
SOLVED
After some debugging I managed to solve this. In case anyone else stumbles across this, since it's not well documented:
If you're using Linkedin API V2, you do need to add some custom parameters to the Linkedin provider. The original issue was that the expected iss was undefined. Simply add issuer to OAuthConfig for the Linkedin provider:
issuer: 'https://www.linkedin.com'
This triggers a new error: 'jwks_uri must be configured on the issuer'. Add jwks_endpoint to OAuthConfig:
jwks_endpoint: 'https://www.linkedin.com/oauth/openid/jwks'
Again, this triggers a new error, where nextauth complains that the profile id is missing. Linkedin API V2 use "sub" not "id". Solved by overriding the profile function in OAuthConfig:
return { id: profile.sub, name: profile.name, firstname: profile.given_name, lastname: profile.family_name, email: profile.email, image: profile.picture, } }
Complete working provider for Linkedin using Linkedin API V2:
LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET, authorization: { params: { scope: 'profile email openid' } }, issuer: 'https://www.linkedin.com', 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 } }, })
God bless, this is the right solution and docs should be updated
@andersengenolsen If you have looked into the API deeply, what is the "sub" prop being returned by them in the response, and how can it be further used? Also, there is any way to know the users' actual LinkedIn ID (for example: personx is the user ID below)? https://linkedin.com/personx/
@andersengenolsen If you have looked into the API deeply, what is the "sub" prop being returned by them in the response, and how can it be further used? Also, there is any way to know the users' actual LinkedIn ID (for example: personx is the user ID below)? https://linkedin.com/personx/
Hi,
The sub parameter is an unique user identifier issued "within" the ID token (JWT) from Linkedin API V2 after authenticating. It's simply the user ID.
Documentation available here: https://learn.microsoft.com/en-us/linkedin/consumer/
Also a bit out of scope, since we're discussing Nextauth.. :)
@andersengenolsen If you have looked into the API deeply, what is the "sub" prop being returned by them in the response, and how can it be further used? Also, there is any way to know the users' actual LinkedIn ID (for example: personx is the user ID below)? https://linkedin.com/personx/
Hi,
The sub parameter is simply an unique user identifier issued "within" the ID token (JWT) from Linkedin API V2 after authenticating.
The latter seems to be a bit out of scope. Documentation available here: https://learn.microsoft.com/en-us/linkedin/consumer/
Thanks for you response
Basically they have restricted most of the api to a normal developer i believe? Like for example getting user profile information, also communication api
I'm still receiving
[next-auth][error][OAUTH_CALLBACK_ERROR]
https://next-auth.js.org/errors#oauth_callback_error read ECONNRESET {
error: Error: read ECONNRESET
at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20)
at TLSWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
name: 'OAuthCallbackError',
code: 'ECONNRESET'
},
providerId: 'linkedin',
message: 'read ECONNRESET'
}
"next": "^13.5.6",
"next-auth": "^4.24.4",
node 18.18.2
npm 9.8.1
Edit: This is working for me but I'm still receiving OAuthCallbackError intermittently.
LinkedinProvider({
clientId: process.env.LINKEDIN_CLIENT_ID || '',
clientSecret: process.env.LINKEDIN_CLIENT_SECRET || '',
issuer: "https://www.linkedin.com",
userinfo: {
url: 'https://api.linkedin.com/v2/userinfo',
},
authorization: {
url: 'https://www.linkedin.com/oauth/v2/authorization',
params: {
scope: 'profile email openid',
prompt: "consent",
access_type: "offline",
response_type: 'code'
},
},
token: {
url: 'https://www.linkedin.com/oauth/v2/accessToken',
},
jwks_endpoint: "https://www.linkedin.com/oauth/openid/jwks",
profile(profile, tokens) {
const defaultImage = 'https://cdn-icons-png.flaticon.com/512/174/174857.png';
return {
id: profile.sub,
name: profile.name,
email: profile.email,
image: profile.picture ?? defaultImage,
};
},
})
If you want to use it with an adapter you will need to increase the access_token default column length because Linkedin's access_token has more than 255 characters.
Is it still working for you guys? Even with the snippet posted, I'm still getting an error:
error: invalid_client, error_description: Client authentication failed
(I've double checked the client_id and client_secret multiple times already..
Hi @jormaj , did you find a workaround for this ? I'm facing the same error..
Has anyone resolved this? I'm also getting the invalid_client error and can't work out why.
I tried the same code in my route.ts inside app/api/auth/[...nextauth] folder:
LinkedInProvider({
clientId: process.env.LINKEDIN_CLIENT_ID as string,
clientSecret: process.env.LINKEDIN_CLIENT_SECRET as string,
authorization: { params: { scope: 'openid profile email' } },
issuer: 'https://www.linkedin.com',
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
}
},
})
],
yet still I am running into this error
Please read the error message completely. It says "The redirect_uri does not match the registered" It means that the callbackUrl you passed as parameter in you signIn() function call is not known to LinkedIn. You need to add it to you configuration in LinkedIn.
Thanks. Resolved it. 👍🏻
Hi everyone, I'm looking into this now. I think I found a bug in the LinkedIn OIDC implementation, I reached out to them and awaiting response.
Hi everyone, I'm looking into this now. I think I found a bug in the LinkedIn OIDC implementation, I reached out to them and awaiting response.
Ah fantastic! Thanks for looking into this. Can confirm it's still an issue at my end.
Hi everyone, I'm looking into this now. I think I found a bug in the LinkedIn OIDC implementation, I reached out to them and awaiting response.
is this in reference to this error message?
[auth][error] OperationProcessingError: "response" is not a conform Authorization Server Metadata response
because i'm also coming to the same conclusion
SOLVED
After some debugging I managed to solve this. In case anyone else stumbles across this, since it's not well documented: If you're using Linkedin API V2, you do need to add some custom parameters to the Linkedin provider. The original issue was that the expected iss was undefined. Simply add issuer to OAuthConfig for the Linkedin provider:
issuer: 'https://www.linkedin.com'
This triggers a new error: 'jwks_uri must be configured on the issuer'. Add jwks_endpoint to OAuthConfig:jwks_endpoint: 'https://www.linkedin.com/oauth/openid/jwks'
Again, this triggers a new error, where nextauth complains that the profile id is missing. Linkedin API V2 use "sub" not "id". Solved by overriding the profile function in OAuthConfig:return { id: profile.sub, name: profile.name, firstname: profile.given_name, lastname: profile.family_name, email: profile.email, image: profile.picture, } }
Complete working provider for Linkedin using Linkedin API V2:
LinkedIn({ clientId: process.env.LINKEDIN_ID, clientSecret: process.env.LINKEDIN_SECRET, authorization: { params: { scope: 'profile email openid' } }, issuer: 'https://www.linkedin.com', 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 } }, })
God bless, this is the right solution and docs should be updated
I tried this exact same configuration in LinkedInProvider, but I am not getting user information.
In the signIn callback, console the response
It's not giving any error, but I want user info like email, name etc I am using next-auth 4.22.3
The main problem is that LinkedIn is currently not spec-compliant as per the OIDC spec.
https://www.linkedin.com/oauth/.well-known/openid-configuration
returns issuer
as https://www.linkedin.com
while it should be https://www.linkedin.com/oauth
, or the https://www.linkedin.com/.well-known/openid-configuration
URL should not 404 but return the same as https://www.linkedin.com/oauth/.well-known/openid-configuration
does. I reached out to them but haven't gotten a response yet.
While this is the case, I cannot easily update the default provider config. I'm thinking about adding a new conform()
method to issuer
, similarly to what token
has: https://github.com/nextauthjs/next-auth/blob/26c47a8e1ae3e7b1069878003fe15da4e1a1928b/packages/core/src/providers/oauth.ts#L264C1-L265
Is there any update for this @balazsorban44 ? In v5 beta 4 I'm still gettin the following error despite having implemented the fixes here:
[auth][error] OperationProcessingError: "response" is not a conform Authorization Server Metadata response
at Module.processDiscoveryResponse (webpack-internal:///(rsc)/./node_modules/oauth4webapi/build/index.js:259:15)
at getAuthorizationUrl (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/actions/signin/authorization-url.js:24:68)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async Module.signIn (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/actions/signin/index.js:19:60)
at async AuthInternal (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/index.js:71:24)
at async Auth (webpack-internal:///(rsc)/./node_modules/@auth/core/index.js:123:29)
at async eval (webpack-internal:///(rsc)/./node_modules/next/dist/esm/server/future/route-modules/app-route/module.js:218:37)
✓ Compiled in 491ms (2078 modules)
Here's my config:
import LinkedIn from "next-auth/providers/linkedin";
import type { NextAuthConfig } from "next-auth";
export default {
providers: [
LinkedIn({
// clientId: process.env.LINKEDIN_ID!,
// clientSecret: process.env.LINKEDIN_SECRET!,
authorization: {
params: {
scope:
"profile openid email w_member_social r_organization_social w_organization_social rw_organization_admin r_1st_connections_size",
},
},
issuer: "https://www.linkedin.com",
jwks_endpoint: "https://www.linkedin.com/oauth/openid/jwks",
async profile(profile) {
console.log("PROFILE FROM LINKEDIN:", profile);
return {
id: profile.sub,
name: profile.name,
firstname: profile.given_name,
lastname: profile.family_name,
email: profile.email,
};
},
}),
],
} satisfies NextAuthConfig;
No update, LinkedIn has not answered me yet. I pinged them again.
The solution for them would be a single rewrite from https://www.linkedin.com/.well-known/openid-configuration to https://www.linkedin.com/oauth/.well-known/openid-configuration to be spec-compliant.
When they fix this, the minimal config can simply be:
import LinkedIn from "next-auth/providers/linkedin"
import type { NextAuthConfig } from "next-auth"
export default { providers: [ LinkedIn ] } satisfies NextAuthConfig
Is there a workaround for v5 beta 4 for now?
Not without a new release/feature as mentioned here: https://github.com/nextauthjs/next-auth/issues/8831#issuecomment-1866910591
We would need to "fake" spec-conformity for LinkedIn.
Not even with a custom provider?
We don't want to support non-spec OAuth providers, the industry needs to converge on specifications, especially if they are committing to it.
Of course you can hack around it, but if you are not sure how to, it's probably best to wait out. A PR for the proposed conform()
method is welcome, if you are interested.
Totally get this. At the same time, LinkedIn has proven they give zero cares about their API or conformity. Realistically, they'll never implement this properly. I'll take a stab at the conform()
method in the next couple of weeks after I get a few things off my plate. Thanks for outlining the suggested fix, @balazsorban44 !