UNPKG

ally-42-oauth

Version:

AdonisJS Ally driver for 42 School's Intra OAuth2 authentication

125 lines (124 loc) 4.51 kB
/* |-------------------------------------------------------------------------- | 42 School OAuth driver for AdonisJS Ally |-------------------------------------------------------------------------- */ import { Oauth2Driver } from '@adonisjs/ally'; /** * 42 School OAuth driver implementation for AdonisJS Ally. */ export class FortyTwoDriver extends Oauth2Driver { config; /** * The URL for the redirect request. */ authorizeUrl = 'https://api.intra.42.fr/oauth/authorize'; /** * The URL to exchange the authorization code for the access token. */ accessTokenUrl = 'https://api.intra.42.fr/oauth/token'; /** * The URL to get the user details. */ userInfoUrl = 'https://api.intra.42.fr/v2/me'; codeParamName = 'code'; errorParamName = 'error'; stateCookieName = 'forty_two_oauth_state'; stateParamName = 'state'; scopeParamName = 'scope'; scopesSeparator = ' '; constructor(ctx, config) { super(ctx, config); this.config = config; this.loadState(); } /** * Configure the authorization redirect request. */ configureRedirectRequest(request) { request.param('response_type', 'code'); } /** * Check if the error received during redirect means "ACCESS DENIED". */ accessDenied() { return this.ctx.request.input('error') === 'access_denied'; } /** * Get the user details by querying the 42 API. */ async user(callback) { const accessToken = await this.accessToken(); const request = this.httpClient(this.config.userInfoUrl || this.userInfoUrl); request.header('Authorization', `Bearer ${accessToken.token}`); if (typeof callback === 'function') { callback(request); } const response = await request.get(); let userData = response.data || response.body || response; if (typeof userData === 'string') { try { userData = JSON.parse(userData); } catch (e) { throw new Error(`Failed to parse 42 API response as JSON: ${userData}`); } } if (!userData || typeof userData !== 'object') { throw new Error(`42 API returned no user data. Response status: ${response.status}`); } if (!userData.id) { throw new Error(`42 API returned invalid user data structure. User data: ${JSON.stringify(userData)}`); } return { id: userData.id, nickName: userData.login, name: userData.displayname || `${userData.first_name} ${userData.last_name}`, email: userData.email, emailVerificationState: 'unsupported', avatarUrl: userData.image?.link || null, original: userData, token: accessToken, }; } async userFromToken(accessToken, callback) { const request = this.httpClient(this.config.userInfoUrl || this.userInfoUrl); request.header('Authorization', `Bearer ${accessToken}`); if (typeof callback === 'function') { callback(request); } const response = await request.get(); let userData = response.data || response.body || response; if (typeof userData === 'string') { try { userData = JSON.parse(userData); } catch (e) { throw new Error(`Failed to parse 42 API response as JSON: ${userData}`); } } if (!userData || typeof userData !== 'object') { throw new Error(`42 API returned no user data. Response status: ${response.status}`); } if (!userData.id) { throw new Error(`42 API returned invalid user data structure. User data: ${JSON.stringify(userData)}`); } return { id: userData.id, nickName: userData.login, name: userData.displayname || `${userData.first_name} ${userData.last_name}`, email: userData.email, emailVerificationState: 'unsupported', avatarUrl: userData.image?.link || null, original: userData, token: { token: accessToken, type: 'bearer' }, }; } } /** * The factory function to reference the driver implementation * inside the "config/ally.ts" file. */ export function FortyTwoDriverService(config) { return (ctx) => new FortyTwoDriver(ctx, config); }