Skip to content

Commit

Permalink
Fix #674 Add tokens_revoked / app_uninstalled event supports (#1328)
Browse files Browse the repository at this point in the history
  • Loading branch information
seratch authored Feb 23, 2022
1 parent 113bbab commit 5ea31af
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 18 deletions.
104 changes: 104 additions & 0 deletions src/App.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,110 @@ describe('App', () => {
// Assert
assert.isTrue(workedAsExpected);
});
it('should be skipped for tokens_revoked events #674', async () => {
// Arrange
const fakeAxiosPost = sinon.fake.resolves({});
overrides = buildOverrides([withNoopWebClient(), withAxiosPost(fakeAxiosPost)]);
const MockApp = await importApp(overrides);

// Act
let workedAsExpected = false;
let authorizeCallCount = 0;
const app = new MockApp({
receiver: fakeReceiver,
authorize: async () => {
authorizeCallCount += 1;
return {};
},
});
app.event('tokens_revoked', async () => {
workedAsExpected = true;
});

// The authorize must be called for other events
await fakeReceiver.sendEvent({
ack: noop,
body: {
enterprise_id: 'E_org_id',
api_app_id: 'A111',
event: {
type: 'app_mention',
},
type: 'event_callback',
},
});
assert.equal(authorizeCallCount, 1);

await fakeReceiver.sendEvent({
ack: noop,
body: {
enterprise_id: 'E_org_id',
api_app_id: 'A111',
event: {
type: 'tokens_revoked',
tokens: {
oauth: ['P'],
bot: ['B'],
},
},
type: 'event_callback',
},
});

// Assert
assert.equal(authorizeCallCount, 1); // still 1
assert.isTrue(workedAsExpected);
});
it('should be skipped for app_uninstalled events #674', async () => {
// Arrange
const fakeAxiosPost = sinon.fake.resolves({});
overrides = buildOverrides([withNoopWebClient(), withAxiosPost(fakeAxiosPost)]);
const MockApp = await importApp(overrides);

// Act
let workedAsExpected = false;
let authorizeCallCount = 0;
const app = new MockApp({
receiver: fakeReceiver,
authorize: async () => {
authorizeCallCount += 1;
return {};
},
});
app.event('app_uninstalled', async () => {
workedAsExpected = true;
});

// The authorize must be called for other events
await fakeReceiver.sendEvent({
ack: noop,
body: {
enterprise_id: 'E_org_id',
api_app_id: 'A111',
event: {
type: 'app_mention',
},
type: 'event_callback',
},
});
assert.equal(authorizeCallCount, 1);

await fakeReceiver.sendEvent({
ack: noop,
body: {
enterprise_id: 'E_org_id',
api_app_id: 'A111',
event: {
type: 'app_uninstalled',
},
type: 'event_callback',
},
});

// Assert
assert.equal(authorizeCallCount, 1); // still 1
assert.isTrue(workedAsExpected);
});
});

describe('routing', () => {
Expand Down
50 changes: 32 additions & 18 deletions src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -792,25 +792,32 @@ export default class App {
const source = buildSource(type, conversationId, bodyArg, isEnterpriseInstall);

let authorizeResult: AuthorizeResult;
try {
if (source.isEnterpriseInstall) {
authorizeResult = await this.authorize(source as AuthorizeSourceData<true>, bodyArg);
} else {
authorizeResult = await this.authorize(source as AuthorizeSourceData<false>, bodyArg);
if (type === IncomingEventType.Event && isEventTypeToSkipAuthorize(event.body.event.type)) {
authorizeResult = {
enterpriseId: source.enterpriseId,
teamId: source.teamId,
};
} else {
try {
if (source.isEnterpriseInstall) {
authorizeResult = await this.authorize(source as AuthorizeSourceData<true>, bodyArg);
} else {
authorizeResult = await this.authorize(source as AuthorizeSourceData<false>, bodyArg);
}
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const e = error as any;
this.logger.warn('Authorization of incoming event did not succeed. No listeners will be called.');
e.code = ErrorCode.AuthorizationError;
// disabling due to https://github.com/typescript-eslint/typescript-eslint/issues/1277
// eslint-disable-next-line consistent-return
return this.handleError({
error: e,
logger: this.logger,
body: bodyArg,
context: {},
});
}
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const e = error as any;
this.logger.warn('Authorization of incoming event did not succeed. No listeners will be called.');
e.code = ErrorCode.AuthorizationError;
// disabling due to https://github.com/typescript-eslint/typescript-eslint/issues/1277
// eslint-disable-next-line consistent-return
return this.handleError({
error: e,
logger: this.logger,
body: bodyArg,
context: {},
});
}

// Try to set teamId from AuthorizeResult before using one from source
Expand Down Expand Up @@ -1448,6 +1455,13 @@ function buildRespondFn(
};
}

// token revocation use cases
// https://github.com/slackapi/bolt-js/issues/674
const eventTypesToSkipAuthorize = ['app_uninstalled', 'tokens_revoked'];
function isEventTypeToSkipAuthorize(eventType: string) {
return eventTypesToSkipAuthorize.includes(eventType);
}

// ----------------------------
// Instrumentation
// Don't change the position of the following code
Expand Down

0 comments on commit 5ea31af

Please sign in to comment.