63 lines
1.8 KiB
JavaScript
63 lines
1.8 KiB
JavaScript
|
import crypto from 'crypto'
|
||
|
import { HttpError } from 'flaska'
|
||
|
import { decode, encode } from '../util.mjs'
|
||
|
import config from '../config.mjs'
|
||
|
|
||
|
const levels = {
|
||
|
Normal: 1,
|
||
|
Manager: 10,
|
||
|
Admin: 100,
|
||
|
}
|
||
|
|
||
|
const issuer = config.get('mssql:connectionUser')
|
||
|
const secret = config.get('jwtsecret')
|
||
|
|
||
|
export function authenticate(minLevel = levels.Manager) {
|
||
|
return function(ctx) {
|
||
|
if (!ctx.req.headers.authorization) {
|
||
|
throw new HttpError(401, 'Authentication token missing')
|
||
|
}
|
||
|
if (!ctx.req.headers.authorization.startsWith('Bearer ')) {
|
||
|
throw new HttpError(401, 'Authentication token invalid')
|
||
|
}
|
||
|
|
||
|
let parts = ctx.req.headers.authorization.slice(7).split('.')
|
||
|
|
||
|
if (parts.length !== 4) {
|
||
|
throw new HttpError(401, 'Authentication token invalid')
|
||
|
}
|
||
|
|
||
|
const hmac = crypto.createHmac('sha256', secret)
|
||
|
const token = [parts[0], parts[1], parts[2]].join('.')
|
||
|
hmac.update(token)
|
||
|
let apiSignature = encode(hmac.digest())
|
||
|
|
||
|
if (apiSignature !== parts[3]) {
|
||
|
throw new HttpError(401, 'Authentication token invalid signature')
|
||
|
}
|
||
|
|
||
|
let header
|
||
|
let body
|
||
|
try {
|
||
|
header = JSON.parse(decode(parts[0]).toString('utf8'))
|
||
|
body = JSON.parse(decode(parts[1]).toString('utf8'))
|
||
|
} catch (err) {
|
||
|
throw new HttpError(401, 'Authentication token invalid json')
|
||
|
}
|
||
|
|
||
|
if (header.alg !== 'HS256') {
|
||
|
throw new HttpError(401, 'Authentication token invalid alg')
|
||
|
}
|
||
|
|
||
|
let unixNow = Math.floor(Date.now() / 1000)
|
||
|
|
||
|
// Validate token, add a little skew support for issued_at
|
||
|
if (body.iss !== issuer || !body.iat || !body.exp
|
||
|
|| body.iat > unixNow + 300 || body.exp <= unixNow) {
|
||
|
throw new HttpError(403, 'Authentication token expired or invalid')
|
||
|
}
|
||
|
ctx.state.auth_user = body
|
||
|
ctx.state.auth_token = token
|
||
|
}
|
||
|
}
|