zalando/zappr

View on GitHub
server/passport/passport.js

Summary

Maintainability
A
1 hr
Test Coverage
import passport from 'koa-passport'
import { Strategy as GithubStrategy } from 'passport-github'
import GithubAPIStrategy from './passport-github-api'
import nconf from '../nconf'
import { User } from './../model'
import { joinURL } from '../../common/util'
import { logger } from '../../common/debug'

const log = logger('passport')

/**
 * Merge Passport and Github user profiles.
 *
 * http://passportjs.org/docs/profile
 * https://developer.github.com/v3/users/#get-a-single-user
 */
function normalizeProfile(profile) {
  const {_raw, _json, ...rest} = profile
  const normalizedProfile = {...rest, ..._json}
  return normalizedProfile
}

/**
 * Configure and return passport instance.
 *
 * @param {Strategy} - Passport strategy
 * @returns {Authenticator} - Passport instance
 */
export function init(Strategy = GithubStrategy) {
  const GITHUB_CLIENT_ID = nconf.get('GITHUB_CLIENT_ID')
  const GITHUB_CLIENT_SECRET = nconf.get('GITHUB_CLIENT_SECRET')
  const GITHUB_UI_URL = nconf.get('GITHUB_UI_URL')
  const GITHUB_API_URL = nconf.get('GITHUB_API_URL')
  const HOST_ADDR = nconf.get('HOST_ADDR')

  /**
   * Serialize user data into the session.
   */
  passport.serializeUser((data, done) => {
    log(`serializeUser id: ${data.id}`)
    done(null, data)
  })

  /**
   * Deserialize user profile out of the session.
   */
  passport.deserializeUser((data, done) => {
    log(`deserializeUser id: ${data.id}`)
    User.findById(data.id)
        .then(user => user
          ? user.toJSON()
          : null)
        .then(user => user
          ? done(null, {...user, ...data})
          : done(new Error(`no user for id ${data.id}`)))
  })

  passport.use(new GithubAPIStrategy({
      apiUrl: GITHUB_API_URL
    },
    (accessToken, profile, done) =>
      // make it look like it came from the database
      done(null, {id: profile.id, accessToken, json: normalizeProfile(profile)})
  ))

  passport.use(new Strategy({
      // See https://developer.github.com/v3/oauth
      clientID: GITHUB_CLIENT_ID,
      clientSecret: GITHUB_CLIENT_SECRET,
      callbackURL: joinURL(HOST_ADDR, 'auth/github/callback'),
      authorizationURL: joinURL(GITHUB_UI_URL, 'login/oauth/authorize'),
      tokenURL: joinURL(GITHUB_UI_URL, 'login/oauth/access_token'),
      userProfileURL: joinURL(GITHUB_API_URL, 'user')
    },
    (accessToken, refreshToken, profile, done) => {
      // Add the accessToken to the returned object so that it is stored in the session.
      // DO NOT persist the accessToken in the user profile.
      log(`verify profile id: ${profile.id} username: ${profile.username}`)

      // Clean up the user profile data
      const user = normalizeProfile(profile)
      // The session data:
      const data = {id: user.id, accessToken}

      User.upsert({id: user.id, json: user})
          .then(() => done(null, data))
          .catch(done)
    }
  ))

  return passport
}