UNPKG

clever-passport

Version:

Passport strategy for Instant Login with Clever

208 lines (182 loc) 6.79 kB
/** * Module dependencies. */ let util = require('util') , querystring = require('querystring') , fetch = require('node-fetch') , OAuth2Strategy = require('passport-oauth2').Strategy , InternalOAuthError = require('passport-oauth2').InternalOAuthError; /** * `Strategy` constructor. * * The Clever authentication strategy authenticates requests by delegating to * Clever using the OAuth 2.0 protocol. * * Applications must specify a `verify` callback which accepts an `accessToken`, * `refreshToken` and service-specific `profile`, and then calls the `done` * callback supplying a `user`, which should be set to `false` if the * credentials are not valid. If an exception occured, `err` should be set. * * Options: * - `clientID` Clever application's client id * - `clientSecret` Clever application's client secret * - `callbackURL` URL to which Clever will redirect the user after granting authorization * * Examples: * * passport.use(new CleverStrategy({ * clientID: "<client id>", * clientSecret: "<client secret>", * callbackURL: "<callback url>", * passReqToCallback: true * }, function(req, accessToken, refreshToken, profile, done) { * User.findOrCreate({ cleverId: profile.data.id }, function(err, user) { * return done(err, user); * }); * } * )); * * @param {Object} options * @param {Function} verify * @api public */ function Strategy(options, verify) { options = options || {}; options.authorizationURL = options.authorizationURL || 'https://clever.com/oauth/authorize'; options.tokenURL = options.tokenURL || 'https://clever.com/oauth/tokens'; OAuth2Strategy.call(this, options, verify); this.name = 'clever'; this._passReqToCallback = options.passReqToCallback; this._oauth2.useAuthorizationHeaderforGET(true); this._oauth2.getOAuthAccessToken = function (code, params, callback) { var params = params || {}; var codeParam = (params.grant_type === 'refresh_token') ? 'refresh_token' : 'code'; params[codeParam] = code; var post_data = querystring.stringify(params); var post_headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': 'Basic ' + Buffer.from(this._clientId + ":" + this._clientSecret).toString('base64') }; this._request("POST", this._getAccessTokenUrl(), post_headers, post_data, null, function (error, data, response) { if (error) { console.log("error"); callback(error); } else { var results; try { results = JSON.parse(data); } catch (e) { results = querystring.parse(data); } let access_token = results["access_token"]; let refresh_token = results["refresh_token"]; delete results["refresh_token"]; callback(null, access_token, refresh_token, results); } }); } } /** * Inherit from `OAuth2Strategy`. */ util.inherits(Strategy, OAuth2Strategy); /** * Retrieve user profile from Clever for Instant Login. * * This function constructs a normalized profile, with the following properties: * * - `id` * - `name` * - `email` * - `sections` * - `grade` * - `id` * - `name` * - `students` (Array) * - `subject` * - `teacher` * - `teachers` (Array) * * @param {String} accessToken * @param {Function} done * @api protected */ OAuth2Strategy.prototype.userProfile = function (accessToken, done) { let options = { method: 'GET', headers: { 'Authorization': 'Bearer ' + accessToken, 'Content-Type': 'application/json' } }; function getMe(options) { fetch('https://api.clever.com/v2.1/me', options) .then(response => response.json()) .then(data => { let link = data.links.filter(obj => { if (obj.rel == "canonical") { return obj; } }); let user = data.data; getUserInfo(link[0].uri, user, options); }) .catch(error => { return done(error); }); } function getUserInfo(link, user, options) { fetch(`https://api.clever.com/${link}`, options) .then(response => response.json()) .then(info => { user.email = info.data.email; user.name = info.data.name; if (user.type === "teacher") { getUserSectionsWithStudents(user, options); } else { return done(null, user); } }) .catch(error => { return done(error); }); } let studentsBySection = []; function getUserSectionsWithStudents(user, options) { fetch(`https://api.clever.com/v2.1/${user.type}s/${user.id}/sections`, options) .then(response => response.json()) .then(async info => { let sections = info.data; // for every section, go fetch the data for each of its students await Promise.all(sections.map(async section => { await getStudentsForSection(section.data.id, options); })); let sectionsWithStudents = sections.map((section, index) => { let newSection = section.data; newSection.students = studentsBySection[index]; return newSection; }); user.sections = sectionsWithStudents; return done(null, user); }) .catch(error => { return done(error); }); } async function getStudentsForSection(section_id, options) { await fetch(`https://api.clever.com/v2.1/sections/${section_id}/students`, options) .then(response => response.json()) .then(info => { let studentsForSection = [info.data[0].data]; studentsBySection.push(studentsForSection); }) .catch(error => { return done(error); }); } getMe(options); }; /** * Expose `Strategy`. */ module.exports = Strategy;