@slack/oauth
Advanced tools
Comparing version 1.3.0 to 1.4.0
@@ -28,5 +28,5 @@ /// <reference types="node" /> | ||
/** | ||
* Fetches data from the installationStore. | ||
* Fetches data from the installationStore for non Org Installations. | ||
*/ | ||
authorize(source: InstallationQuery): Promise<AuthorizeResult>; | ||
authorize(source: InstallationQuery<boolean>): Promise<AuthorizeResult>; | ||
/** | ||
@@ -68,3 +68,3 @@ * Returns a URL that is suitable for including in an Add to Slack button | ||
export interface CallbackOptions { | ||
success?: (installation: Installation, options: InstallURLOptions, callbackReq: IncomingMessage, callbackRes: ServerResponse) => void; | ||
success?: (installation: Installation | OrgInstallation, options: InstallURLOptions, callbackReq: IncomingMessage, callbackRes: ServerResponse) => void; | ||
failure?: (error: CodedError, options: InstallURLOptions, callbackReq: IncomingMessage, callbackRes: ServerResponse) => void; | ||
@@ -77,13 +77,56 @@ } | ||
export interface InstallationStore { | ||
storeInstallation: (installation: Installation, logger?: Logger) => Promise<void>; | ||
fetchInstallation: (query: InstallationQuery, logger?: Logger) => Promise<Installation>; | ||
storeInstallation<AuthVersion extends 'v1' | 'v2'>(installation: Installation<AuthVersion, false>, logger?: Logger): Promise<void>; | ||
storeOrgInstallation?(installation: OrgInstallation, logger?: Logger): Promise<void>; | ||
fetchInstallation: (query: InstallationQuery<false>, logger?: Logger) => Promise<Installation<'v1' | 'v2', false>>; | ||
fetchOrgInstallation?: (query: OrgInstallationQuery, logger?: Logger) => Promise<OrgInstallation>; | ||
} | ||
export interface Installation { | ||
team: { | ||
/** | ||
* An individual installation of the Slack app. | ||
* | ||
* This interface creates a representation for installations that normalizes the responses from OAuth grant exchanges | ||
* across auth versions (responses from the Web API methods `oauth.v2.access` and `oauth.access`). It describes some of | ||
* these differences using the `AuthVersion` generic placeholder type. | ||
* | ||
* This interface also represents both installations which occur on individual Slack workspaces and on Slack enterprise | ||
* organizations. The `IsEnterpriseInstall` generic placeholder type is used to describe some of those differences. | ||
* | ||
* This representation is designed to be used both when producing data that should be stored by an InstallationStore, | ||
* and when consuming data that is fetched from an InstallationStore. Most often, InstallationStore implementations | ||
* are a database. If you are going to implement an InstallationStore, it's advised that you **store as much of the | ||
* data in these objects as possible so that you can return as much as possible inside `fetchInstallation()`**. | ||
* | ||
* A few properties are synthesized with a default value if they are not present when returned from | ||
* `fetchInstallation()`. These properties are optional in the interface so that stored installations from previous | ||
* versions of this library (from before those properties were introduced) continue to work without requiring a breaking | ||
* change. However the synthesized default values are not always perfect and are based on some assumptions, so this is | ||
* why it's recommended to store as much of that data as possible in any InstallationStore. | ||
* | ||
* Some of the properties (e.g. `team.name`) can change between when the installation occurred and when it is fetched | ||
* from the InstallationStore. This can be seen as a reason not to store those properties. In most workspaces these | ||
* properties rarely change, and for most Slack apps having a slightly out of date value has no impact. However if your | ||
* app uses these values in a way where it must be up to date, it's recommended to implement a caching strategy in the | ||
* InstallationStore to fetch the latest data from the Web API (using methods such as `auth.test`, `teams.info`, etc.) | ||
* as often as it makes sense for your Slack app. | ||
* | ||
* TODO: IsEnterpriseInstall is always false when AuthVersion is v1 | ||
*/ | ||
export interface Installation<AuthVersion extends ('v1' | 'v2') = ('v1' | 'v2'), IsEnterpriseInstall extends boolean = boolean> { | ||
/** | ||
* TODO: when performing a “single workspace” install with the admin scope on the enterprise, | ||
* is the team property returned from oauth.access? | ||
*/ | ||
team: IsEnterpriseInstall extends true ? undefined : { | ||
id: string; | ||
name: string; | ||
/** Left as undefined when not returned from fetch. */ | ||
name?: string; | ||
}; | ||
enterprise?: { | ||
/** | ||
* When the installation is an enterprise install or when the installation occurs on the org to acquire `admin` scope, | ||
* the name and ID of the enterprise org. | ||
*/ | ||
enterprise: IsEnterpriseInstall extends true ? EnterpriseInfo : (EnterpriseInfo | undefined); | ||
user: { | ||
token: AuthVersion extends 'v1' ? string : (string | undefined); | ||
scopes: AuthVersion extends 'v1' ? string[] : (string[] | undefined); | ||
id: string; | ||
name?: string; | ||
}; | ||
@@ -93,25 +136,44 @@ bot?: { | ||
scopes: string[]; | ||
id?: string; | ||
id: string; | ||
userId: string; | ||
}; | ||
user: { | ||
token?: string; | ||
scopes?: string[]; | ||
id: string; | ||
}; | ||
incomingWebhook?: { | ||
url: string; | ||
channel: string; | ||
channelId: string; | ||
configurationUrl: string; | ||
/** Left as undefined when not returned from fetch. */ | ||
channel?: string; | ||
/** Left as undefined when not returned from fetch. */ | ||
channelId?: string; | ||
/** Left as undefined when not returned from fetch. */ | ||
configurationUrl?: string; | ||
}; | ||
appId: string | undefined; | ||
tokenType?: string; | ||
/** The App ID, which does not vary per installation. Left as undefined when not returned from fetch. */ | ||
appId?: AuthVersion extends 'v2' ? string : undefined; | ||
/** When the installation contains a bot user, the token type. Left as undefined when not returned from fetch. */ | ||
tokenType?: 'bot'; | ||
/** | ||
* When the installation is an enterprise org install, the URL of the landing page for all workspaces in the org. | ||
* Left as undefined when not returned from fetch. | ||
*/ | ||
enterpriseUrl?: AuthVersion extends 'v2' ? string : undefined; | ||
/** Whether the installation was performed on an enterprise org. Synthesized as `false` when not present. */ | ||
isEnterpriseInstall?: IsEnterpriseInstall; | ||
/** The version of Slack's auth flow that produced this installation. Synthesized as `v2` when not present. */ | ||
authVersion?: AuthVersion; | ||
} | ||
export interface InstallationQuery { | ||
teamId: string; | ||
enterpriseId?: string; | ||
/** | ||
* A type to describe enterprise organization installations. | ||
*/ | ||
export declare type OrgInstallation = Installation<'v2', true>; | ||
interface EnterpriseInfo { | ||
id: string; | ||
name?: string; | ||
} | ||
export interface InstallationQuery<isEnterpriseInstall extends boolean> { | ||
teamId: isEnterpriseInstall extends false ? string : undefined; | ||
enterpriseId: isEnterpriseInstall extends true ? string : (string | undefined); | ||
userId?: string; | ||
conversationId?: string; | ||
isEnterpriseInstall: isEnterpriseInstall; | ||
} | ||
export declare type OrgInstallationQuery = InstallationQuery<true>; | ||
export interface AuthorizeResult { | ||
@@ -122,4 +184,6 @@ botToken?: string; | ||
botUserId?: string; | ||
teamId?: string; | ||
enterpriseId?: string; | ||
} | ||
export { Logger, LogLevel } from './logger'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -108,3 +108,3 @@ "use strict"; | ||
/** | ||
* Fetches data from the installationStore. | ||
* Fetches data from the installationStore for non Org Installations. | ||
*/ | ||
@@ -117,6 +117,17 @@ InstallProvider.prototype.authorize = function (source) { | ||
case 0: | ||
_a.trys.push([0, 2, , 3]); | ||
return [4 /*yield*/, this.installationStore.fetchInstallation(source, this.logger)]; | ||
_a.trys.push([0, 5, , 6]); | ||
queryResult = void 0; | ||
if (!source.isEnterpriseInstall) return [3 /*break*/, 2]; | ||
if (this.installationStore.fetchOrgInstallation === undefined) { | ||
throw new Error('Installation Store is missing the fetchOrgInstallation method'); | ||
} | ||
return [4 /*yield*/, this.installationStore.fetchOrgInstallation(source, this.logger)]; | ||
case 1: | ||
queryResult = _a.sent(); | ||
return [3 /*break*/, 4]; | ||
case 2: return [4 /*yield*/, this.installationStore.fetchInstallation(source, this.logger)]; | ||
case 3: | ||
queryResult = _a.sent(); | ||
_a.label = 4; | ||
case 4: | ||
if (queryResult === undefined) { | ||
@@ -127,2 +138,18 @@ throw new Error('Failed fetching data from the Installation Store'); | ||
authResult.userToken = queryResult.user.token; | ||
if (queryResult.team !== undefined) { | ||
authResult.teamId = queryResult.team.id; | ||
} | ||
else if (source.teamId !== undefined) { | ||
/** | ||
* since queryResult is a org installation, it won't have team.id. If one was passed in via source, | ||
* we should add it to the authResult | ||
*/ | ||
authResult.teamId = source.teamId; | ||
} | ||
if (queryResult.enterprise !== undefined) { | ||
authResult.enterpriseId = queryResult.enterprise.id; | ||
} | ||
else if (source.enterpriseId !== undefined) { | ||
authResult.enterpriseId = source.enterpriseId; | ||
} | ||
if (queryResult.bot !== undefined) { | ||
@@ -134,6 +161,6 @@ authResult.botToken = queryResult.bot.token; | ||
return [2 /*return*/, authResult]; | ||
case 2: | ||
case 5: | ||
error_1 = _a.sent(); | ||
throw new errors_1.AuthorizationError(error_1.message); | ||
case 3: return [2 /*return*/]; | ||
case 6: return [2 /*return*/]; | ||
} | ||
@@ -205,8 +232,9 @@ }); | ||
InstallProvider.prototype.handleCallback = function (req, res, options) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var parsedUrl, code, state, installOptions, client, resp, installation, botId, botId, error_2; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
var parsedUrl, code, state, installOptions, client, installation, resp, v1Resp, v1Installation, authResult, botId, v2Resp, v2Installation, authResult, authResult, error_2; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
_a.trys.push([0, 10, , 11]); | ||
_b.trys.push([0, 17, , 18]); | ||
if (req.url !== undefined) { | ||
@@ -225,6 +253,6 @@ parsedUrl = url_1.parse(req.url, true); | ||
case 1: | ||
installOptions = _a.sent(); | ||
client = new web_api_1.WebClient('', this.clientOptions); | ||
installOptions = _b.sent(); | ||
client = new web_api_1.WebClient(undefined, this.clientOptions); | ||
installation = void 0; | ||
resp = void 0; | ||
installation = void 0; | ||
if (!(this.authVersion === 'v1')) return [3 /*break*/, 5]; | ||
@@ -238,32 +266,31 @@ return [4 /*yield*/, client.oauth.access({ | ||
case 2: | ||
// convert response type from WebApiCallResult to OAuthResponse | ||
resp = (_a.sent()); | ||
// resp obj for v1 - https://api.slack.com/methods/oauth.access#response | ||
installation = { | ||
team: { id: resp.team_id, name: resp.team_name }, | ||
appId: resp.app_id, | ||
v1Resp = _b.sent(); | ||
v1Installation = { | ||
team: { id: v1Resp.team_id, name: v1Resp.team_name }, | ||
enterprise: v1Resp.enterprise_id === null ? undefined : { id: v1Resp.enterprise_id }, | ||
user: { | ||
token: resp.access_token, | ||
scopes: resp.scope.split(','), | ||
id: resp.user_id !== undefined ? resp.user_id : '', | ||
token: v1Resp.access_token, | ||
scopes: v1Resp.scope.split(','), | ||
id: v1Resp.user_id, | ||
}, | ||
// synthesized properties: enterprise installation is unsupported in v1 auth | ||
isEnterpriseInstall: false, | ||
authVersion: 'v1', | ||
}; | ||
if (!(resp.bot !== undefined)) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, getBotId(resp.bot.bot_access_token, this.clientOptions)]; | ||
if (!(v1Resp.bot !== undefined)) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, runAuthTest(v1Resp.bot.bot_access_token, this.clientOptions)]; | ||
case 3: | ||
botId = _a.sent(); | ||
installation.bot = { | ||
authResult = _b.sent(); | ||
botId = authResult.bot_id; | ||
v1Installation.bot = { | ||
id: botId, | ||
scopes: ['bot'], | ||
token: resp.bot.bot_access_token, | ||
userId: resp.bot.bot_user_id, | ||
token: v1Resp.bot.bot_access_token, | ||
userId: v1Resp.bot.bot_user_id, | ||
}; | ||
_a.label = 4; | ||
_b.label = 4; | ||
case 4: | ||
if (resp.enterprise_id !== null) { | ||
installation.enterprise = { | ||
id: resp.enterprise_id, | ||
}; | ||
} | ||
return [3 /*break*/, 8]; | ||
resp = v1Resp; | ||
installation = v1Installation; | ||
return [3 /*break*/, 12]; | ||
case 5: return [4 /*yield*/, client.oauth.v2.access({ | ||
@@ -276,32 +303,47 @@ code: code, | ||
case 6: | ||
// convert response type from WebApiCallResult to OAuthResponse | ||
resp = (_a.sent()); | ||
return [4 /*yield*/, getBotId(resp.access_token, this.clientOptions)]; | ||
case 7: | ||
botId = _a.sent(); | ||
// resp obj for v2 - https://api.slack.com/methods/oauth.v2.access#response | ||
installation = { | ||
team: resp.team, | ||
appId: resp.app_id, | ||
v2Resp = _b.sent(); | ||
v2Installation = { | ||
team: v2Resp.team === null ? undefined : v2Resp.team, | ||
enterprise: v2Resp.enterprise == null ? undefined : v2Resp.enterprise, | ||
user: { | ||
token: resp.authed_user.access_token, | ||
scopes: resp.authed_user.scope !== undefined ? resp.authed_user.scope.split(',') : undefined, | ||
id: resp.authed_user.id, | ||
token: v2Resp.authed_user.access_token, | ||
scopes: (_a = v2Resp.authed_user.scope) === null || _a === void 0 ? void 0 : _a.split(','), | ||
id: v2Resp.authed_user.id, | ||
}, | ||
bot: { | ||
scopes: resp.scope.split(','), | ||
token: resp.access_token, | ||
userId: resp.bot_user_id, | ||
id: botId, | ||
}, | ||
tokenType: resp.token_type, | ||
tokenType: v2Resp.token_type, | ||
isEnterpriseInstall: v2Resp.is_enterprise_install, | ||
appId: v2Resp.app_id, | ||
// synthesized properties | ||
authVersion: 'v2', | ||
}; | ||
if (resp.enterprise !== null) { | ||
installation.enterprise = { | ||
id: resp.enterprise.id, | ||
name: resp.enterprise.name, | ||
}; | ||
if (!(v2Resp.access_token !== undefined && v2Resp.scope !== undefined && v2Resp.bot_user_id !== undefined)) return [3 /*break*/, 8]; | ||
return [4 /*yield*/, runAuthTest(v2Resp.access_token, this.clientOptions)]; | ||
case 7: | ||
authResult = _b.sent(); | ||
v2Installation.bot = { | ||
scopes: v2Resp.scope.split(','), | ||
token: v2Resp.access_token, | ||
userId: v2Resp.bot_user_id, | ||
id: authResult.bot_id, | ||
}; | ||
if (v2Resp.is_enterprise_install) { | ||
// if it is an org enterprise install, add the enterprise url | ||
v2Installation.enterpriseUrl = authResult.url; | ||
} | ||
_a.label = 8; | ||
return [3 /*break*/, 11]; | ||
case 8: | ||
if (!(v2Resp.authed_user.access_token !== undefined && v2Resp.is_enterprise_install)) return [3 /*break*/, 10]; | ||
return [4 /*yield*/, runAuthTest(v2Resp.authed_user.access_token, this.clientOptions)]; | ||
case 9: | ||
authResult = _b.sent(); | ||
v2Installation.enterpriseUrl = authResult.url; | ||
return [3 /*break*/, 11]; | ||
case 10: | ||
// TODO: make this a coded error | ||
throw new Error('The response from the authorization URL contained inconsistent information. Please file a bug.'); | ||
case 11: | ||
resp = v2Resp; | ||
installation = v2Installation; | ||
_b.label = 12; | ||
case 12: | ||
if (resp.incoming_webhook !== undefined) { | ||
@@ -315,7 +357,17 @@ installation.incomingWebhook = { | ||
} | ||
// save access code to installationStore | ||
return [4 /*yield*/, this.installationStore.storeInstallation(installation, this.logger)]; | ||
case 9: | ||
// save access code to installationStore | ||
_a.sent(); | ||
if (!installation.isEnterpriseInstall) return [3 /*break*/, 14]; | ||
if (this.installationStore.storeOrgInstallation === undefined) { | ||
// TODO: make this a coded error | ||
throw new Error('Installation store is missing the storeOrgInstallation method'); | ||
} | ||
return [4 /*yield*/, this.installationStore.storeOrgInstallation(installation, this.logger)]; | ||
case 13: | ||
_b.sent(); | ||
return [3 /*break*/, 16]; | ||
case 14: return [4 /*yield*/, this.installationStore.storeInstallation(installation, this.logger)]; | ||
case 15: | ||
_b.sent(); | ||
_b.label = 16; | ||
case 16: | ||
// Call the success callback | ||
if (options !== undefined && options.success !== undefined) { | ||
@@ -329,6 +381,7 @@ this.logger.debug('calling passed in options.success'); | ||
} | ||
return [3 /*break*/, 11]; | ||
case 10: | ||
error_2 = _a.sent(); | ||
return [3 /*break*/, 18]; | ||
case 17: | ||
error_2 = _b.sent(); | ||
this.logger.error(error_2); | ||
// Call the failure callback | ||
if (options !== undefined && options.failure !== undefined) { | ||
@@ -342,4 +395,4 @@ this.logger.debug('calling passed in options.failure'); | ||
} | ||
return [3 /*break*/, 11]; | ||
case 11: return [2 /*return*/]; | ||
return [3 /*break*/, 18]; | ||
case 18: return [2 /*return*/]; | ||
} | ||
@@ -386,7 +439,15 @@ }); | ||
return __generator(this, function (_a) { | ||
// NOTE: installations on a single workspace that happen to be within an enterprise organization are stored by | ||
// the team ID as the key | ||
// TODO: what about installations on an enterprise (acting as a single workspace) with `admin` scope, which is not | ||
// an org install? | ||
if (logger !== undefined) { | ||
logger.warn('Storing Access Token. Please use a real Installation Store for production!'); | ||
} | ||
// db write | ||
this.devDB[installation.team.id] = installation; | ||
if (isNotOrgInstall(installation)) { | ||
this.devDB[installation.team.id] = installation; | ||
} | ||
else { | ||
throw new Error('Failed saving installation data to installationStore'); | ||
} | ||
return [2 /*return*/]; | ||
@@ -396,5 +457,20 @@ }); | ||
}; | ||
MemoryInstallationStore.prototype.storeOrgInstallation = function (installation, logger) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
if (logger !== undefined) { | ||
logger.warn('Storing Access Token. Please use a real Installation Store for production!'); | ||
} | ||
if (isOrgInstall(installation)) { | ||
this.devDB[installation.enterprise.id] = installation; | ||
} | ||
else { | ||
throw new Error('Failed saving installation data to installationStore'); | ||
} | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
}; | ||
MemoryInstallationStore.prototype.fetchInstallation = function (query, logger) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var item; | ||
return __generator(this, function (_a) { | ||
@@ -404,7 +480,22 @@ if (logger !== undefined) { | ||
} | ||
item = this.devDB[query.teamId]; | ||
return [2 /*return*/, item]; | ||
if (query.teamId !== undefined) { | ||
return [2 /*return*/, this.devDB[query.teamId]]; | ||
} | ||
throw new Error('Failed fetching installation'); | ||
}); | ||
}); | ||
}; | ||
MemoryInstallationStore.prototype.fetchOrgInstallation = function (query, logger) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
if (logger !== undefined) { | ||
logger.warn('Retrieving Access Token from DB. Please use a real Installation Store for production!'); | ||
} | ||
if (query.enterpriseId !== undefined) { | ||
return [2 /*return*/, this.devDB[query.enterpriseId]]; | ||
} | ||
throw new Error('Failed fetching installation'); | ||
}); | ||
}); | ||
}; | ||
return MemoryInstallationStore; | ||
@@ -415,3 +506,3 @@ }()); | ||
var redirectUrl; | ||
if (installation.team !== null && installation.team.id !== undefined && installation.appId !== undefined) { | ||
if (isNotOrgInstall(installation) && installation.appId !== undefined) { | ||
// redirect back to Slack native app | ||
@@ -421,2 +512,6 @@ // Changes to the workspace app was installed to, to the app home | ||
} | ||
else if (isOrgInstall(installation)) { | ||
// redirect to Slack app management dashboard | ||
redirectUrl = installation.enterpriseUrl + "manage/organization/apps/profile/" + installation.appId + "/workspaces/add"; | ||
} | ||
else { | ||
@@ -437,3 +532,3 @@ // redirect back to Slack native app | ||
// Gets the bot_id using the `auth.test` method. | ||
function getBotId(token, clientOptions) { | ||
function runAuthTest(token, clientOptions) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
@@ -448,8 +543,3 @@ var client, authResult; | ||
authResult = _a.sent(); | ||
if (authResult.bot_id !== undefined) { | ||
return [2 /*return*/, authResult.bot_id]; | ||
} | ||
// If a user token was used for auth.test, there is no bot_id | ||
// return an empty string in this case | ||
return [2 /*return*/, '']; | ||
return [2 /*return*/, authResult]; | ||
} | ||
@@ -459,4 +549,11 @@ }); | ||
} | ||
// Type guard to narrow Installation type to OrgInstallation | ||
function isOrgInstall(installation) { | ||
return installation.isEnterpriseInstall || false; | ||
} | ||
function isNotOrgInstall(installation) { | ||
return !(isOrgInstall(installation)); | ||
} | ||
var logger_2 = require("./logger"); | ||
exports.LogLevel = logger_2.LogLevel; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@slack/oauth", | ||
"version": "1.3.0", | ||
"version": "1.4.0", | ||
"description": "Official library for interacting with Slack's Oauth endpoints", | ||
@@ -5,0 +5,0 @@ "author": "Slack Technologies, Inc.", |
@@ -35,3 +35,3 @@ # Slack OAuth | ||
This package exposes an `InstallProvider` class, which sets up the required configuration and exposes methods such as `generateInstallUrl`, `handleCallback`, `authorize` for use within your apps. At a minimum, `InstallProvider` takes a `clientId` and `clientSecret` (both which can be obtained under the **Basic Information** of your app configuration). `InstallProvider` also requires a `stateSecret`, which is used to encode the generated state, and later used to decode that same state to verify it wasn't tampered with during the OAuth flow. **Note**: This example is not ready for production because it only stores installations (tokens) in memory. Please go to the [storing installations in a database](#storing-installations-in-a-database) section to learn how to plug in your own database. | ||
This package exposes an `InstallProvider` class, which sets up the required configuration and exposes methods such as `generateInstallUrl`, `handleCallback`, `authorize`, `orgAuthorize` for use within your apps. At a minimum, `InstallProvider` takes a `clientId` and `clientSecret` (both which can be obtained under the **Basic Information** of your app configuration). `InstallProvider` also requires a `stateSecret`, which is used to encode the generated state, and later used to decode that same state to verify it wasn't tampered with during the OAuth flow. **Note**: This example is not ready for production because it only stores installations (tokens) in memory. Please go to the [storing installations in a database](#storing-installations-in-a-database) section to learn how to plug in your own database. | ||
@@ -146,3 +146,3 @@ ```javascript | ||
const htmlResponse = `<html><body>Success!</body></html>` | ||
res.writeHead(200, { 'Content-Type': 'text/html' }); | ||
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); | ||
res.end(htmlResponse); | ||
@@ -152,3 +152,3 @@ }, | ||
// Do custom failure logic here | ||
res.writeHead(500, { 'Content-Type': 'text/html' }); | ||
res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' }); | ||
res.end('<html><body><h1>Oops, Something Went Wrong! Please Try Again or Contact the App Owner</h1></body></html>'); | ||
@@ -168,6 +168,8 @@ } | ||
An installation store is an object that provides two methods: `storeInstallation` and `fetchInstallation`. `storeInstallation` takes an `installation` as an argument, which is an object that contains all installation related data (like tokens, teamIds, enterpriseIds, etc). `fetchInstallation` takes in a `installQuery`, which is used to query the database. The `installQuery` can contain `teamId`, `enterpriseId`, `userId`, and `conversationId`. | ||
An installation store is an object that provides four methods: `storeInstallation`, `storeOrgInstallation`, `fetchInstallation` and `fetchOrgInstallation`. `storeInstallation` and `storeOrgInstallation` takes an `installation` as an argument, which is an object that contains all installation related data (like tokens, teamIds, enterpriseIds, etc). `fetchInstallation` and `fetchOrgInstallation` takes in a `installQuery`, which is used to query the database. The `installQuery` can contain `teamId`, `enterpriseId`, `userId`, and `conversationId`. | ||
In the following example, the `installationStore` option is used and the object is defined in line. The required methods are implemented by calling an example database library with simple get and set operations. | ||
**Note**: `fetchOrgInstallation` and `storeOrgInstallation` were introduced to support Org wide app installations (currently in beta). | ||
In the following example, the `installationStore` option is used and the object is defined in line. The methods are implemented by calling an example database library with simple get and set operations. | ||
```javascript | ||
@@ -181,6 +183,10 @@ const installer = new InstallProvider({ | ||
// returns nothing | ||
storeInstallation: (installation) => { | ||
storeInstallation: async (installation) => { | ||
// replace myDB.set with your own database or OEM setter | ||
myDB.set(installation.team.id, installation); | ||
return; | ||
if (installation.team.id !== undefined) { | ||
// non enterprise org app installation | ||
return myDB.set(installation.team.id, installation); | ||
} else { | ||
throw new Error('Failed saving installation data to installationStore'); | ||
} | ||
}, | ||
@@ -190,6 +196,26 @@ // takes in an installQuery as an argument | ||
// returns installation object from database | ||
fetchInstallation: (installQuery) => { | ||
fetchInstallation: async (installQuery) => { | ||
// replace myDB.get with your own database or OEM getter | ||
return myDB.get(installQuery.teamId); | ||
// non enterprise org app lookup | ||
return await myDB.get(installQuery.teamId); | ||
}, | ||
// takes in an installation object as an argument | ||
// returns nothing | ||
storeOrgInstallation: async (installation) => { | ||
// replace myDB.set with your own database or OEM setter | ||
if (installation.isEnterpriseInstall && installation.enterprise !== undefined) { | ||
// enterprise app, org wide installation | ||
return myDB.set(installation.enterprise.id, installation); | ||
} else { | ||
throw new Error('Failed saving installation data to installationStore'); | ||
} | ||
}, | ||
// takes in an installQuery as an argument | ||
// installQuery = {teamId: 'string', enterpriseId: 'string', userId: string, conversationId: 'string'}; | ||
// returns installation object from database | ||
fetchInstallation: async (installQuery) => { | ||
// replace myDB.get with your own database or OEM getter | ||
// enterprise org app installation lookup | ||
return await myDB.get(installQuery.enterpriseId); | ||
}, | ||
}, | ||
@@ -202,3 +228,3 @@ }); | ||
You can use the the `installationProvider.authorize()` function to fetch data that has been saved in your installation store. | ||
You can use the the `installationProvider.authorize()` function to fetch data that has been saved in your installation store. For Org wide app installations, you can use `installationProvider.orgAuthorize()` | ||
@@ -208,3 +234,4 @@ ```javascript | ||
// installQuery = {teamId: 'string', enterpriseId: 'string', userId: string, conversationId: 'string'}; | ||
const result = installer.installationStore.fetchInstallation({teamId:'my-team-ID', enterpriseId:'my-enterprise-ID'}); | ||
const result = installer.authorize({teamId: 'my-team-ID'}); | ||
const orgResult = installer.orgAuthorize({enterpriseId: 'my-enterprise-ID'}); | ||
/* | ||
@@ -225,3 +252,3 @@ result = { | ||
The `installer.authorize()` method only returns a subset of the installation data returned by the installation store. To fetch the entire saved installation, use the `installer.installationStore.fetchInstallation()` method. | ||
The `installer.authorize()`/`installer.orgAuthorize()` methods only returns a subset of the installation data returned by the installation store. To fetch the entire saved installation, use the `installer.installationStore.fetchInstallation()`/`installer.installationStore.fetchOrgInstallation()` methods. | ||
@@ -232,3 +259,4 @@ ```javascript | ||
// returns an installation object | ||
const result = installer.installationStore.fetchInstallation({teamId:'my-Team-ID'}); | ||
const result = await installer.installationStore.fetchInstallation({teamId:'my-team-ID', enterpriseId:'my-enterprise-ID'}); | ||
const orgResult = await installer.installationStore.fetchOrgInstallation({enterpriseId:'my-enterprise-ID'}); | ||
``` | ||
@@ -235,0 +263,0 @@ </details> |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
84356
866
379