diff --git a/config/config-sample-client-localhost.yaml b/config/config-sample-client-localhost.yaml index 8670a9f..49a29ce 100644 --- a/config/config-sample-client-localhost.yaml +++ b/config/config-sample-client-localhost.yaml @@ -21,13 +21,13 @@ responseCache: # 2. webhook # 3. pubSub client: - synchronous: + synchronous: mongoURL: "mongodb://127.0.0.1:27017/protocolserver-v2-response-cache" #webhook: # url: "https://beckn.free.beeceptor.com/clientURL" - - #messageQueue: + + #messageQueue: # amqpURL: "amqp://guest:guest@localhost:5672" # incomingQueue: "protocol-server-incoming" # outgoingQueue: "protocol-server-outgoing" @@ -49,25 +49,24 @@ app: actions: requests: search: - ttl : "PT10S" + ttl: "PT10S" init: - ttl : "PT10S" + ttl: "PT10S" select: - ttl : "PT10S" + ttl: "PT10S" confirm: - ttl : "PT10S" + ttl: "PT10S" status: ttl: "PT10S" get_cancellation_reasons: ttl: "PT10S" get_rating_categories: ttl: "PT10S" - - - responses: + + responses: on_search: ttl: "PT10S" - + on_init: ttl: "PT10S" on_select: @@ -80,7 +79,7 @@ app: ttl: "PT10S" rating_categories: ttl: "PT10S" - + # Mandatory. privateKey: "private key" publicKey: "public key" @@ -88,12 +87,12 @@ app: # Mandatory. subscriberId: "dev.bap.faiz.protocol-server.com.dsep:jobs.BAP" subscriberUri: "https://410e-45-248-79-20.ngrok.io/" - + # Mandatory. registryUrl: https://registry.becknprotocol.io/subscribers auth: false uniqueKey: "dev.bap.faiz.protocol-server.key" - + # Mandatory. city: "std:080" country: "IND" @@ -111,3 +110,6 @@ app: # In minutes syncInterval: 30 redis_db: 3 + + useHMACForWebhook: false + sharedKeyForWebhookHMAC: "" diff --git a/config/config-sample-network-localhost.yaml b/config/config-sample-network-localhost.yaml index 7028c6a..b476d9e 100644 --- a/config/config-sample-network-localhost.yaml +++ b/config/config-sample-network-localhost.yaml @@ -21,13 +21,13 @@ responseCache: # 2. webhook # 3. pubSub client: - synchronous: + synchronous: mongoURL: "mongodb://127.0.0.1:27017/protocolserver-v2-response-cache" #webhook: # url: "https://beckn.free.beeceptor.com/clientURL" - - #messageQueue: + + #messageQueue: # amqpURL: "amqp://guest:guest@localhost:5672" # incomingQueue: "protocol-server-incoming" # outgoingQueue: "protocol-server-outgoing" @@ -49,24 +49,24 @@ app: actions: requests: search: - ttl : "PT10S" + ttl: "PT10S" init: - ttl : "PT10S" + ttl: "PT10S" select: - ttl : "PT10S" + ttl: "PT10S" confirm: - ttl : "PT10S" + ttl: "PT10S" status: - ttl : "PT10S" + ttl: "PT10S" get_cancellation_reasons: ttl: "PT10S" get_rating_categories: ttl: "PT10S" - - responses: + + responses: on_search: ttl: "PT10S" - + on_init: ttl: "PT10S" on_select: @@ -79,7 +79,7 @@ app: ttl: "PT10S" rating_categories: ttl: "PT10S" - + # Mandatory. privateKey: "private key" publicKey: "public key" @@ -87,12 +87,12 @@ app: # Mandatory. subscriberId: "dev.bap.faiz.protocol-server.com.dsep:jobs.BAP" subscriberUri: "https://410e-45-248-79-20.ngrok.io/" - + # Mandatory. registryUrl: https://registry.becknprotocol.io/subscribers auth: true uniqueKey: "dev.bap.faiz.protocol-server.key" - + # Mandatory. city: "std:080" country: "IND" @@ -110,3 +110,6 @@ app: # In minutes syncInterval: 30 redis_db: 3 + + useHMACForWebhook: false + sharedKeyForWebhookHMAC: "" diff --git a/config/config-sample.yaml b/config/config-sample.yaml index 24fe328..7308a14 100644 --- a/config/config-sample.yaml +++ b/config/config-sample.yaml @@ -21,13 +21,13 @@ responseCache: # 2. webhook # 3. pubSub client: - synchronous: + synchronous: mongoURL: "mongodb://tvast:password@localhost:27017/protocol_server-v2?authSource=admin" webhook: url: "https://beckn.free.beeceptor.com/clientURL" - - messageQueue: + + messageQueue: amqpURL: "amqp://guest:guest@localhost:5672" incomingQueue: "protocol-server-incoming" outgoingQueue: "protocol-server-outgoing" @@ -49,17 +49,17 @@ app: actions: requests: search: - ttl : "PT10S" + ttl: "PT10S" init: - ttl : "PT10S" - - responses: + ttl: "PT10S" + + responses: on_search: ttl: "PT10S" - + on_init: ttl: "PT10S" - + # Mandatory. privateKey: "your private key" publicKey: "your public key" @@ -67,12 +67,12 @@ app: # Mandatory. subscriberId: "dev.bap.protocol-server.com" subscriberUri: "https://ayush.free.beeceptor.com/" - + # Mandatory. registryUrl: https://registry.becknprotocol.io/subscribers auth: true uniqueKey: "dev.bap.protocol-server.key" - + # Mandatory. city: "std:080" country: "IND" @@ -90,3 +90,6 @@ app: # In minutes syncInterval: 30 redis_db: 3 + + useHMACForWebhook: false + sharedKeyForWebhookHMAC: "" diff --git a/config/new-config-sample.yaml b/config/new-config-sample.yaml index bcd08d5..3e5126c 100644 --- a/config/new-config-sample.yaml +++ b/config/new-config-sample.yaml @@ -21,13 +21,13 @@ responseCache: # 2. webhook # 3. pubSub client: - synchronous: + synchronous: mongoURL: "mongodb://tvast:password@localhost:27017/protocol_server-v2?authSource=admin" webhook: url: "https://beckn.free.beeceptor.com/clientURL" - - messageQueue: + + messageQueue: amqpUrl: "amqp://guest:guest@localhost:5672" request-queue: "bap-protocol-server" response-queue: "bap-protocol-client" @@ -50,17 +50,17 @@ app: actions: requests: search: - ttl : "PT10S" + ttl: "PT10S" init: - ttl : "PT10S" - - responses: + ttl: "PT10S" + + responses: on_search: ttl: "PT10S" - + on_init: ttl: "PT10S" - + # Mandatory. privateKey: "your private key" publicKey: "your public key" @@ -68,12 +68,12 @@ app: # Mandatory. subscriberId: "dev.bap.protocol-server.com" subscriberUri: "https://ayush.free.beeceptor.com/" - + # Mandatory. registryUrl: https://registry.becknprotocol.io/subscribers auth: true uniqueKey: "dev.bap.protocol-server.key" - + # Mandatory. city: "std:080" country: "IND" @@ -91,3 +91,6 @@ app: # In minutes syncInterval: 30 redis_db: 3 + + useHMACForWebhook: false + sharedKeyForWebhookHMAC: "" diff --git a/config/samples/bap-client.yaml b/config/samples/bap-client.yaml index 076ec3d..0d0a814 100644 --- a/config/samples/bap-client.yaml +++ b/config/samples/bap-client.yaml @@ -21,13 +21,13 @@ responseCache: # 2. webhook # 3. pubSub client: - synchronous: + synchronous: mongoURL: "mongodb://MONGO_USERNAME:MONGO_PASSWORD@MONOG_URL:27017/MONGO_DB_NAME?authSource=admin" #webhook: # url: "https://beckn.free.beeceptor.com/clientURL" - - #messageQueue: + + #messageQueue: # amqpURL: "amqp://guest:guest@localhost:5672" # incomingQueue: "protocol-server-incoming" # outgoingQueue: "protocol-server-outgoing" @@ -49,33 +49,33 @@ app: actions: requests: search: - ttl : "PT15S" + ttl: "PT15S" init: - ttl : "PT10S" + ttl: "PT10S" select: - ttl : "PT10S" + ttl: "PT10S" confirm: - ttl : "PT10S" + ttl: "PT10S" status: - ttl : "PT10S" + ttl: "PT10S" track: - ttl : "PT10S" + ttl: "PT10S" cancel: - ttl : "PT10S" + ttl: "PT10S" update: - ttl : "PT10S" + ttl: "PT10S" rating: - ttl : "PT10S" + ttl: "PT10S" support: - ttl : "PT10S" + ttl: "PT10S" get_cancellation_reasons: - ttl : "PT10S" + ttl: "PT10S" get_rating_categories: - ttl : "PT10S" + ttl: "PT10S" cancellation: - ttl : "PT10S" + ttl: "PT10S" - responses: + responses: on_search: ttl: "PT15S" on_init: @@ -99,8 +99,8 @@ app: cancellation_reasons: ttl: "PT10S" rating_categories: - ttl: "PT10S" - + ttl: "PT10S" + # Mandatory. privateKey: "PRIVATE_KEY" publicKey: "PUBLIC_KEY" @@ -113,7 +113,7 @@ app: registryUrl: REGISTRY_URL auth: false uniqueKey: "BAP_SUBSCRIBER_ID_KEY" - + # Mandatory. city: "std:080" country: "IND" @@ -131,3 +131,6 @@ app: # In minutes syncInterval: 30 redis_db: 3 + + useHMACForWebhook: false + sharedKeyForWebhookHMAC: "" diff --git a/config/samples/bap-network.yaml b/config/samples/bap-network.yaml index a935a71..e929f49 100644 --- a/config/samples/bap-network.yaml +++ b/config/samples/bap-network.yaml @@ -21,13 +21,13 @@ responseCache: # 2. webhook # 3. pubSub client: - synchronous: + synchronous: mongoURL: "mongodb://MONGO_USERNAME:MONGO_PASSWORD@MONOG_URL:27017/MONGO_DB_NAME?authSource=admin" #webhook: # url: "https://beckn.free.beeceptor.com/clientURL" - - #messageQueue: + + #messageQueue: # amqpURL: "amqp://guest:guest@localhost:5672" # incomingQueue: "protocol-server-incoming" # outgoingQueue: "protocol-server-outgoing" @@ -49,33 +49,33 @@ app: actions: requests: search: - ttl : "PT15S" + ttl: "PT15S" init: - ttl : "PT10S" + ttl: "PT10S" select: - ttl : "PT10S" + ttl: "PT10S" confirm: - ttl : "PT10S" + ttl: "PT10S" status: - ttl : "PT10S" + ttl: "PT10S" track: - ttl : "PT10S" + ttl: "PT10S" cancel: - ttl : "PT10S" + ttl: "PT10S" update: - ttl : "PT10S" + ttl: "PT10S" rating: - ttl : "PT10S" + ttl: "PT10S" support: - ttl : "PT10S" + ttl: "PT10S" get_cancellation_reasons: - ttl : "PT10S" + ttl: "PT10S" get_rating_categories: - ttl : "PT10S" + ttl: "PT10S" cancellation: - ttl : "PT10S" + ttl: "PT10S" - responses: + responses: on_search: ttl: "PT15S" on_init: @@ -99,8 +99,8 @@ app: cancellation_reasons: ttl: "PT10S" rating_categories: - ttl: "PT10S" - + ttl: "PT10S" + # Mandatory. privateKey: "PRIVATE_KEY" publicKey: "PUBLIC_KEY" @@ -113,7 +113,7 @@ app: registryUrl: REGISTRY_URL auth: false uniqueKey: "BAP_SUBSCRIBER_ID_KEY" - + # Mandatory. city: "std:080" country: "IND" @@ -131,3 +131,6 @@ app: # In minutes syncInterval: 30 redis_db: 3 + + useHMACForWebhook: false + sharedKeyForWebhookHMAC: "" diff --git a/config/samples/bpp-client.yaml b/config/samples/bpp-client.yaml index 7a90948..676bec0 100644 --- a/config/samples/bpp-client.yaml +++ b/config/samples/bpp-client.yaml @@ -8,7 +8,7 @@ cache: port: 6379 ttl: "PT10M" # Optional. Default is 0. - db: 0 + db: 0 # Optional. responseCache: @@ -21,13 +21,13 @@ responseCache: # 2. webhook # 3. pubSub client: -# synchronous: -# mongoURL: "mongodb://tvast:password@0.0.0.0:27017/ps?authSource=admin" + # synchronous: + # mongoURL: "mongodb://tvast:password@0.0.0.0:27017/ps?authSource=admin" webhook: - url: "WEBHOOK_URL" - - #messageQueue: + url: "WEBHOOK_URL" + + #messageQueue: # amqpURL: "amqp://guest:guest@localhost:5672" # incomingQueue: "protocol-server-incoming" # outgoingQueue: "protocol-server-outgoing" @@ -49,31 +49,31 @@ app: actions: requests: search: - ttl : "PT15S" + ttl: "PT15S" init: - ttl : "PT10S" + ttl: "PT10S" select: - ttl : "PT10S" + ttl: "PT10S" confirm: - ttl : "PT10S" + ttl: "PT10S" status: - ttl : "PT10S" + ttl: "PT10S" track: - ttl : "PT10S" + ttl: "PT10S" cancel: - ttl : "PT10S" + ttl: "PT10S" update: - ttl : "PT10S" + ttl: "PT10S" rating: - ttl : "PT10S" + ttl: "PT10S" support: - ttl : "PT10S" + ttl: "PT10S" get_cancellation_reasons: ttl: "PT10S" get_rating_categories: - ttl: "PT10S" - - responses: + ttl: "PT10S" + + responses: on_search: ttl: "PT15S" on_init: @@ -97,8 +97,8 @@ app: cancellation_reasons: ttl: "PT10S" rating_categories: - ttl: "PT10S" - + ttl: "PT10S" + # Mandatory. privateKey: "PRIVATE_KEY" publicKey: "PUBLIC_KEY" @@ -111,7 +111,7 @@ app: registryUrl: REGISTRY_URL auth: true uniqueKey: "BPP_SUBSCRIBER_ID_KEY" - + # Mandatory. city: "std:080" country: "IND" @@ -129,3 +129,6 @@ app: # In minutes syncInterval: 30 redis_db: 3 + + useHMACForWebhook: false + sharedKeyForWebhookHMAC: "" diff --git a/config/samples/bpp-network.yaml b/config/samples/bpp-network.yaml index 857a315..773bc3e 100644 --- a/config/samples/bpp-network.yaml +++ b/config/samples/bpp-network.yaml @@ -8,7 +8,7 @@ cache: port: 6379 ttl: "PT10M" # Optional. Default is 0. - db: 0 + db: 0 # Optional. responseCache: @@ -21,13 +21,13 @@ responseCache: # 2. webhook # 3. pubSub client: -# synchronous: -# mongoURL: "mongodb://tvast:password@0.0.0.0:27017/ps?authSource=admin" + # synchronous: + # mongoURL: "mongodb://tvast:password@0.0.0.0:27017/ps?authSource=admin" webhook: - url: "WEBHOOK_URL" - - #messageQueue: + url: "WEBHOOK_URL" + + #messageQueue: # amqpURL: "amqp://guest:guest@localhost:5672" # incomingQueue: "protocol-server-incoming" # outgoingQueue: "protocol-server-outgoing" @@ -49,31 +49,31 @@ app: actions: requests: search: - ttl : "PT15S" + ttl: "PT15S" init: - ttl : "PT10S" + ttl: "PT10S" select: - ttl : "PT10S" + ttl: "PT10S" confirm: - ttl : "PT10S" + ttl: "PT10S" status: - ttl : "PT10S" + ttl: "PT10S" track: - ttl : "PT10S" + ttl: "PT10S" cancel: - ttl : "PT10S" + ttl: "PT10S" update: - ttl : "PT10S" + ttl: "PT10S" rating: - ttl : "PT10S" + ttl: "PT10S" support: - ttl : "PT10S" + ttl: "PT10S" get_cancellation_reasons: ttl: "PT10S" get_rating_categories: - ttl: "PT10S" - - responses: + ttl: "PT10S" + + responses: on_search: ttl: "PT15S" on_init: @@ -97,8 +97,8 @@ app: cancellation_reasons: ttl: "PT10S" rating_categories: - ttl: "PT10S" - + ttl: "PT10S" + # Mandatory. privateKey: "PRIVATE_KEY" publicKey: "PUBLIC_KEY" @@ -111,7 +111,7 @@ app: registryUrl: REGISTRY_URL auth: true uniqueKey: "BPP_SUBSCRIBER_ID_KEY" - + # Mandatory. city: "std:080" country: "IND" @@ -129,3 +129,6 @@ app: # In minutes syncInterval: 30 redis_db: 3 + + useHMACForWebhook: false + sharedKeyForWebhookHMAC: "" diff --git a/src/controllers/bpp.request.controller.ts b/src/controllers/bpp.request.controller.ts index cb094ab..1a3626f 100644 --- a/src/controllers/bpp.request.controller.ts +++ b/src/controllers/bpp.request.controller.ts @@ -5,10 +5,8 @@ import * as AmqbLib from "amqplib"; import { Exception, ExceptionType } from "../models/exception.model"; import { acknowledgeACK } from "../utils/acknowledgement.utils"; import { GatewayUtils } from "../utils/gateway.utils"; -import { ActionUtils } from "../utils/actions.utils"; import { RequestCache } from "../utils/cache/request.cache.utils"; import { parseRequestCache } from "../schemas/cache/request.cache.schema"; -import { getSubscriberDetails } from "../utils/lookup.utils"; import { Locals } from "../interfaces/locals.interface"; import moment from "moment"; import { getConfig } from "../utils/config.utils"; @@ -19,6 +17,7 @@ import { createTelemetryEvent, processTelemetry } from "../utils/telemetry.utils"; +import { createBppWebhookAuthHeaderConfig } from "../utils/auth.utils"; export const bppNetworkRequestHandler = async ( req: Request, @@ -92,7 +91,11 @@ export const bppNetworkRequestSettler = async ( break; } case ClientConfigType.webhook: { - requestCallback(requestBody); + let axios_config = {}; + if (getConfig().app.useHMACForWebhook) { + axios_config = await createBppWebhookAuthHeaderConfig(requestBody); + } + requestCallback(requestBody, axios_config); break; } case ClientConfigType.messageQueue: { diff --git a/src/schemas/configs/app.config.schema.ts b/src/schemas/configs/app.config.schema.ts index 4160bae..180018e 100644 --- a/src/schemas/configs/app.config.schema.ts +++ b/src/schemas/configs/app.config.schema.ts @@ -54,7 +54,9 @@ export const appConfigSchema = z.object({ mandateLayer2Config: z.boolean().optional(), unsolicitedWebhook: z.object({ url: z.string().optional() - }).optional() + }).optional(), + useHMACForWebhook: z.boolean().optional(), + sharedKeyForWebhookHMAC: z.string().optional(), }); export type AppConfigDataType = z.infer; diff --git a/src/utils/auth.utils.ts b/src/utils/auth.utils.ts index 7ed0d38..28c4040 100644 --- a/src/utils/auth.utils.ts +++ b/src/utils/auth.utils.ts @@ -70,9 +70,8 @@ export const createAuthorizationHeader = async (message: any) => { getConfig().app.privateKey || "" ); const subscriber_id = getConfig().app.subscriberId; - const header = `Signature keyId="${subscriber_id}|${ - getConfig().app.uniqueKey - }|ed25519",algorithm="ed25519",created="${created}",expires="${expires}",headers="(created) (expires) digest",signature="${signature}"`; + const header = `Signature keyId="${subscriber_id}|${getConfig().app.uniqueKey + }|ed25519",algorithm="ed25519",created="${created}",expires="${expires}",headers="(created) (expires) digest",signature="${signature}"`; return header; }; @@ -204,8 +203,30 @@ export const createAuthHeaderConfig = async (request: any) => { timeout: getConfig().app.httpTimeout }; logger.info( - `Axios Config for Request from ${ - getConfig().app.mode + " " + getConfig().app.gateway.mode + `Axios Config for Request from ${getConfig().app.mode + " " + getConfig().app.gateway.mode + }:==>\n${JSON.stringify(axios_config)}\n\n` + ); + return axios_config; +}; + +const createBppWebhookAuthHeader = async (request: any) => { + const sodium = _sodium; + const key = getConfig().app.sharedKeyForWebhookHMAC || ""; + const keyUint8Array = sodium.from_string(key); + const messageUint8Array = sodium.from_string(JSON.stringify(request)); + const hmac = sodium.crypto_auth(messageUint8Array, keyUint8Array); + return sodium.to_hex(hmac); +}; + +export const createBppWebhookAuthHeaderConfig = async (request: any) => { + const header = await createBppWebhookAuthHeader(request); + const axios_config = { + headers: { + authorization: header + } + }; + logger.info( + `Axios Config for Request from ${getConfig().app.mode + " " + getConfig().app.gateway.mode }:==>\n${JSON.stringify(axios_config)}\n\n` ); return axios_config; diff --git a/src/utils/callback.utils.ts b/src/utils/callback.utils.ts index ae60010..3e4f664 100644 --- a/src/utils/callback.utils.ts +++ b/src/utils/callback.utils.ts @@ -1,8 +1,7 @@ -import axios from "axios"; +import axios, { AxiosRequestConfig } from "axios"; import { Exception, ExceptionType } from "../models/exception.model"; import { - BecknErrorDataType, - becknErrorSchema + BecknErrorDataType } from "../schemas/becknError.schema"; import { requestCallbackSchema } from "../schemas/callbacks/request.callback.schema"; import { responseCallbackSchema } from "../schemas/callbacks/response.callback.schema"; @@ -10,11 +9,10 @@ import { ClientConfigType, WebhookClientConfigDataType } from "../schemas/configs/client.config.schema"; -import { GatewayMode } from "../schemas/configs/gateway.app.config.schema"; import { getConfig } from "./config.utils"; import logger from "./logger.utils"; -async function makeClientCallback(data: any) { +async function makeClientCallback(data: any, axios_config?: AxiosRequestConfig) { try { if (getConfig().client.type != ClientConfigType.webhook) { throw new Exception( @@ -30,7 +28,7 @@ async function makeClientCallback(data: any) { console.log( `TMTR - ${data?.context?.message_id} - ${getConfig().app.mode}-${getConfig().app.gateway.mode} FORW EXIT: ${new Date().valueOf()}` ); - const response = await axios.post(clientConnectionConfig.url, data); + const response = await axios.post(clientConnectionConfig.url, data, axios_config || {}); logger.info( `Response from Webhook:==>\n ${JSON.stringify( response.data @@ -88,10 +86,10 @@ export async function responseCallback(data: any) { } } -export async function requestCallback(data: any) { +export async function requestCallback(data: any, axios_config?: AxiosRequestConfig) { try { const callbackData = requestCallbackSchema.parse(data); - await makeClientCallback(callbackData); + await makeClientCallback(callbackData, axios_config); } catch (error) { if (error instanceof Exception) { throw error;