Skip to content

Commit

Permalink
Add expired token exception and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ceciliaavila authored and Tracy Boehrer committed Mar 21, 2023
1 parent 67504ee commit a2bdf10
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 2 deletions.
63 changes: 61 additions & 2 deletions libraries/botbuilder/tests/cloudAdapter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,18 @@ const assert = require('assert');
const httpMocks = require('node-mocks-http');
const net = require('net');
const sinon = require('sinon');
const { BotFrameworkAuthenticationFactory } = require('botframework-connector');
const {
AuthenticationConfiguration,
BotFrameworkAuthenticationFactory,
allowedCallersClaimsValidator,
} = require('botframework-connector');
const {
ConfigurationServiceClientCredentialFactory,
createBotFrameworkAuthenticationFromConfiguration,
} = require('botbuilder');
const { CloudAdapter, ActivityTypes, INVOKE_RESPONSE_KEY } = require('..');
const { NamedPipeServer } = require('botframework-streaming');
const { StatusCodes } = require('botframework-schema');

const FakeBuffer = () => Buffer.from([]);
const FakeNodeSocket = () => new net.Socket();
Expand All @@ -16,7 +25,7 @@ const noop = () => null;
describe('CloudAdapter', function () {
let sandbox;
beforeEach(function () {
sandbox = sinon.createSandbox({ useFakeTimers: true });
sandbox = sinon.createSandbox({ useFakeTimers: false });
});

afterEach(function () {
Expand Down Expand Up @@ -87,6 +96,56 @@ describe('CloudAdapter', function () {

mock.verify();
});

it('throws exception on expired token', async function () {
// Expired token with removed AppID
const authorization =
'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjJaUXBKM1VwYmpBWVhZR2FYRUpsOGxWMFRPSSIsImtpZCI6IjJaUXBKM1VwYmpBWVhZR2FYRUpsOGxWMFRPSSJ9.eyJhdWQiOiJodHRwczovL2FwaS5ib3RmcmFtZXdvcmsuY29tIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZDZkNDk0MjAtZjM5Yi00ZGY3LWExZGMtZDU5YTkzNTg3MWRiLyIsImlhdCI6MTY3MDM1MDQxNSwibmJmIjoxNjcwMzUwNDE1LCJleHAiOjE2NzA0MzcxMTUsImFpbyI6IkUyWmdZTkJONEpWZmxlOTJUc2wxYjhtOHBjOWpBQT09IiwiYXBwaWQiOiI5ZGRmM2QwZS02ZDRlLTQ2MWEtYjM4Yi0zMTYzZWQ3Yjg1NmIiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9kNmQ0OTQyMC1mMzliLTRkZjctYTFkYy1kNTlhOTM1ODcxZGIvIiwicmgiOiIwLkFXNEFJSlRVMXB2ejkwMmgzTldhazFoeDIwSXpMWTBwejFsSmxYY09EcS05RnJ4dUFBQS4iLCJ0aWQiOiJkNmQ0OTQyMC1mMzliLTRkZjctYTFkYy1kNTlhOTM1ODcxZGIiLCJ1dGkiOiJIWDlncld2bU1rMlhESTRkS3BHSEFBIiwidmVyIjoiMS4wIn0.PBLuja5sCcDfFjweoy-VucvbfHEyEcs1GyqXjekzBqgvK-mSc1UrEfqr5834qY6dLNsXVIMJzMFuH6WyPbnAfIfRcabdiVSOAl8N8e9Tex6vHfPi4h4P2F96VkXU80EtZX4QMjsJMDJ5eXbJlIDEAxXoJbAdHqgy-lHcVBx8XK7toJ_W7vSsFhis3C4CPCHI1cf1WuHVwfFXBiNwsOzj9cnRUKpea6UELV89q4C0L6aeSNdWYXehZmgq-wlo2wIaGgQ7rOXx4MlIrc83LBzMMc6TWvBJecK6O8pJWLe6BTwOltBI8Tmo2hWnY1OnsbOhbSSlfwLaZqKI7QpA50_2GQ';

const activity = { type: ActivityTypes.Invoke, value: 'invoke' };

const req = httpMocks.createRequest({
method: 'POST',
headers: { authorization },
body: activity,
});

const res = httpMocks.createResponse();

const logic = async (context) => {
context.turnState.set(INVOKE_RESPONSE_KEY, {
type: ActivityTypes.InvokeResponse,
value: {
status: 200,
body: 'invokeResponse',
},
});
};

const validTokenIssuers = [];
const claimsValidators = allowedCallersClaimsValidator(['*']);
const authConfig = new AuthenticationConfiguration([], claimsValidators, validTokenIssuers);
const credentialsFactory = new ConfigurationServiceClientCredentialFactory({
MicrosoftAppId: '',
MicrosoftAppPassword: '',
MicrosoftAppType: '',
MicrosoftAppTenantId: '',
});

const botFrameworkAuthentication = createBotFrameworkAuthenticationFromConfiguration(
null,
credentialsFactory,
authConfig,
undefined,
undefined
);

const adapter = new CloudAdapter(botFrameworkAuthentication);

await adapter.process(req, res, logic);

assert.equal(StatusCodes.UNAUTHORIZED, res.statusCode);
});
});

describe('connectNamedPipe', function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ export class JwtTokenExtractor {
// from a validated JWT (see `verify` above), so no harm in doing so.
return new ClaimsIdentity(claims, true);
} catch (err) {
if (err.name === 'TokenExpiredError') {
console.error(err);
throw new AuthenticationError('The token has expired', StatusCodes.UNAUTHORIZED);
}
console.error(`Error finding key for token. Available keys: ${metadata.key}`);
throw err;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
const assert = require('assert');
const { jwt } = require('botbuilder-test-utils');
const { JwtTokenExtractor } = require('../../lib/auth/jwtTokenExtractor');
const { StatusCodes } = require('../../../botframework-schema');

describe('JwtTokenExtractor', function () {
jwt.mocha();
Expand Down Expand Up @@ -37,4 +38,27 @@ describe('JwtTokenExtractor', function () {
verify();
});
});

describe('validateToken', function () {
it('throws with expired token', async function () {
const { algorithm, issuer, metadata, sign } = jwt.stub();
const key = 'value';
const token = sign({ key });

const client = new JwtTokenExtractor(
{
issuer,
clockTimestamp: Date.now(),
clockTolerance: -10,
},
metadata,
[algorithm]
);

await assert.rejects(client.getIdentityFromAuthHeader(`Bearer ${token}`), {
message: 'The token has expired',
statusCode: StatusCodes.UNAUTHORIZED,
});
});
});
});

0 comments on commit a2bdf10

Please sign in to comment.