haroun
3
const {resolve} = require('path');
const grpc = require('grpc');
const introspectInternal = require('./introspect-internal');
const port = process.env.PORT || 5566;
const listenAddress = `0.0.0.0:${port}`;
const tyk = grpc.load({
file: 'coprocess_object.proto',
root: resolve(__dirname, 'tyk-protobuf/proto')
}).coprocess;
// Keep lowercase headers
// getHeaders :: Object -> Object
const getHeaders = request => Object.entries(request.headers)
.reduce((acc, [header, value]) => {
acc[header.toLowerCase()] = value;
return acc;
}, {});
// Handler
// handleError :: (Error, Object) -> undefined
const handleError = (err, req) => {
console.error('[GRPC]', err.toString(), JSON.stringify(err.stack || ''), '- headers:', JSON.stringify(req.request.headers));
const response = JSON.parse(JSON.stringify(req));
response.request.return_overrides.response_code = 401;
response.request.return_overrides.headers = {
'Content-Type': 'application/json'
};
response.request.return_overrides.response_error = err.message;
return response;
};
// Middleware
// authMiddleware :: (Object, Function) -> undefined
const authMiddleware = async (obj, callback) => {
// Deep clone
// const response = JSON.parse(JSON.stringify(obj)); // result in key not authorised
const response = obj;
// Keep lowercase headers
const headers = getHeaders(response.request);
// We take the value from the "Authorization" header:
const authorizationToken = (headers.authorization || ''))
.substring('Bearer '.length);
if (authorizationToken === '') {
throw new Error('"authorization" header is missing or empty');
}
const req = {
headers,
body: {
token: authorizationToken
},
params: {},
url: response.request.url,
tokenResponse: {}
};
console.log('[GRPC] req:', JSON.stringify(req));
const token = await introspectInternal.introspect(req);
req.tokenResponse.Authorization = token.authorization;
req.tokenResponse['X-Forwarded-Authorization'] = req.token;
// The token should be attached to the object metadata, this is used internally for key management:
response.metadata = Object.assign(
{token: req.tokenResponse['X-Forwarded-Authorization']},
req.tokenResponse
);
// If the Authrorization and X-Forwarded-Authorization are empty in tokenResponse, we return the call:
if (!req.tokenResponse.Authorization && !req.tokenResponse['X-Forwarded-Authorization']) {
callback(null, obj);
return;
}
if (req.tokenResponse['X-Forwarded-Authorization']) {
response.request.set_headers['X-Forwarded-Authorization'] = 'Bearer ' + req.tokenResponse['X-Forwarded-Authorization'];
}
response.request.set_headers.Authorization = 'Bearer ' + req.tokenResponse.Authorization;
// At this point the token is valid and a session state object is initialized and attached to the coprocess.Object:
const session = new tyk.SessionState();
session.alias = req.tokenResponse['X-Forwarded-Authorization'];
response.session = session;
callback(null, response);
};
// The dispatch function is called for every hook:
const dispatch = async (call, callback) => {
try {
const obj = call.request;
// We dispatch the request based on the hook name, we pass obj.request which is the coprocess.Object:
switch (obj.hook_name) {
case 'MyAuthMiddleware':
await authMiddleware(obj, callback);
break;
default:
callback(null, obj);
break;
}
} catch (err) {
const response = handleError(err, call.request);
callback(null, response);
}
};
const dispatchEvent = () => {};
const reload = () => {};
const main = () => {
const server = new grpc.Server();
server.addService(tyk.Dispatcher.service, {
dispatch,
dispatchEvent,
reload
});
server.bind(listenAddress, grpc.ServerCredentials.createInsecure());
console.log('[GRPC]', `GRPC server running on ${listenAddress}`);
server.start();
};
module.exports = main;