Skip to content

Commit 62112c0

Browse files
authored
port: [#6882] Mock expired token for 'throws exception on expired token' unit test (#4848)
* add fake expired token * remove base64url * fix lint issue
1 parent 7b1434f commit 62112c0

File tree

2 files changed

+60
-8
lines changed

2 files changed

+60
-8
lines changed

libraries/botbuilder/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@
4444
},
4545
"devDependencies": {
4646
"chai": "^4.5.0",
47+
"jsonwebtoken": "^9.0.2",
4748
"lodash": "^4.17.20",
4849
"nock": "^13.5.5",
50+
"node-forge": "^1.3.1",
4951
"node-mocks-http": "^1.16.0"
5052
},
5153
"scripts": {

libraries/botbuilder/tests/cloudAdapter.test.js

+58-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ const httpMocks = require('node-mocks-http');
66
const net = require('net');
77
const { expect } = require('chai');
88
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+
915
const {
1016
AuthenticationConfiguration,
1117
AuthenticationConstants,
@@ -130,19 +136,64 @@ describe('CloudAdapter', function () {
130136
mock.verify();
131137
});
132138

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 () {
135140
const consoleStub = sandbox.stub(console, 'error');
136141

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';
140148

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' });
142191

192+
const activity = { type: ActivityTypes.Invoke, value: 'invoke' };
193+
// Mock request and response
143194
const req = httpMocks.createRequest({
144195
method: 'POST',
145-
headers: { authorization },
196+
headers: { authorization: token },
146197
body: activity,
147198
});
148199

@@ -179,7 +230,6 @@ describe('CloudAdapter', function () {
179230
const adapter = new CloudAdapter(botFrameworkAuthentication);
180231

181232
await adapter.process(req, res, logic);
182-
183233
assert.equal(StatusCodes.UNAUTHORIZED, res.statusCode);
184234
expect(consoleStub.calledWithMatch({ message: 'The token has expired' })).to.equal(true);
185235
});

0 commit comments

Comments
 (0)