import PassportCustomStrategy from 'passport-custom' import request from 'request' import { getAllowedUsers, logUnauthorized, parseToken, getBotPermissions, isAuthEnabled } from './auth' import { PermissionsRole } from '../schema' import { Request } from 'express' export interface UserInfo { cachedAt: string email: string sub: string name: string role: string } const cachedUserInfos = new Map() const getUserInfo = (token: string) => { const cachedUserInfo = cachedUserInfos.get(token) if (cachedUserInfo && !isUserInfoExpired(cachedUserInfo)) { return Promise.resolve(cachedUserInfo) } return fetchUserInfo(token) } const isUserInfoExpired = (userInfo: UserInfo) => { const CACHE_EXPIRATION_MS = parseInt(process.env.USER_INFO_CACHE_EXPIRATION_MS) const cachedAt = Date.parse(userInfo.cachedAt) const now = new Date().valueOf() return now > cachedAt + CACHE_EXPIRATION_MS } const fetchUserInfo = (token: string) => { return new Promise((resolve, reject) => { request.get( process.env.AUTH0_USER_INFO_URL, { auth: { bearer: token } }, (err, res) => { if (err || !res) { reject(err || 'Empty response') } try { const userInfo = JSON.parse(res.body) if (!userInfo) { return reject('Invalid user info') } else if (!userInfo.email_verified) { return reject('Unverified email') } cacheUserInfo(token, userInfo) resolve(userInfo) } catch (e) { reject('Invalid user info') } } ) }) } const checkAllowedUser = async (userInfo: UserInfo) => { const allowedUsers = await getAllowedUsers() if (allowedUsers && allowedUsers.includes(userInfo.email)) { return true } else { logUnauthorized(userInfo) return false } } const cacheUserInfo = (token: string, userInfo: UserInfo) => { userInfo.cachedAt = new Date().toISOString() cachedUserInfos.set(token, userInfo) } export function createBotTokenStrategy() { return new PassportCustomStrategy(async function( req: Request, callback: (error: Error, result: { role: PermissionsRole }) => void ) { try { let token = parseToken(req.headers.authorization) if (!token) { return this.fail() } const permissions = await getBotPermissions(req.params.id) if (token && permissions.tokens) { const permissionToken = permissions.tokens.find(({ token: _token }) => _token === token) if (permissionToken) { const { role } = permissionToken callback(null, { role }) return } } return this.fail() } catch (e) { return this.fail() } }) } export function createAllowedUsersStrategy() { return new PassportCustomStrategy(function( req: Request, callback: (error: Error, result: { isAllowedUser: boolean }) => void ) { if (!isAuthEnabled()) { callback(null, { isAllowedUser: true }) return } const token = parseToken(req.headers.authorization) if (!token) { return this.fail() } getUserInfo(token) .then(async function(userInfo) { if (userInfo.email) { const isAllowedUser = await checkAllowedUser(userInfo) if (!isAllowedUser) { return this.fail() } callback(null, { isAllowedUser }) } else { return this.fail() } }) .catch(err => { return this.fail() }) }) }