oglimmer/linky

View on GitHub
server/auth/oauth2.js

Summary

Maintainability
C
7 hrs
Test Coverage

import request from 'request-promise';
import assert from 'assert';
import winston from 'winston';
import randomstring from 'randomstring';

import properties from '../util/linkyproperties';
import authHelper from './authHelper';
import JwtUtil from '../util/JwtUtil';

const redirectTarget = properties.server.auth.redirectUri;

const init = async (req, res) => {
  const { type } = req.params;
  assert(type, 'Failed to get type from path');
  const { hint } = req.query;
  const responseType = 'response_type=code';
  const scope = `scope=${encodeURIComponent(properties.server.auth[type].scope)}`;
  const clientId = `client_id=${encodeURIComponent(properties.server.auth[type].clientId)}`;
  const redirectUri = `redirect_uri=${encodeURIComponent(`${redirectTarget}/${type}`)}`;
  const loginHint = hint ? `login_hint=${encodeURIComponent(hint)}` : '';
  const randomToken = randomstring.generate();
  const duration = type === 'reddit' ? 'duration=permanent' : '';
  const claim = await JwtUtil.sign({ randomToken }, '15m');
  const state = `state=${encodeURIComponent(claim)}`;
  const url = `${properties.server.auth[type].authUri}?${responseType}&${clientId}&${scope}&${redirectUri}&${state}&${loginHint}&${duration}`;
  winston.loggers.get('application').debug(`redirect to ${url}`);
  res.cookie('stateClaim', claim, { httpOnly: true, secure: properties.server.jwt.httpsOnly });
  res.redirect(url);
};

const getAuthToken = (code, type) => {
  const form = {
    code,
    client_id: properties.server.auth[type].clientId,
    client_secret: properties.server.auth[type].clientSecret,
    grant_type: 'authorization_code',
    redirect_uri: `${redirectTarget}/${type}`,
  };
  const headers = {
    Accept: 'application/json',
    'User-Agent': 'linky1.com',
  };
  if (type === 'reddit') {
    headers.authorization = `Basic ${new Buffer(`${properties.server.auth[type].clientId}:${properties.server.auth[type].clientSecret}`).toString('base64')}`;
  }
  return request.post({ url: properties.server.auth[type].tokenUri,
    json: true,
    form,
    headers,
  });
};

const back = async (req, res) => {
  if (req.query.error || !req.query.code) {
    winston.loggers.get('application').debug('error: %j', req.query);
    res.redirect('/');
  } else {
    const { type } = req.params;
    assert(type, 'Failed to get type from path');
    const { state, code } = req.query;
    assert(state, 'Failed to get state from path');
    try {
      await authHelper.verifyState(req, res, state);
      const authTokenResponse = await getAuthToken(code, type);
      const user = await authHelper.getRemoteUserJson(type, authTokenResponse);
      await authHelper.forward(req, res, type, user, authTokenResponse);
    } catch (err) {
      winston.loggers.get('application').error('Failed to oauth2Back');
      winston.loggers.get('application').error(err);
      res.status(500).end();
    }
  }
};

export default {
  back,
  init,
};