From abc4b135c321cabae8527f7ee84caf2c54632c06 Mon Sep 17 00:00:00 2001 From: tranminhwang Date: Thu, 24 Aug 2023 00:30:49 +0700 Subject: [PATCH 1/5] update LinkedIn provider options and profile with OAuth2.0 --- packages/core/src/providers/linkedin.ts | 44 ++++++-------------- packages/next-auth/src/providers/linkedin.ts | 44 ++++++-------------- 2 files changed, 26 insertions(+), 62 deletions(-) diff --git a/packages/core/src/providers/linkedin.ts b/packages/core/src/providers/linkedin.ts index 30dc9f9312..22658d3b6f 100644 --- a/packages/core/src/providers/linkedin.ts +++ b/packages/core/src/providers/linkedin.ts @@ -14,19 +14,11 @@ interface Identifier { identifier: string } -interface Element { - identifiers?: Identifier[] -} - -export interface LinkedInProfile extends Record { +export interface LinkedInProfile extends Record { id: string - localizedFirstName: string - localizedLastName: string - profilePicture: { - "displayImage~": { - elements?: Element[] - } - } + name: string + email: string + image: string } /** @@ -86,31 +78,21 @@ export default function LinkedIn

( type: "oauth", authorization: { url: "https://www.linkedin.com/oauth/v2/authorization", - params: { scope: "r_liteprofile r_emailaddress" }, + params: { scope: "openid profile email" }, }, token: "https://www.linkedin.com/oauth/v2/accessToken", client: { token_endpoint_auth_method: "client_secret_post", }, - userinfo: { - url: "https://api.linkedin.com/v2/me", - params: { - projection: `(id,localizedFirstName,localizedLastName,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() + userinfo: "https://api.linkedin.com/v2/userinfo", + issuer: "https://www.linkedin.com", + jwks_endpoint: "https://www.linkedin.com/oauth/openid/jwks", + async profile(profile) { return { - id: profile.id, - name: `${profile.localizedFirstName} ${profile.localizedLastName}`, - email: emailData?.elements?.[0]?.["handle~"]?.emailAddress, - image: - profile.profilePicture?.["displayImage~"]?.elements?.[0] - ?.identifiers?.[0]?.identifier, + id: profile.sub, + name: profile.name, + email: profile.email, + image: profile.picture } }, style: { diff --git a/packages/next-auth/src/providers/linkedin.ts b/packages/next-auth/src/providers/linkedin.ts index deb63cd091..f490fcd2a4 100644 --- a/packages/next-auth/src/providers/linkedin.ts +++ b/packages/next-auth/src/providers/linkedin.ts @@ -4,19 +4,11 @@ interface Identifier { identifier: string } -interface Element { - identifiers?: Identifier[] -} - -export interface LinkedInProfile extends Record { +export interface LinkedInProfile extends Record { id: string - localizedFirstName: string - localizedLastName: string - profilePicture: { - "displayImage~": { - elements?: Element[] - } - } + name: string + email: string + image: string } export default function LinkedIn

( @@ -28,31 +20,21 @@ export default function LinkedIn

( type: "oauth", authorization: { url: "https://www.linkedin.com/oauth/v2/authorization", - params: { scope: "r_liteprofile r_emailaddress" }, + params: { scope: "openid profile email" }, }, token: "https://www.linkedin.com/oauth/v2/accessToken", client: { token_endpoint_auth_method: "client_secret_post", }, - userinfo: { - url: "https://api.linkedin.com/v2/me", - params: { - projection: `(id,localizedFirstName,localizedLastName,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() + userinfo: "https://api.linkedin.com/v2/userinfo", + issuer: "https://www.linkedin.com", + jwks_endpoint: "https://www.linkedin.com/oauth/openid/jwks", + async profile(profile) { return { - id: profile.id, - name: `${profile.localizedFirstName} ${profile.localizedLastName}`, - email: emailData?.elements?.[0]?.["handle~"]?.emailAddress, - image: - profile.profilePicture?.["displayImage~"]?.elements?.[0] - ?.identifiers?.[0]?.identifier, + id: profile.sub, + name: profile.name, + email: profile.email, + image: profile.picture } }, style: { From c5b80c8c15f282eada285196ffb2b42d76634af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 25 Sep 2023 11:06:17 +0200 Subject: [PATCH 2/5] Update linkedin.ts --- packages/core/src/providers/linkedin.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/core/src/providers/linkedin.ts b/packages/core/src/providers/linkedin.ts index ea658450c2..d3143160b1 100644 --- a/packages/core/src/providers/linkedin.ts +++ b/packages/core/src/providers/linkedin.ts @@ -71,17 +71,8 @@ export default function LinkedIn

( return { id: "linkedin", name: "LinkedIn", - type: "oauth", - authorization: { - url: "https://www.linkedin.com/oauth/v2/authorization", - params: { scope: "openid profile email" }, - }, type: "oidc", - token: "https://www.linkedin.com/oauth/v2/accessToken", - client: { - token_endpoint_auth_method: "client_secret_post", - }, - userinfo: "https://api.linkedin.com/v2/userinfo", + client: { token_endpoint_auth_method: "client_secret_post" }, issuer: "https://www.linkedin.com", jwks_endpoint: "https://www.linkedin.com/oauth/openid/jwks", async profile(profile) { From e8d900e3671c4ae0f23aef575ad6db5bd0baf507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 25 Sep 2023 11:07:29 +0200 Subject: [PATCH 3/5] Update linkedin.ts --- packages/next-auth/src/providers/linkedin.ts | 44 ++++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/packages/next-auth/src/providers/linkedin.ts b/packages/next-auth/src/providers/linkedin.ts index f490fcd2a4..deb63cd091 100644 --- a/packages/next-auth/src/providers/linkedin.ts +++ b/packages/next-auth/src/providers/linkedin.ts @@ -4,11 +4,19 @@ interface Identifier { identifier: string } -export interface LinkedInProfile extends Record { +interface Element { + identifiers?: Identifier[] +} + +export interface LinkedInProfile extends Record { id: string - name: string - email: string - image: string + localizedFirstName: string + localizedLastName: string + profilePicture: { + "displayImage~": { + elements?: Element[] + } + } } export default function LinkedIn

( @@ -20,21 +28,31 @@ export default function LinkedIn

( type: "oauth", authorization: { url: "https://www.linkedin.com/oauth/v2/authorization", - params: { scope: "openid profile email" }, + params: { scope: "r_liteprofile r_emailaddress" }, }, token: "https://www.linkedin.com/oauth/v2/accessToken", client: { token_endpoint_auth_method: "client_secret_post", }, - userinfo: "https://api.linkedin.com/v2/userinfo", - issuer: "https://www.linkedin.com", - jwks_endpoint: "https://www.linkedin.com/oauth/openid/jwks", - async profile(profile) { + userinfo: { + url: "https://api.linkedin.com/v2/me", + params: { + projection: `(id,localizedFirstName,localizedLastName,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.sub, - name: profile.name, - email: profile.email, - image: profile.picture + id: profile.id, + name: `${profile.localizedFirstName} ${profile.localizedLastName}`, + email: emailData?.elements?.[0]?.["handle~"]?.emailAddress, + image: + profile.profilePicture?.["displayImage~"]?.elements?.[0] + ?.identifiers?.[0]?.identifier, } }, style: { From 0a7e039827a5404d62fa5bb3d11494799c6bfc6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 25 Sep 2023 11:14:07 +0200 Subject: [PATCH 4/5] Update linkedin.ts --- packages/core/src/providers/linkedin.ts | 36 ++++++++++++++----------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/packages/core/src/providers/linkedin.ts b/packages/core/src/providers/linkedin.ts index d3143160b1..385aea03b9 100644 --- a/packages/core/src/providers/linkedin.ts +++ b/packages/core/src/providers/linkedin.ts @@ -8,17 +8,21 @@ * * @module providers/linkedin */ -import type { OAuthConfig, OAuthUserConfig } from "./index.js" +import type { OIDCConfig, OIDCUserConfig } from "./index.js" -export interface LinkedInProfile extends Record { - id: string - name: string - email: string - image: string +export interface LinkedInProfile extends Record { + "sub": string, + "name": string, + "given_name": string, + "family_name": string, + "picture": string, + "locale": string, + "email": string, + "email_verified": boolean } /** - * Add Linkedin login to your page. + * Add LinkedIn login to your page. * * ### Setup * @@ -30,27 +34,27 @@ export interface LinkedInProfile extends Record { * #### Configuration *```js * import Auth from "@auth/core" - * import Linkedin from "@auth/core/providers/linkedin" + * import LinkedIn from "@auth/core/providers/linkedin" * * const request = new Request(origin) * const response = await Auth(request, { - * providers: [Linkedin({ clientId: LINKEDIN_CLIENT_ID, clientSecret: LINKEDIN_CLIENT_SECRET })], + * providers: [LinkedIn({ clientId: LINKEDIN_CLIENT_ID, clientSecret: LINKEDIN_CLIENT_SECRET })], * }) * ``` * * ### Resources * - * - [Linkedin OAuth documentation](https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow) - * - [Linkedin app console](https://www.linkedin.com/developers/apps/) + * - [LinkedIn OAuth documentation](https://docs.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow) + * - [LinkedIn app console](https://www.linkedin.com/developers/apps/) * * ### Notes * - * By default, Auth.js assumes that the Linkedin provider is - * based on the [OAuth 2](https://www.rfc-editor.org/rfc/rfc6749.html) specification. + * By default, Auth.js assumes that the LinkedIn provider is + * based on the [OIDC](https://openid.net/specs/openid-connect-core-1_0.html) specification. * * :::tip * - * The Linkedin provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/linkedin.ts). + * The LinkedIn provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/linkedin.ts). * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/providers/custom-provider#override-default-options). * * ::: @@ -66,8 +70,8 @@ export interface LinkedInProfile extends Record { * ::: */ export default function LinkedIn

( - options: OAuthUserConfig

-): OAuthConfig

{ + options: OIDCUserConfig

+): OIDCConfig

{ return { id: "linkedin", name: "LinkedIn", From dfe0e0419d6874512c8b4942033eb5888305d7be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 25 Sep 2023 11:15:11 +0200 Subject: [PATCH 5/5] Update linkedin.ts --- packages/core/src/providers/linkedin.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/core/src/providers/linkedin.ts b/packages/core/src/providers/linkedin.ts index 385aea03b9..f4ed1b55bd 100644 --- a/packages/core/src/providers/linkedin.ts +++ b/packages/core/src/providers/linkedin.ts @@ -10,15 +10,16 @@ */ import type { OIDCConfig, OIDCUserConfig } from "./index.js" +/** @see https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin-v2#response-body-schema */ export interface LinkedInProfile extends Record { - "sub": string, - "name": string, - "given_name": string, - "family_name": string, - "picture": string, - "locale": string, - "email": string, - "email_verified": boolean + sub: string, + name: string, + given_name: string, + family_name: string, + picture: string, + locale: string, + email: string, + email_verified: boolean } /**