|
import * as base64url from 'base64-url' |
|
import crypto from 'crypto' |
|
import config from 'config' |
|
|
|
import { getLogger } from '../logger.mjs' |
|
import { getOrThrowClientError } from '../util.mjs' |
|
import ResponseError from '../ResponseError.mjs' |
|
|
|
const logger = getLogger('parseSignedRequest') |
|
|
|
export default function parseSignedRequest (req, res, next) { |
|
const signedRequest = getSignedRequest(req) |
|
logger.debug('Validating signature', { |
|
signedRequest |
|
}) |
|
const [signature, payload] = getSignatureAndPayloadFromSignedRequest(signedRequest) |
|
validateSignature(signature, payload) |
|
replaceBodyWithDecodedPayload(req, payload) |
|
next() |
|
} |
|
|
|
function getSignedRequest (req) { |
|
return getOrThrowClientError(req, 'body', 'signed_request') |
|
} |
|
|
|
function getSignatureAndPayloadFromSignedRequest (signedRequest) { |
|
const [encodedSignature, payload] = signedRequest.split('.', 2) |
|
if (encodedSignature == null || payload == null) { |
|
throw new ResponseError(400, 'Signed request has invalid format') |
|
} |
|
const signature = decodeSignature(encodedSignature) |
|
return [signature, payload] |
|
} |
|
|
|
function replaceBodyWithDecodedPayload (req, payload) { |
|
const body = decodePayload(payload) |
|
req.body = body |
|
} |
|
|
|
function validateSignature (actualSignature, payload) { |
|
const expectedSignature = getExpectedSignature(payload) |
|
// For some reason, the actual signature always has a '=' appended |
|
const actualSignatureWithEqualsSign = actualSignature + '=' |
|
if (actualSignatureWithEqualsSign !== expectedSignature) { |
|
throw new ResponseError(401, 'Invalid signature') |
|
} |
|
} |
|
|
|
function decodeSignature (encodedSignature) { |
|
return urlDecode(encodedSignature) |
|
} |
|
|
|
function decodePayload (payload) { |
|
const bodyJson = base64url.decode(urlDecode(payload)) |
|
try { |
|
return JSON.parse(bodyJson) |
|
} catch (error) { |
|
// If the JSON is invalid, this is the client's fault |
|
error.code = 400 |
|
throw error |
|
} |
|
} |
|
|
|
function urlDecode (string) { |
|
return string.replace(/-/g, '+').replace(/_/g, '/') |
|
} |
|
|
|
function getExpectedSignature (payload) { |
|
const secret = config.get('services.facebook.appSecret') |
|
const hmac = crypto.createHmac('sha256', secret) |
|
hmac.update(payload) |
|
return hmac.digest('base64') |
|
} |
can you share also the
'../logger.mjs'
,'../util.mjs'
and'../ResponseError.mjs'
files?