ally-42-oauth
Version:
AdonisJS Ally driver for 42 School's Intra OAuth2 authentication
125 lines (124 loc) • 4.51 kB
JavaScript
/*
|--------------------------------------------------------------------------
| 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);
}