LearnersGuild/idm-jwt-auth

View on GitHub
src/middlewares.js

Summary

Maintainability
B
6 hrs
Test Coverage
import jwt from 'jsonwebtoken'

import {userFromJWT, jwtClaimsForUser, cookieOptsJWT, idmGraphQLFetch} from './utils'

function getToken(req) {
  const authHeaderRegex = /^Bearer\s([A-Za-z0-9+\/_\-\.]+)$/
  const authHeader = req.get('Authorization')
  if (authHeader) {
    return authHeader.match(authHeaderRegex)[1]
  } else if (req.cookies && req.cookies.lgJWT) {
    return req.cookies.lgJWT
  }
  return null
}

export function addUserToRequestFromJWT(req, res, next) {
  if (!req.user || !req.lgJWT) {
    try {
      const lgJWT = getToken(req)
      if (lgJWT) {
        req.user = userFromJWT(lgJWT)
      }
    } catch (err) {
      if (next) {
        return next(err)
      }
    }
  }
  if (next) {
    next()
  }
}

export async function refreshUserFromIDMService(req, res, next) {
  try {
    // if this middleware is being invoked from within this module, skip it
    // (see `idmGraphQLFetch` in `utils.js`)
    const skip = req.get('LearnersGuild-Skip-Update-User-Middleware')
    if (req.user && !skip) {
      const query = {
        query: `
  query ($id: ID!) {
    getUserById(id: $id) {
      id
      active
      email
      handle
      name
      emails
      phone
      dateOfBirth
      timezone
      roles
      profileUrl
      avatarUrl
      authProviders {
        githubOAuth2 {
          accessToken
        }
      }
    }
  }
        `,
        variables: {
          id: req.user.id,
        },
      }

      const lgJWT = getToken(req)
      const result = await idmGraphQLFetch(query, lgJWT)
      const user = result.data.getUserById
      if (user.dateOfBirth) {
        const dateOfBirth = new Date(user.dateOfBirth)
        if (!isNaN(dateOfBirth.getTime())) {
          user.dateOfBirth = dateOfBirth
        }
      }
      req.user = user.active ? user : null
    }
  } catch (err) {
    if (next) {
      return next(err)
    }
  }
  if (next) {
    next()
  }
}

export function extendJWTExpiration(req, res, next) {
  if (process.env.JWT_PRIVATE_KEY && req.user) {
    if (req.user.active) {
      try {
        const jwtClaims = jwtClaimsForUser(req.user)
        const expires = new Date(jwtClaims.exp * 1000)
        const token = jwt.sign(jwtClaims, process.env.JWT_PRIVATE_KEY, {algorithm: 'RS512'})
        req.lgJWT = token
        res.set('LearnersGuild-JWT', token)
        res.cookie('lgJWT', token, Object.assign(cookieOptsJWT(req), {expires}))
      } catch (err) {
        revokeJWT(req, res)
        if (next) {
          return next(err)
        }
      }
    } else {
      revokeJWT(req, res)
    }
  }
  if (next) {
    next()
  }
}

function revokeJWT(req, res) {
  res.clearCookie('lgJWT', cookieOptsJWT(req))
  req.lgJWT = null
}