All files / lib/util googleAuth.ts

1.89% Statements 1/53
0% Branches 0/9
0% Functions 0/8
1.89% Lines 1/53

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102        16x                                                                                                                                                                                                  
import Debug from 'debug'
import auth from './auth'
import { google as googleApis } from 'googleapis'
 
const debug = Debug('crowi:lib:googleAuth')
 
export default config => {
  const lib: any = {}
 
  lib.PROVIDER = 'google'
 
  function createOauth2Client() {
    const clientId = config.crowi['google:clientId']
    const clientSecret = config.crowi['google:clientSecret']
    const callbackUrl = config.crowi['app:url'] + '/google/callback'
    return new googleApis.auth.OAuth2(clientId, clientSecret, callbackUrl)
  }
 
  lib.createAuthUrl = function(req, callback) {
    const oauth2Client = createOauth2Client()
    googleApis.options({ auth: oauth2Client })
 
    const redirectUrl = oauth2Client.generateAuthUrl({
      access_type: 'offline',
      scope: ['profile', 'email'],
      state: req.query.continue,
      prompt: 'consent',
    })
 
    callback(null, redirectUrl)
  }
 
  lib.refreshAccessToken = async tokens => {
    const oauth2Client = createOauth2Client()
    googleApis.options({ auth: oauth2Client })
    oauth2Client.setCredentials({ access_token: tokens.accessToken, refresh_token: tokens.refreshToken })
    const {
      res: {
        data: { access_token: accessToken, refresh_token: refreshToken, expiry_date: expiryDate },
      },
    } = (await oauth2Client.refreshAccessToken()) as any
    return { accessToken, refreshToken, expiryDate }
  }
 
  lib.reauth = async (id, { accessToken, refreshToken }) => {
    try {
      const tokens = await lib.refreshAccessToken({ accessToken, refreshToken })
      const oauth2Client = createOauth2Client()
      googleApis.options({ auth: oauth2Client })
      oauth2Client.setCredentials({ access_token: tokens.accessToken, refresh_token: tokens.refreshToken })
      const {
        data: { user_id: userId },
      } = await googleApis.oauth2('v2').tokeninfo({ access_token: tokens.accessToken })
      const success = id === userId
 
      return { success, tokens }
    } catch (err) {
      debug('Error on reauthenticating', err)
 
      return { success: false }
    }
  }
 
  lib.handleCallback = function(req, callback) {
    const oauth2Client = createOauth2Client()
    googleApis.options({ auth: oauth2Client })
    const { google = {} } = req.session
    const { authCode: code } = google
 
    if (!code) {
      return callback(new Error('No code exists.'), null)
    }
 
    debug('Request googleToken by auth code', code)
    oauth2Client.getToken(code, function(err, tokens) {
      debug('Result of google.getToken()', err, tokens)
      if (err) {
        return callback(new Error('[googleAuth.handleCallback] Error to get token.'), null)
      }
 
      oauth2Client.setCredentials({
        access_token: (tokens as any).access_token,
      })
 
      const oauth2 = googleApis.oauth2('v2')
      oauth2.userinfo.get({}, function(err, response) {
        debug('Response of oauth2.userinfo.get', err, response && response.data)
        if (err) {
          return callback(new Error('[googleAuth.handleCallback] Error while proceccing userinfo.get.'), null)
        }
        const { access_token: accessToken, refresh_token: refreshToken, expiry_date: expiryDate } = tokens as any
        auth.saveTokenToSession(req, lib.PROVIDER, { accessToken, refreshToken, expiryDate })
        const { data } = response as any
        data.user_id = data.id // This is for B.C. (tokeninfo をつかっている前提のコードに対してのもの)
        return callback(null, data)
      })
    })
  }
 
  return lib
}