@adonisjs/ally
Version:
Social authentication provider for AdonisJS
229 lines (227 loc) • 6.73 kB
JavaScript
import {
Oauth2Driver
} from "../../chunk-EXGR73T6.js";
import "../../chunk-NK6X76EQ.js";
import "../../chunk-TSIMPJ6I.js";
// src/drivers/github.ts
var GithubDriver = class extends Oauth2Driver {
/**
* @param ctx - The HTTP context
* @param config - Configuration for the GitHub driver
*/
constructor(ctx, config) {
super(ctx, config);
this.config = config;
this.loadState();
}
config;
/**
* GitHub token endpoint URL.
*/
accessTokenUrl = "https://github.com/login/oauth/access_token";
/**
* GitHub authorization endpoint URL.
*/
authorizeUrl = "https://github.com/login/oauth/authorize";
/**
* GitHub profile endpoint URL.
*/
userInfoUrl = "https://api.github.com/user";
/**
* GitHub email endpoint URL.
*/
userEmailUrl = "https://api.github.com/user/emails";
/**
* The param name for the authorization code
*/
codeParamName = "code";
/**
* The param name for the error
*/
errorParamName = "error";
/**
* Cookie name for storing the "gh_oauth_state"
*/
stateCookieName = "gh_oauth_state";
/**
* Parameter name to be used for sending and receiving the state
* from Github
*/
stateParamName = "state";
/**
* Parameter name for defining the scopes
*/
scopeParamName = "scope";
/**
* Scopes separator
*/
scopesSeparator = " ";
/**
* Configures the redirect request with default scopes and GitHub-specific
* parameters like allow_signup and login.
*
* @param request - The redirect request to configure
*/
configureRedirectRequest(request) {
request.scopes(this.config.scopes || ["user"]);
if (this.config.allowSignup !== void 0) {
request.param("allow_signup", this.config.allowSignup);
}
if (this.config.login) {
request.param("login", this.config.login);
}
}
/**
* Configures the access token request with GitHub-specific requirements.
* GitHub doesn't accept the grant_type field that is set by default.
*
* @param request - The API request to configure
*/
configureAccessTokenRequest(request) {
if (!this.isStateless) {
request.field("state", this.stateCookieValue);
}
request.clearField("grant_type");
}
/**
* Creates an authenticated HTTP request with the proper authorization
* header for GitHub API calls.
*
* @param url - The API endpoint URL
* @param token - The access token
*/
getAuthenticatedRequest(url, token) {
const request = this.httpClient(url);
request.header("Authorization", `token ${token}`);
request.header("Accept", "application/json");
request.parseAs("json");
return request;
}
/**
* Fetches the authenticated user's profile information from the GitHub API.
*
* @param token - The access token
* @param callback - Optional callback to customize the API request
*
* @see https://docs.github.com/en/rest/reference/users#get-the-authenticated-user
*/
async getUserInfo(token, callback) {
const request = this.getAuthenticatedRequest(this.config.userInfoUrl || this.userInfoUrl, token);
if (typeof callback === "function") {
callback(request);
}
const body = await request.get();
return {
id: body.id,
nickName: body.name,
email: body.email,
// May not always be there
emailVerificationState: body.email ? "verified" : "unsupported",
name: body.name ?? body.login,
avatarUrl: body.avatar_url,
original: body
};
}
/**
* Fetches the user's email addresses from the GitHub API. This is needed
* when the user's email is not included in the basic profile response.
* Returns the primary verified email, or the first available email.
*
* @param token - The access token
* @param callback - Optional callback to customize the API request
*
* @see https://docs.github.com/en/rest/reference/users#list-email-addresses-for-the-authenticated-user
*/
async getUserEmail(token, callback) {
const request = this.getAuthenticatedRequest(
this.config.userEmailUrl || this.userEmailUrl,
token
);
if (typeof callback === "function") {
callback(request);
}
try {
let emails = await request.get();
emails = emails.sort((email) => email.primary ? -1 : 1);
let mainEmail = emails.find((email) => email.verified);
if (!mainEmail) {
mainEmail = emails[0];
}
return mainEmail;
} catch (error) {
if (error && typeof error === "object" && "response" in error && error.response && typeof error.response === "object" && "statusCode" in error.response && error.response.statusCode === 404) {
return;
}
throw error;
}
}
/**
* Check if the error from the callback indicates that the user
* denied authorization.
*/
accessDenied() {
const error = this.getError();
if (!error) {
return false;
}
return error === "access_denied";
}
/**
* 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('github').user()
* console.log(user.name, user.email)
* ```
*/
async user(callback) {
const token = await this.accessToken(callback);
const user = await this.getUserInfo(token.token, callback);
if (!user.email) {
this.ctx.logger.trace("Fetching github user email separately");
const emailResponse = await this.getUserEmail(token.token, callback);
if (emailResponse) {
user.email = emailResponse.email;
user.emailVerificationState = emailResponse.verified ? "verified" : "unverified";
}
}
return {
...user,
token
};
}
/**
* Get the user's profile and email information using an existing
* access token.
*
* @param token - The GitHub access token
* @param callback - Optional callback to customize the API request
*
* @example
* ```ts
* const user = await ally.use('github').userFromToken(accessToken)
* ```
*/
async userFromToken(token, callback) {
const user = await this.getUserInfo(token, callback);
if (!user.email) {
this.ctx.logger.trace("Fetching github user email separately");
const emailResponse = await this.getUserEmail(token, callback);
if (emailResponse) {
user.email = emailResponse.email;
user.emailVerificationState = emailResponse.verified ? "verified" : "unverified";
}
}
return {
...user,
token: { token, type: "bearer" }
};
}
};
export {
GithubDriver
};