npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@activelylearn/passport-clever-oauth20

v0.1.2

Published

PassportJS + Clever Instant Login

Downloads

132

Readme

Supporting Clever Instant Login

Clever integrates with many application types through Instant Login. Instant Login is powered by the OAuth2 standard, so it's easy to get started quickly in our technology of choice. Let's take a look at an example NodeJS web application using the PassportJS framework.

Dependencies

App Creation

An application must be created in Clever's developer portal. Signing up for a developer account creates this application. It can be found later by browsing to Applications in the Settings section of left-hand menu of the developer portal. A demo district is also generated for testing purposes.

Here are a few resources that should not be missed:

  1. Getting Started
  2. Obtaining Bearer Tokens
  3. Testing Your Integration

App Configuration

We've stored configuration for our app in a separate file. The values to complete this configuration are found in your application and district within the developer portal. We've separated our client id from the instant login url below. The PassportJS framework will handle this addition at runtime, and this relieves us from repeating the client id in our configuration. We have used the /instant-login endpoint for the login url, but this application would function the same if the /authorize endpoint were substituted.

var appConfig = {
  'port' : 3000,
  'sessionSecret' : '{SessionSecret}',
  'requestUrl' : 'https://api.clever.com',
  'oauth' : {
    // credentials
    'clientId' : '{ClientId}',
    'clientSecret' : '{ClientSecret}',
    // instant clever login urls
    'instantLoginUrl' : 'https://clever.com/oauth/instant-login?district_id={DistrictId}',
    'instantCallbackUrl' : 'https://{DomainName}/auth/clever-instant/callback',
    // token url
    'tokenUrl' : 'https://clever.com/oauth/tokens?grant_type=authorization_code' 
  }
};
module.exports = appConfig;

Note: Placeholder values are surrounded in braces.

Node.js Modules

We'll use a few Node.js dependencies and our config from above. We did run into one small issue where OAuth2Strategy did not match Clever's requirement of passing the client credentials to the token endpoint. The built-in strategy passed these values in the query string, but Clever requires them in a basic authentication header. It was really just a couple of lines that needed to change, but they were deep in the framework. We will gloss over this detail below for simplicity, but you can compare bak files in the clever-passport-oauth folder for clarity.

var express = require('express');
var passport = require('passport');
//var OAuth2Strategy = require('passport-oauth').OAuth2Strategy;
var OAuth2Strategy = require('./clever-passport-oauth/index').OAuth2Strategy;
var request = require('request');
var appConfig = require('./appConfig');

Note: Passport's OAuth2 strategy was customized to support Clever's token URL.

App Setup

There is some necessary setup to get the NodeJS frameworks configured. It has little to do with our application's purpose, but we'll show it here anyway.

var app = express();
app.set('port', (process.env.PORT || appConfig.port));

app.use(express.static(__dirname + '/public'));
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: appConfig.sessionSecret, resave: true, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());

Passport for OAuth

Passport Configuration

This PassportJS framework will handle the OAuth details for us. We must give it some configuration and direction on parsing and persisting the returned data.

// setup instant login through passport
passport.use('clever-instant', new OAuth2Strategy({
    authorizationURL: appConfig.oauth.instantLoginUrl,
    tokenURL: appConfig.oauth.tokenUrl,
    clientID: appConfig.oauth.clientId,
    clientSecret: appConfig.oauth.clientSecret,
    callbackURL: appConfig.oauth.instantCallbackUrl
  },
  function(accessToken, refreshToken, profile, done) {
    var fakeUser = { 'accessToken' : accessToken };
    var options =  { 'method' : 'GET',
                     'uri' : '/me'
                   };
    var callback = function(user, data) {
      var fullUser = null;
      if(data  && data.data) {
        fullUser = data.data;
        fullUser.accessToken = user.accessToken;
      }
      done(null, fullUser);
    };
    handleRequest(fakeUser,options,callback);
  }
));

Passport Persistence

A naive persistence layer is used for this demo. You'll want something more durable. The PassportJS framework just needs to store a user's tokens between request.

var users = [];

// retrieve user for request
function findUser(request) { 
  return users[request.session.passport.user]; 
};

// store user in persistent storage for passport
passport.serializeUser(function(user, done) {
  users[user.id] = user;
  done(null, user.id);
});

// retrieve user from persistent store for passport 
passport.deserializeUser(function(id, done) {
  var user = users[id];
  done(null, user);
});

// require token for session enabled request
function requireToken() {
  return function(req, res, next) {
     if (req.session.passport &&
         req.session.passport.user &&
         findUser(req)) {
       next();
     }
     else {
       res.redirect('/login');
     }
   }
}

Application Logic

Common Utilities

We'll need to make API calls, and this function will make that a little easier.

// simplified request handling
function handleRequest(user, options, callback) {
  var headers =  { 'Authorization': 'Bearer ' + user.accessToken, 
                   'Content-Type': 'application/json',
                   'User-Agent': 'My App'
                 };
  request({ 
        'uri' : appConfig.requestUrl + options.uri,
        'method' : 'GET',
        'headers' : headers 
  }, 
  function(error, response, body) {
    if(error) {
      callback(user,null);
      return;
    }
    if (response.statusCode === 200) {
      callback(user,JSON.parse(body));
    } else {
      callback(user,response.statusCode);
    }
  });
};

App URLs

We need to expose URLs that route our applications' traffic to Passport's functionality.

// entry point for clever instant auth
app.get('/auth/clever-instant', passport.authenticate('clever-instant'));

// callback for clever auth
app.get('/auth/clever-instant/callback', 
  passport.authenticate('clever-instant', { successRedirect: '/',
                                            failureRedirect: '/login'
                                          }));

// login setup
app.get('/login', function(req, res){
  res.send('<a href="/auth/clever/instant"><img src="images/sign-in-with-clever-full.png" /></a>');
});

// list the user's profile if authenticated
app.get('/',requireToken(), function(request, response) {
    var options =  { 'method' : 'GET', 
                     'uri' : '/me'
                   };
    var callback = function(user, data) {
      response.send('<pre>' + JSON.stringify(data,null,'  ') + '</pre>');
    };
    handleRequest(findUser(request),options,callback);  
});

App Launch

Finally, we just start the server.

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

Application Experience

Expect the following when testing the application:

  1. The "Login with Clever" button will greet you.
  2. This button will launch Clever's demo login screen.
  3. Successful login will diplay the user's profile in raw JSON.

Enjoy!