@adonisjs/ally
Version:
Social authentication provider for AdonisJS
202 lines (200 loc) • 5.96 kB
JavaScript
import {
Oauth2Driver
} from "../../chunk-EXGR73T6.js";
import "../../chunk-NK6X76EQ.js";
import "../../chunk-TSIMPJ6I.js";
// src/drivers/linked_in.ts
import { Exception } from "@adonisjs/core/exceptions";
var LinkedInDriver = class extends Oauth2Driver {
/**
* @param ctx - The HTTP context
* @param config - Configuration for the LinkedIn driver
*/
constructor(ctx, config) {
super(ctx, config);
this.config = config;
this.loadState();
}
config;
/**
* LinkedIn token endpoint URL.
*/
accessTokenUrl = "https://www.linkedin.com/oauth/v2/accessToken";
/**
* LinkedIn authorization endpoint URL.
*/
authorizeUrl = "https://www.linkedin.com/oauth/v2/authorization";
/**
* LinkedIn profile endpoint URL.
*/
userInfoUrl = "https://api.linkedin.com/v2/me";
/**
* LinkedIn email endpoint URL.
*/
userEmailUrl = "https://api.linkedin.com/v2/clientAwareMemberHandles";
/**
* The param name for the authorization code
*/
codeParamName = "code";
/**
* The param name for the error
*/
errorParamName = "error";
/**
* Cookie name for storing the "linkedin_oauth_state"
*/
stateCookieName = "linkedin_oauth_state";
/**
* Parameter name to be used for sending and receiving the state
* from linkedin
*/
stateParamName = "state";
/**
* Parameter name for defining the scopes
*/
scopeParamName = "scope";
/**
* Scopes separator
*/
scopesSeparator = " ";
/**
* Configures the redirect request with default scopes.
*
* @param request - The redirect request to configure
*/
configureRedirectRequest(request) {
request.scopes(this.config.scopes || ["r_emailaddress", "r_liteprofile"]);
request.param("response_type", "code");
}
/**
* Creates an authenticated HTTP request with the proper authorization
* header for LinkedIn API calls.
*
* @param url - The API endpoint URL
* @param token - The access token
*/
getAuthenticatedRequest(url, token) {
const request = this.httpClient(url);
request.header("Authorization", `Bearer ${token}`);
request.header("Accept", "application/json");
request.parseAs("json");
return request;
}
/**
* Fetches the authenticated user's profile information from the LinkedIn API.
*
* @param token - The access token
* @param callback - Optional callback to customize the API request
*/
async getUserInfo(token, callback) {
let url = this.config.userInfoUrl || this.userInfoUrl;
const request = this.getAuthenticatedRequest(url, token);
request.param(
"projection",
"(id,localizedLastName,localizedFirstName,vanityName,profilePicture(displayImage~digitalmediaAsset:playableStreams))"
);
if (typeof callback === "function") {
callback(request);
}
const body = await request.get();
let avatar = "";
if (body.profilePicture) {
const avatars = body.profilePicture["displayImage~"]["elements"] || [];
if (avatars.length && avatars[0].identifiers && avatars[0].identifiers.length) {
avatar = avatars[0].identifiers[0].identifier;
}
}
return {
id: body.id,
nickName: body.vanityName || `${body.localizedFirstName} ${body.localizedLastName}`,
name: `${body.localizedFirstName} ${body.localizedLastName}`,
avatarUrl: avatar,
original: body
};
}
/**
* Fetches the user's email address from the LinkedIn API.
* Requires the 'r_emailaddress' scope.
*
* @param token - The access token
* @param callback - Optional callback to customize the API request
*/
async getUserEmail(token, callback) {
let url = this.config.userEmailUrl || this.userEmailUrl;
const request = this.getAuthenticatedRequest(url, token);
request.param("q", "members");
request.param("projection", "(elements*(primary,type,handle~))");
if (typeof callback === "function") {
callback(request);
}
const body = await request.get();
let mainEmail = body.elements.find((resource) => {
return resource.type === "EMAIL" && resource["handle~"];
});
if (!mainEmail) {
throw new Exception(
'Cannot request user email. Make sure you are using the "r_emailaddress" scope'
);
}
return mainEmail["handle~"]["emailAddress"];
}
/**
* Check if the error from the callback indicates that the user
* denied authorization or cancelled the login.
*/
accessDenied() {
const error = this.getError();
if (!error) {
return false;
}
return error === "user_cancelled_login" || error === "user_cancelled_authorize";
}
/**
* Get the authenticated user's profile and email information using
* the authorization code from the callback request.
*
* @param callback - Optional callback to customize the API request
*
* @example
* ```ts
* const user = await ally.use('linkedin').user()
* console.log(user.name, user.email)
* ```
*/
async user(callback) {
const token = await this.accessToken(callback);
const user = await this.getUserInfo(token.token, callback);
const email = await this.getUserEmail(token.token, callback);
return {
...user,
email,
emailVerificationState: "unsupported",
token
};
}
/**
* Get the user's profile and email information using an existing
* access token.
*
* @param token - The LinkedIn access token
* @param callback - Optional callback to customize the API request
*
* @example
* ```ts
* const user = await ally.use('linkedin').userFromToken(accessToken)
* ```
*/
async userFromToken(token, callback) {
const user = await this.getUserInfo(token, callback);
const email = await this.getUserEmail(token, callback);
return {
...user,
email,
emailVerificationState: "unsupported",
token: { token, type: "bearer" }
};
}
};
export {
LinkedInDriver
};