@@ -6,6 +6,12 @@ const httpMocks = require('node-mocks-http');
6
6
const net = require ( 'net' ) ;
7
7
const { expect } = require ( 'chai' ) ;
8
8
const sinon = require ( 'sinon' ) ;
9
+ const jwt = require ( 'jsonwebtoken' ) ;
10
+ const { v4 : uuidv4 } = require ( 'uuid' ) ;
11
+ const nock = require ( 'nock' ) ;
12
+ const forge = require ( 'node-forge' ) ;
13
+ const { generateKeyPairSync } = require ( 'crypto' ) ;
14
+
9
15
const {
10
16
AuthenticationConfiguration,
11
17
AuthenticationConstants,
@@ -130,19 +136,64 @@ describe('CloudAdapter', function () {
130
136
mock . verify ( ) ;
131
137
} ) ;
132
138
133
- //eslint-disable-next-line mocha/no-skipped-tests
134
- it . skip ( 'throws exception on expired token' , async function ( ) {
139
+ it ( 'throws exception on expired token' , async function ( ) {
135
140
const consoleStub = sandbox . stub ( console , 'error' ) ;
136
141
137
- // Expired token with removed AppID
138
- const authorization =
139
- 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ii1LSTNROW5OUjdiUm9meG1lWm9YcWJIWkdldyIsImtpZCI6Ii1LSTNROW5OUjdiUm9meG1lWm9YcWJIWkdldyJ9.eyJhdWQiOiJodHRwczovL2FwaS5ib3RmcmFtZXdvcmsuY29tIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZDZkNDk0MjAtZjM5Yi00ZGY3LWExZGMtZDU5YTkzNTg3MWRiLyIsImlhdCI6MTY5Mjg3MDMwMiwibmJmIjoxNjkyODcwMzAyLCJleHAiOjE2OTI5NTcwMDIsImFpbyI6IkUyRmdZUGhhdFZ6czVydGFFYTlWbDN2ZnIyQ2JBZ0E9IiwiYXBwaWQiOiIxNWYwMTZmZS00ODhjLTQwZTktOWNiZS00Yjk0OGY5OGUyMmMiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9kNmQ0OTQyMC1mMzliLTRkZjctYTFkYy1kNTlhOTM1ODcxZGIvIiwicmgiOiIwLkFXNEFJSlRVMXB2ejkwMmgzTldhazFoeDIwSXpMWTBwejFsSmxYY09EcS05RnJ4dUFBQS4iLCJ0aWQiOiJkNmQ0OTQyMC1mMzliLTRkZjctYTFkYy1kNTlhOTM1ODcxZGIiLCJ1dGkiOiJkenVwa1dWd2FVT2x1RldkbnlvLUFBIiwidmVyIjoiMS4wIn0.sbQH997Q2GDKiiYd6l5MIz_XNfXypJd6zLY9xjtvEgXMBB0x0Vu3fv9W0nM57_ZipQiZDTZuSQA5BE30KBBwU-ZVqQ7MgiTkmE9eF6Ngie_5HwSr9xMK3EiDghHiOP9pIj3oEwGOSyjR5L9n-7tLSdUbKVyV14nS8OQtoPd1LZfoZI3e7tVu3vx8Lx3KzudanXX8Vz7RKaYndj3RyRi4wEN5hV9ab40d7fQsUzygFd5n_PXC2rs0OhjZJzjCOTC0VLQEn1KwiTkSH1E-OSzkrMltn1sbhD2tv_H-4rqQd51vAEJ7esC76qQjz_pfDRLs6T2jvJyhd5MZrN_MT0TqlA' ;
142
+ const { publicKey, privateKey } = generateKeyPairSync ( 'rsa' , {
143
+ modulusLength : 2048 , // Key length (in bits)
144
+ } ) ;
145
+
146
+ const fakeKid = uuidv4 ( ) ;
147
+ const fakeTid = 'd6d49420-f39b-4df7-a1dc-d59a935871db' ;
140
148
141
- const activity = { type : ActivityTypes . Invoke , value : 'invoke' } ;
149
+ // Parse public key to get modulus and exponent
150
+ const publicKeyPem = publicKey . export ( { type : 'spki' , format : 'pem' } ) ;
151
+ const publicKeyForge = forge . pki . publicKeyFromPem ( publicKeyPem ) ;
152
+ const modulus = Buffer . from ( publicKeyForge . n . toByteArray ( ) ) . toString ( 'base64url' ) ;
153
+ const exponent = Buffer . from ( publicKeyForge . e . toByteArray ( ) ) . toString ( 'base64url' ) ;
154
+
155
+ // Mock the request to get jwks_uri
156
+ nock ( 'https://login.microsoftonline.com' ) . get ( '/common/v2.0/.well-known/openid-configuration' ) . reply ( 200 , {
157
+ jwks_uri : 'https://login.microsoftonline.com/common/discovery/v2.0/keys' ,
158
+ } ) ;
159
+
160
+ // Mock the request to the jwks_uri
161
+ nock ( 'https://login.microsoftonline.com' )
162
+ . get ( '/common/discovery/v2.0/keys' )
163
+ . reply ( 200 , {
164
+ keys : [
165
+ {
166
+ kty : 'RSA' ,
167
+ use : 'sig' ,
168
+ kid : fakeKid ,
169
+ e : exponent ,
170
+ n : modulus ,
171
+ } ,
172
+ ] ,
173
+ } ) ;
174
+
175
+ // Mock expired token
176
+ const header = { alg : 'RS256' , typ : 'JWT' , kid : fakeKid } ;
177
+ const payload = {
178
+ aud : 'https://api.botframework.com' ,
179
+ iss : `https://login.microsoftonline.com/${ fakeTid } /v2.0` ,
180
+ iat : Math . floor ( Date . now ( ) / 1000 ) - 7200 , // Issued 2 hours ago
181
+ nbf : Math . floor ( Date . now ( ) / 1000 ) - 7200 , // Not valid before 2 hours ago
182
+ exp : Math . floor ( Date . now ( ) / 1000 ) - 3600 , // Expired 1 hour ago
183
+ tid : fakeTid ,
184
+ ver : '2.0' ,
185
+ } ;
186
+
187
+ // Create the token using the secret key
188
+ const token =
189
+ 'Bearer ' +
190
+ jwt . sign ( payload , privateKey . export ( { type : 'pkcs1' , format : 'pem' } ) , { header, algorithm : 'RS256' } ) ;
142
191
192
+ const activity = { type : ActivityTypes . Invoke , value : 'invoke' } ;
193
+ // Mock request and response
143
194
const req = httpMocks . createRequest ( {
144
195
method : 'POST' ,
145
- headers : { authorization } ,
196
+ headers : { authorization : token } ,
146
197
body : activity ,
147
198
} ) ;
148
199
@@ -179,7 +230,6 @@ describe('CloudAdapter', function () {
179
230
const adapter = new CloudAdapter ( botFrameworkAuthentication ) ;
180
231
181
232
await adapter . process ( req , res , logic ) ;
182
-
183
233
assert . equal ( StatusCodes . UNAUTHORIZED , res . statusCode ) ;
184
234
expect ( consoleStub . calledWithMatch ( { message : 'The token has expired' } ) ) . to . equal ( true ) ;
185
235
} ) ;
0 commit comments