UNPKG

advms-koa-auth

Version:

140 lines (121 loc) 4.16 kB
'use strict' const request = require('request-promise-native') const jwt = require('jsonwebtoken') const fs = require('fs') const keyPath = process.env.JWT_KEY || 'certs/public.pem' const log = require('debug')('advms-auth') module.exports = function (options, router) { const cert = (() => { return fs.existsSync(keyPath) ? fs.readFileSync(keyPath) : request({ method: 'GET', uri: options.oauthUrl + '/public.pem' }) })() const auth = async function (ctx, next) { log('start') const token = ctx.request.get(options.header || 'authorization') || (options.cookie && 'Bearer ' + ctx.cookies.get(options.cookie)) log('fetching public key for auth') const key = await cert log('public key fetched') try { ctx.user = await new Promise((resolve, reject) => { if (!token || !token.startsWith('Bearer ')) return reject(new Error('authorization header validation failed')) log('token verify start') jwt.verify(token.substring(7), key, { algorithms: ['RS256'] }, (e, d) => { log('verify done') if (e) return reject(e) resolve(d) }) }) if (!ctx.user || (options.claim && !ctx.user[options.claim])) { throw new Error('no user or claim is invalid') } } catch (e) { log('error:', e) ctx.throw(401) } await next() } if (!router) { log('no router passed. We done.') return auth } router.get('callback', '/auth/callback', async ctx => { log('callback request recieved') let token = {} try { log('requesting auth token') token = JSON.parse(await request({ method: 'POST', uri: options.oauthUrl + '/token', form: { grant_type: 'authorization_code', client_id: options.client_id, client_secret: options.client_secret, code: ctx.query.code, redirect_uri: ctx.origin + router.url('callback') } })) log('auth token request complete') } catch (e) { log('token request error:', e.message) } if (token.error) { log('token request fs server error', token.error) return } log('storing token in cookie') let date = new Date() date.setSeconds(date.getSeconds() + token.expires_in - 60) ctx.cookies.set(options.cookie, token.access_token, { path: options.cookie_path, expires: date, secure: ctx.request.secure }) log('token saved') return ctx.redirect(Buffer.from(ctx.query.state, 'base64').toString('utf8')) }) router.get('/auth/logout', async ctx => { let date = new Date() date.setSeconds(date.getSeconds() - 60) ctx.cookies.set(options.cookie, null, { path: options.cookie_path, expires: date, secure: ctx.request.secure }) ctx.redirect(options.oauthUrl + '/authorize/logout' + '?response_type=code' + '&client_id=' + options.client_id + '&state=' + Buffer.from(options.redirect).toString('base64') + '&redirect_uri=' + ctx.origin + router.url('callback')) }) router.use(async (ctx, next) => { log('authorization middleware') let action = '/authorize/login' if (ctx.cookies.get(options.cookie)) { try { ctx.request.headers.authorization = 'Bearer ' + ctx.cookies.get(options.cookie) await auth(ctx, next) log('authorization middleware is done') return } catch (e) { if (e.message !== 'Unauthorized') throw e action = '/authorize/logout' } } log('redirecting to fs server', action) ctx.redirect(options.oauthUrl + action + '?response_type=code' + '&client_id=' + options.client_id + '&state=' + Buffer.from(ctx.request.url).toString('base64') + '&redirect_uri=' + ctx.origin + router.url('callback')) }) router.get('/auth/login', async ctx => { log('redirecting to fs server') ctx.redirect(options.redirect) }) return auth }