UNPKG

@adonisjs/ally

Version:

Social authentication provider for AdonisJS

229 lines (227 loc) 6.73 kB
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 };