{"_id":"@activelylearn/passport-clever-oauth20","_rev":"5-1fa835d69de1ae2365bf127ce15e15e1","name":"@activelylearn/passport-clever-oauth20","description":"PassportJS + Clever Instant Login","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@activelylearn/passport-clever-oauth20","version":"0.1.0","description":"PassportJS + Clever Instant Login","engines":{"node":"5.9.1"},"main":"index.js","scripts":{"start":"node index.js"},"dependencies":{"ejs":"2.4.1","express":"4.13.3","passport":"0.3.2","passport-oauth":"1.0.0","cookie-parser":"1.4.3","body-parser":"1.15.2","express-session":"1.14.2","request":"2.79.0","passport-oauth2":"1.x.x","oauth":"0.9.x","passport-strategy":"1.x.x","uid2":"0.0.x","utils-merge":"1.x.x"},"gitHead":"996f69f91c5a66b87e82223a5a3d95d32a7004d4","_id":"@activelylearn/passport-clever-oauth20@0.1.0","_shasum":"c15ec06fb1e3877364d68a35b6b02a134739ffe2","_from":".","_npmVersion":"3.10.9","_nodeVersion":"6.9.4","_npmUser":{"name":"admehta01","email":"admehta01@gmail.com"},"dist":{"shasum":"c15ec06fb1e3877364d68a35b6b02a134739ffe2","tarball":"https://registry.npmjs.org/@activelylearn/passport-clever-oauth20/-/passport-clever-oauth20-0.1.0.tgz","integrity":"sha512-Gl8yOw35JNzGtkgeoFrlUdjuCO90mMirQjtYXiAB9Prsczu6VCNehdbGzveVNW6BlAyOyzWEzHyg7yzwTCJTFw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGvwgJFcr+J1fwQrIPGqgWdh8ZiI/C6qhtrC5XvK8XdDAiEA/hRm7PdBANuo0Dnzao3nDaMJ1e95OQCvpCwulLJIxh4="}]},"maintainers":[{"name":"admehta01","email":"admehta01@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/passport-clever-oauth20-0.1.0.tgz_1505343836133_0.013136544497683644"},"directories":{}},"0.1.1":{"name":"@activelylearn/passport-clever-oauth20","version":"0.1.1","description":"PassportJS + Clever Instant Login","engines":{"node":"5.9.1"},"main":"index.js","scripts":{"start":"node index.js"},"dependencies":{"ejs":"2.4.1","express":"4.13.3","passport":"0.3.2","passport-oauth":"1.0.0","cookie-parser":"1.4.3","body-parser":"1.15.2","express-session":"1.14.2","request":"2.79.0","passport-oauth2":"1.x.x","oauth":"0.9.x","passport-strategy":"1.x.x","uid2":"0.0.x","utils-merge":"1.x.x"},"gitHead":"877cac0cc30cca409c020209d5cfc543d1e48945","_id":"@activelylearn/passport-clever-oauth20@0.1.1","_shasum":"5e7044834c822940185fb338dc09e04e844f556c","_from":".","_npmVersion":"3.10.9","_nodeVersion":"6.9.4","_npmUser":{"name":"admehta01","email":"admehta01@gmail.com"},"dist":{"shasum":"5e7044834c822940185fb338dc09e04e844f556c","tarball":"https://registry.npmjs.org/@activelylearn/passport-clever-oauth20/-/passport-clever-oauth20-0.1.1.tgz","integrity":"sha512-zFIn3s+Rw1SIRcQBHyRHHvmUDba4qP/5UFaspKjXJJ/4w/4U92l58NtMELlLI5s3q5YvjdjRNcXDaKU1I4EeZA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHyUkHV1ncs0QTAzvZ6bOGGR0RaHdzVWt7WzeDvQW+cYAiAwZnx7yJpHyg63vTbBYz3JCzPyzatas3BdhDRsbxf7GA=="}]},"maintainers":[{"name":"admehta01","email":"admehta01@gmail.com"},{"name":"setvik","email":"stein.setvik@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/passport-clever-oauth20-0.1.1.tgz_1505349403609_0.5896220053546131"},"directories":{}},"0.1.2":{"name":"@activelylearn/passport-clever-oauth20","version":"0.1.2","description":"PassportJS + Clever Instant Login","engines":{"node":"5.9.1"},"main":"index.js","scripts":{"start":"node index.js"},"dependencies":{"ejs":"2.4.1","express":"4.16.3","passport":"0.3.2","passport-oauth":"1.0.0","cookie-parser":"1.4.3","body-parser":"1.18.3","express-session":"1.15.6","request":"2.88.0","passport-oauth2":"1.x.x","oauth":"0.9.x","passport-strategy":"1.x.x","uid2":"0.0.x","utils-merge":"1.x.x"},"gitHead":"ad71afc9e2ca31efd2d880c356aa3617a49ed638","_id":"@activelylearn/passport-clever-oauth20@0.1.2","_npmVersion":"6.4.1","_nodeVersion":"8.12.0","_npmUser":{"name":"setvik","email":"stein.setvik@gmail.com"},"dist":{"integrity":"sha512-qJQPGTd0ak9vuv6lxLV6Ed+5OvoNzmdPsYsbaTjt1HPjN+ItnMYHFCtXzXPbLBgzQL3QXXkXDPeICC4Rx5+MUg==","shasum":"4059fe351ea24ecdb7e8d26dff039a7bbde8f5ff","tarball":"https://registry.npmjs.org/@activelylearn/passport-clever-oauth20/-/passport-clever-oauth20-0.1.2.tgz","fileCount":13,"unpackedSize":45807,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbvYXDCRA9TVsSAnZWagAAhn0P/0JfdK7YRPkr6simf0ry\nwkdJaZCRk7WTiiL6POwGDcWWThMOrJpt1nluwwC2CzWI/H3QeLNtKIkzy/qs\nd/PcYoOxXLNFzVPJQw++jZKOIgH1HpgEC1R8X+PAxzHkxE/aeLbYudH2klNi\n9ZJNBnwZY1iwvm8U6smZFbObfLkQNcd/EJbTygbZGrEMWamfJr7s7V+a7lcx\nWSHP3Zyq2krsjga1riwp1FAN7764UxfRBDZQMnUiZUTrVcsuGwdfBHp3RwnO\nvDCNuc/sXmvy+XAnoaNOpI8NdHfZXkoAGDTw+nOHoBAxeLF7x2vlL9E0SbSx\n2EChl5lG5Owwd68/Bfjzpdm/GYTUaCkTFJNmPuqfJ/B2t+3uWYHvrsO2v8tg\nNLzBiy3RrygO8XMnivfmxUy3mB40cU3HCrwxdDTwf1mqS6KC+xFjx1FlDGTg\nXE2HYB9ZE+lxH7zIO9W8hBnYHQLqDy60cWdp7Lcvmrk5o5gmmHFtVcrUFYbR\nfQgSI0fs7aSH3q/Jwk2tv2ATATpMuEJXTTtP4fgMGgKP3KzVua1x7WX0Px+U\nd44+cCU1eMp+SRAtYvMdlqLgueLWREpFdQZ1HohJyepco2P2QcuDK3bHXWnH\nFaEq5FOEQzauMu6M0U7dPupP56nEFXXfKbna/u66nBeBs27X6nceh6un5lox\nJ96e\r\n=acgP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBNR0N3ekMhx+4jG+Ll/LVDdWLJDRvm23G0Sg67cT25hAiEA3TOhguGXEmlyZSCh6GIIH5xE9uGkgAVCwGMMlCA64m8="}]},"maintainers":[{"name":"admehta01","email":"admehta01@gmail.com"},{"name":"setvik","email":"stein.setvik@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/passport-clever-oauth20_0.1.2_1539147202925_0.5627903789837654"},"_hasShrinkwrap":false}},"readme":"# Supporting Clever Instant Login\n\nClever integrates with many application types through [Instant Login](https://dev.clever.com/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.\n\n## Dependencies\n\n### App Creation\n\nAn 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. \n\nHere are a few resources that should not be missed:\n\n  1. [Getting Started](https://dev.clever.com/clever/)\n  1. [Obtaining Bearer Tokens](https://dev.clever.com/instant-login/bearer-tokens)\n  1. [Testing Your Integration](https://dev.clever.com/instant-login/testing)\n\n\n### App Configuration\n\nWe'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.\n\n```javascript\nvar appConfig = {\n  'port' : 3000,\n  'sessionSecret' : '{SessionSecret}',\n  'requestUrl' : 'https://api.clever.com',\n  'oauth' : {\n    // credentials\n    'clientId' : '{ClientId}',\n    'clientSecret' : '{ClientSecret}',\n    // instant clever login urls\n    'instantLoginUrl' : 'https://clever.com/oauth/instant-login?district_id={DistrictId}',\n    'instantCallbackUrl' : 'https://{DomainName}/auth/clever-instant/callback',\n    // token url\n    'tokenUrl' : 'https://clever.com/oauth/tokens?grant_type=authorization_code' \n  }\n};\nmodule.exports = appConfig;\n```\nNote: Placeholder values are surrounded in braces.\n\n### Node.js Modules\n\nWe'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.\n\n```javascript\nvar express = require('express');\nvar passport = require('passport');\n//var OAuth2Strategy = require('passport-oauth').OAuth2Strategy;\nvar OAuth2Strategy = require('./clever-passport-oauth/index').OAuth2Strategy;\nvar request = require('request');\nvar appConfig = require('./appConfig');\n```\nNote: Passport's OAuth2 strategy was customized to support Clever's token URL.\n\n#### App Setup\n\nThere 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.\n\n```javascript\nvar app = express();\napp.set('port', (process.env.PORT || appConfig.port));\n\napp.use(express.static(__dirname + '/public'));\napp.use(require('cookie-parser')());\napp.use(require('body-parser').urlencoded({ extended: true }));\napp.use(require('express-session')({ secret: appConfig.sessionSecret, resave: true, saveUninitialized: true }));\napp.use(passport.initialize());\napp.use(passport.session());\n```\n\n## Passport for OAuth\n\n### Passport Configuration\n\nThis PassportJS framework will handle the OAuth details for us. We must give it some configuration and direction on parsing and persisting the returned data.\n\n```javascript\n// setup instant login through passport\npassport.use('clever-instant', new OAuth2Strategy({\n    authorizationURL: appConfig.oauth.instantLoginUrl,\n    tokenURL: appConfig.oauth.tokenUrl,\n    clientID: appConfig.oauth.clientId,\n    clientSecret: appConfig.oauth.clientSecret,\n    callbackURL: appConfig.oauth.instantCallbackUrl\n  },\n  function(accessToken, refreshToken, profile, done) {\n    var fakeUser = { 'accessToken' : accessToken };\n    var options =  { 'method' : 'GET',\n                     'uri' : '/me'\n                   };\n    var callback = function(user, data) {\n      var fullUser = null;\n      if(data  && data.data) {\n        fullUser = data.data;\n        fullUser.accessToken = user.accessToken;\n      }\n      done(null, fullUser);\n    };\n    handleRequest(fakeUser,options,callback);\n  }\n));\n```\n\n### Passport Persistence\n\nA 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.\n\n```javascript\nvar users = [];\n\n// retrieve user for request\nfunction findUser(request) { \n  return users[request.session.passport.user]; \n};\n\n// store user in persistent storage for passport\npassport.serializeUser(function(user, done) {\n  users[user.id] = user;\n  done(null, user.id);\n});\n\n// retrieve user from persistent store for passport \npassport.deserializeUser(function(id, done) {\n  var user = users[id];\n  done(null, user);\n});\n\n// require token for session enabled request\nfunction requireToken() {\n  return function(req, res, next) {\n     if (req.session.passport &&\n         req.session.passport.user &&\n         findUser(req)) {\n       next();\n     }\n     else {\n       res.redirect('/login');\n     }\n   }\n}\n```\n\n## Application Logic\n\n### Common Utilities\n\nWe'll need to make API calls, and this function will make that a little easier.\n\n```javascript\n// simplified request handling\nfunction handleRequest(user, options, callback) {\n  var headers =  { 'Authorization': 'Bearer ' + user.accessToken, \n                   'Content-Type': 'application/json',\n                   'User-Agent': 'My App'\n                 };\n  request({ \n        'uri' : appConfig.requestUrl + options.uri,\n        'method' : 'GET',\n        'headers' : headers \n  }, \n  function(error, response, body) {\n    if(error) {\n      callback(user,null);\n      return;\n    }\n    if (response.statusCode === 200) {\n      callback(user,JSON.parse(body));\n    } else {\n      callback(user,response.statusCode);\n    }\n  });\n};\n```\n\n### App URLs\n\nWe need to expose URLs that route our applications' traffic to Passport's functionality.\n\n```javascript\n// entry point for clever instant auth\napp.get('/auth/clever-instant', passport.authenticate('clever-instant'));\n\n// callback for clever auth\napp.get('/auth/clever-instant/callback', \n  passport.authenticate('clever-instant', { successRedirect: '/',\n                                            failureRedirect: '/login'\n                                          }));\n\n// login setup\napp.get('/login', function(req, res){\n  res.send('<a href=\"/auth/clever/instant\"><img src=\"images/sign-in-with-clever-full.png\" /></a>');\n});\n\n// list the user's profile if authenticated\napp.get('/',requireToken(), function(request, response) {\n    var options =  { 'method' : 'GET', \n                     'uri' : '/me'\n                   };\n    var callback = function(user, data) {\n      response.send('<pre>' + JSON.stringify(data,null,'  ') + '</pre>');\n    };\n    handleRequest(findUser(request),options,callback);  \n});\n```\n\n### App Launch\n\nFinally, we just start the server.\n\n```javascript\napp.listen(app.get('port'), function() {\n  console.log('Node app is running on port', app.get('port'));\n});\n```\n\n## Application Experience\n\nExpect the following when testing the application: \n  \n  1. The \"Login with Clever\" button will greet you.\n  1. This button will launch Clever's demo login screen.\n  1. Successful login will diplay the user's profile in raw JSON.\n\nEnjoy!\n","maintainers":[{"name":"admehta01","email":"admehta01@gmail.com"},{"name":"setvik","email":"stein.setvik@gmail.com"}],"time":{"modified":"2022-06-12T14:16:20.392Z","created":"2017-09-13T23:03:56.277Z","0.1.0":"2017-09-13T23:03:56.277Z","0.1.1":"2017-09-14T00:36:43.717Z","0.1.2":"2018-10-10T04:53:23.067Z"},"readmeFilename":"README.md"}