@@ -4,12 +4,14 @@ import http from 'http';
44import { ioServer } from '../helpers/socketHelper' ;
55import { SubscriptionManagementService } from '../helpers/requestHelper' ;
66import { getSubscription } from '../helpers/dbHelper' ;
7- import { subscriptionConfiguration } from '../constants' ;
7+ import { subscriptionConfiguration , certificateConfiguration , adalConfiguration } from '../constants' ;
8+ import { decryptSymetricKey , decryptPayload , verifySignature } from '../helpers/certificateHelper' ;
9+ import { isTokenValid } from '../helpers/tokenHelper' ;
810
911export const listenRouter = express . Router ( ) ;
1012
1113/* Default listen route */
12- listenRouter . post ( '/' , ( req , res , next ) => {
14+ listenRouter . post ( '/' , async ( req , res , next ) => {
1315 let status ;
1416 let clientStatesValid ;
1517
@@ -37,17 +39,39 @@ listenRouter.post('/', (req, res, next) => {
3739 }
3840 }
3941
42+ // if we're receiving notifications with resource data we have to validate the origin of the request by validating the tokens
43+ let areTokensValid = true ;
44+ if ( req . body . validationTokens ) {
45+ const validationResults = await Promise . all ( req . body . validationTokens . map ( ( x ) => isTokenValid ( x , adalConfiguration . clientID , adalConfiguration . tenantID ) ) ) ;
46+ areTokensValid = validationResults . reduce ( ( x , y ) => x && y ) ;
47+ }
48+
4049 // If all the clientStates are valid, then process the notification
41- if ( clientStatesValid ) {
50+ if ( clientStatesValid && areTokensValid ) {
4251 for ( let i = 0 ; i < req . body . value . length ; i ++ ) {
4352 const resource = req . body . value [ i ] . resource ;
4453 const subscriptionId = req . body . value [ i ] . subscriptionId ;
45- processNotification ( subscriptionId , resource , res , next ) ;
54+
55+ if ( req . body . value [ i ] . encryptedContent ) {
56+ // we have a notification with resource data, let's decrypt the enclosed data
57+ // eslint-disable-next-line no-loop-func
58+ const decryptedSymetricKey = decryptSymetricKey ( req . body . value [ i ] . encryptedContent . dataKey , certificateConfiguration . relativeKeyPath ) ;
59+ const isSignatureValid = verifySignature ( req . body . value [ i ] . encryptedContent . dataSignature , req . body . value [ i ] . encryptedContent . data , decryptedSymetricKey ) ;
60+ if ( isSignatureValid ) {
61+ // the signature is valid, data hasn't been tampered with. We can proceed to displaying the data
62+ const decryptedPayload = decryptPayload ( req . body . value [ i ] . encryptedContent . data , decryptedSymetricKey ) ;
63+ emitNotification ( subscriptionId , decryptedPayload ) ;
64+ } // otherwise data is invalid, ignore it
65+ } else {
66+ // we have a plain notification that doesn't contain data, let's call Microsoft Graph to get the resource data
67+ processNotification ( subscriptionId , resource , res , next ) ;
68+ }
4669 }
4770 // Send a status of 'Accepted'
4871 status = 202 ;
4972 } else {
5073 // Since the clientState field doesn't have the expected value,
74+ // or the validation tokens are invalid for notifications with data
5175 // this request might NOT come from Microsoft Graph.
5276 // However, you should still return the same status that you'd
5377 // return to Microsoft Graph to not alert possible impostors
@@ -58,16 +82,20 @@ listenRouter.post('/', (req, res, next) => {
5882 res . status ( status ) . end ( http . STATUS_CODES [ status ] ) ;
5983} ) ;
6084
85+ function emitNotification ( subscriptionId , data ) {
86+ ioServer . to ( subscriptionId ) . emit ( 'notification_received' , data ) ;
87+ }
88+
6189// Get subscription data from the database
62- // Retrieve the actual mail message data from Office 365 .
90+ // Retrieve the entity from Microsoft Graph .
6391// Send the message data to the socket.
6492function processNotification ( subscriptionId , resource , res , next ) {
6593 getSubscription ( subscriptionId , async ( dbError , subscriptionData ) => {
6694 if ( subscriptionData ) {
6795 try {
6896 const subscriptionManagementService = new SubscriptionManagementService ( subscriptionData . accessToken ) ;
6997 const endpointData = await subscriptionManagementService . getData ( resource ) ;
70- ioServer . to ( subscriptionId ) . emit ( 'notification_received' , endpointData ) ;
98+ emitNotification ( subscriptionId , endpointData ) ;
7199 } catch ( requestError ) {
72100 res . status ( 500 ) ;
73101 next ( requestError ) ;
0 commit comments