@slack/oauth
Advanced tools
Comparing version 1.1.0-beta.1 to 1.1.0-orgAppsBeta.2
@@ -25,6 +25,10 @@ /// <reference types="node" /> | ||
/** | ||
* Fetches data from the installationStore. | ||
* Fetches data from the installationStore for non Org Installations. | ||
*/ | ||
authorize(source: InstallationQuery): Promise<AuthorizeResult>; | ||
/** | ||
* Fetches data from the installationStore for Org Installations. | ||
*/ | ||
orgAuthorize(source: OrgInstallationQuery): Promise<AuthorizeResult>; | ||
/** | ||
* Returns a URL that is suitable for including in an Add to Slack button | ||
@@ -63,3 +67,3 @@ * Uses stateStore to generate a value for the state query param. | ||
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; | ||
@@ -73,3 +77,5 @@ } | ||
storeInstallation: (installation: Installation, logger?: Logger) => Promise<void>; | ||
storeOrgInstallation?: (installation: OrgInstallation, logger?: Logger) => Promise<void>; | ||
fetchInstallation: (query: InstallationQuery, logger?: Logger) => Promise<Installation>; | ||
fetchOrgInstallation?: (query: OrgInstallationQuery, logger?: Logger) => Promise<OrgInstallation>; | ||
} | ||
@@ -80,3 +86,3 @@ export interface Installation { | ||
name: string; | ||
} | null; | ||
}; | ||
enterprise?: { | ||
@@ -107,2 +113,28 @@ id: string; | ||
} | ||
export interface OrgInstallation { | ||
enterprise: { | ||
id: string; | ||
name?: string; | ||
}; | ||
bot?: { | ||
token: string; | ||
scopes: string[]; | ||
id?: string; | ||
userId: string; | ||
}; | ||
user: { | ||
token?: string; | ||
scopes?: string[]; | ||
id: string; | ||
}; | ||
incomingWebhook?: { | ||
url: string; | ||
channel: string; | ||
channelId: string; | ||
configurationUrl: string; | ||
}; | ||
appId: string | undefined; | ||
tokenType?: string; | ||
isEnterpriseInstall: boolean; | ||
} | ||
export interface InstallationQuery { | ||
@@ -114,2 +146,8 @@ teamId: string; | ||
} | ||
export interface OrgInstallationQuery { | ||
enterpriseId: string; | ||
teamId?: string; | ||
userId?: string; | ||
conversationId?: string; | ||
} | ||
export interface AuthorizeResult { | ||
@@ -120,4 +158,5 @@ botToken?: string; | ||
botUserId?: string; | ||
teamId?: string; | ||
} | ||
export { Logger, LogLevel } from './logger'; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
@@ -86,6 +97,7 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
this.authorize = this.authorize.bind(this); | ||
this.orgAuthorize = this.orgAuthorize.bind(this); | ||
this.authVersion = authVersion; | ||
} | ||
/** | ||
* Fetches data from the installationStore. | ||
* Fetches data from the installationStore for non Org Installations. | ||
*/ | ||
@@ -107,2 +119,3 @@ InstallProvider.prototype.authorize = function (source) { | ||
authResult.userToken = queryResult.user.token; | ||
authResult.teamId = queryResult.team.id; | ||
if (queryResult.bot !== undefined) { | ||
@@ -123,2 +136,37 @@ authResult.botToken = queryResult.bot.token; | ||
/** | ||
* Fetches data from the installationStore for Org Installations. | ||
*/ | ||
InstallProvider.prototype.orgAuthorize = function (source) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var queryResult, authResult, error_2; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
_a.trys.push([0, 2, , 3]); | ||
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(); | ||
if (queryResult === undefined) { | ||
throw new Error('Failed fetching data from the Installation Store'); | ||
} | ||
authResult = {}; | ||
authResult.userToken = queryResult.user.token; | ||
if (queryResult.bot !== undefined) { | ||
authResult.botToken = queryResult.bot.token; | ||
authResult.botId = queryResult.bot.id; | ||
authResult.botUserId = queryResult.bot.userId; | ||
} | ||
return [2 /*return*/, authResult]; | ||
case 2: | ||
error_2 = _a.sent(); | ||
throw new errors_1.AuthorizationError(error_2.message); | ||
case 3: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Returns a URL that is suitable for including in an Add to Slack button | ||
@@ -189,7 +237,7 @@ * Uses stateStore to generate a value for the state query param. | ||
return __awaiter(this, void 0, void 0, function () { | ||
var parsedUrl, code, state, installOptions, client, resp, installation, botId, botId, error_2; | ||
var parsedUrl, code, state, installOptions, client, resp, installation, botId, botId, error_3; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
_a.trys.push([0, 10, , 11]); | ||
_a.trys.push([0, 13, , 14]); | ||
if (req.url !== undefined) { | ||
@@ -263,25 +311,35 @@ parsedUrl = url_1.parse(req.url, true); | ||
// resp obj for v2 - https://api.slack.com/methods/oauth.v2.access#response | ||
installation = { | ||
team: resp.team, | ||
appId: resp.app_id, | ||
user: { | ||
token: resp.authed_user.access_token, | ||
scopes: resp.authed_user.scope !== undefined ? resp.authed_user.scope.split(',') : undefined, | ||
id: resp.authed_user.id, | ||
}, | ||
bot: { | ||
scopes: resp.scope.split(','), | ||
token: resp.access_token, | ||
userId: resp.bot_user_id, | ||
id: botId, | ||
}, | ||
tokenType: resp.token_type, | ||
isEnterpriseInstall: resp.is_enterprise_install, | ||
}; | ||
if (resp.enterprise !== null) { | ||
installation.enterprise = { | ||
id: resp.enterprise.id, | ||
name: resp.enterprise.name, | ||
if (resp.is_enterprise_install) { | ||
// org installation | ||
installation = { | ||
enterprise: resp.enterprise, | ||
appId: resp.app_id, | ||
user: { | ||
token: resp.authed_user.access_token, | ||
scopes: resp.authed_user.scope !== undefined ? resp.authed_user.scope.split(',') : undefined, | ||
id: resp.authed_user.id, | ||
}, | ||
bot: { | ||
scopes: resp.scope.split(','), | ||
token: resp.access_token, | ||
userId: resp.bot_user_id, | ||
id: botId, | ||
}, | ||
tokenType: resp.token_type, | ||
isEnterpriseInstall: resp.is_enterprise_install, | ||
}; | ||
} | ||
else { | ||
// workspace or non org enterprise installation | ||
installation = __assign(__assign(__assign({ team: resp.team }, (resp.enterprise !== null && resp.enterprise !== undefined) ? { enterprise: resp.enterprise } : {}), { appId: resp.app_id, user: { | ||
token: resp.authed_user.access_token, | ||
scopes: resp.authed_user.scope !== undefined ? resp.authed_user.scope.split(',') : undefined, | ||
id: resp.authed_user.id, | ||
}, bot: { | ||
scopes: resp.scope.split(','), | ||
token: resp.access_token, | ||
userId: resp.bot_user_id, | ||
id: botId, | ||
}, tokenType: resp.token_type }), (resp.is_enterprise_install !== undefined) ? { isEnterpriseInstall: resp.is_enterprise_install } : {}); | ||
} | ||
_a.label = 8; | ||
@@ -297,7 +355,20 @@ case 8: | ||
} | ||
// save access code to installationStore | ||
return [4 /*yield*/, this.installationStore.storeInstallation(installation, this.logger)]; | ||
if (!installation.isEnterpriseInstall) return [3 /*break*/, 10]; | ||
if (this.installationStore.storeOrgInstallation === undefined) { | ||
throw new Error('Installation store is missing the storeOrgInstallation method'); | ||
} | ||
// save token to orgInstallationStore | ||
return [4 /*yield*/, this.installationStore.storeOrgInstallation(installation, this.logger)]; | ||
case 9: | ||
// save access code to installationStore | ||
// save token to orgInstallationStore | ||
_a.sent(); | ||
return [3 /*break*/, 12]; | ||
case 10: | ||
// save token to InstallationStore | ||
return [4 /*yield*/, this.installationStore.storeInstallation(installation, this.logger)]; | ||
case 11: | ||
// save token to InstallationStore | ||
_a.sent(); | ||
_a.label = 12; | ||
case 12: | ||
if (options !== undefined && options.success !== undefined) { | ||
@@ -311,16 +382,16 @@ this.logger.debug('calling passed in options.success'); | ||
} | ||
return [3 /*break*/, 11]; | ||
case 10: | ||
error_2 = _a.sent(); | ||
this.logger.error(error_2); | ||
return [3 /*break*/, 14]; | ||
case 13: | ||
error_3 = _a.sent(); | ||
this.logger.error(error_3); | ||
if (options !== undefined && options.failure !== undefined) { | ||
this.logger.debug('calling passed in options.failure'); | ||
options.failure(error_2, installOptions, req, res); | ||
options.failure(error_3, installOptions, req, res); | ||
} | ||
else { | ||
this.logger.debug('run built-in failure function'); | ||
callbackFailure(error_2, installOptions, req, res); | ||
callbackFailure(error_3, installOptions, req, res); | ||
} | ||
return [3 /*break*/, 11]; | ||
case 11: return [2 /*return*/]; | ||
return [3 /*break*/, 14]; | ||
case 14: return [2 /*return*/]; | ||
} | ||
@@ -371,6 +442,3 @@ }); | ||
// db write | ||
if (installation.isEnterpriseInstall && installation.enterprise !== undefined) { | ||
this.devDB[installation.enterprise.id] = installation; | ||
} | ||
else if (installation.team !== null && installation.team.id !== undefined) { | ||
if (isNotOrgInstall(installation)) { | ||
this.devDB[installation.team.id] = installation; | ||
@@ -385,5 +453,22 @@ } | ||
}; | ||
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!'); | ||
} | ||
// db write | ||
if (installation.isEnterpriseInstall) { | ||
this.devDB[installation.enterprise.id] = installation; | ||
} | ||
else { | ||
throw new Error('Failed saving installation data to installationStore'); | ||
} | ||
return [2 /*return*/, Promise.resolve()]; | ||
}); | ||
}); | ||
}; | ||
// non org app lookup | ||
MemoryInstallationStore.prototype.fetchInstallation = function (query, logger) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var item; | ||
return __generator(this, function (_a) { | ||
@@ -393,12 +478,20 @@ if (logger !== undefined) { | ||
} | ||
item = undefined; | ||
// enterprise org app installation lookup | ||
if (query.teamId !== undefined) { | ||
return [2 /*return*/, this.devDB[query.teamId]]; | ||
} | ||
throw new Error('Failed fetching installation'); | ||
}); | ||
}); | ||
}; | ||
// enterprise org app installation lookup | ||
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) { | ||
item = this.devDB[query.enterpriseId]; | ||
return [2 /*return*/, this.devDB[query.enterpriseId]]; | ||
} | ||
// non org app lookup | ||
if (item === undefined && query.teamId !== undefined) { | ||
item = this.devDB[query.teamId]; | ||
} | ||
return [2 /*return*/, item]; | ||
throw new Error('Failed fetching installation'); | ||
}); | ||
@@ -412,3 +505,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 | ||
@@ -453,4 +546,8 @@ // Changes to the workspace app was installed to, to the app home | ||
} | ||
// type guard to confirm an installation isn't an OrgInstallation | ||
function isNotOrgInstall(installation) { | ||
return installation.team !== undefined && installation.team !== null; | ||
} | ||
var logger_2 = require("./logger"); | ||
exports.LogLevel = logger_2.LogLevel; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@slack/oauth", | ||
"version": "1.1.0-beta.1", | ||
"version": "1.1.0-orgAppsBeta.2", | ||
"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. | ||
@@ -166,6 +166,8 @@ ```javascript | ||
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,12 +183,8 @@ const installer = new InstallProvider({ | ||
// replace myDB.set with your own database or OEM setter | ||
if (installation.isEnterpriseInstall && installation.enterprise !== undefined) { | ||
// enterprise app, org wide installation | ||
myDB.set(installation.enterprise.id, installation); | ||
} else if (installation.team.id !== undefined) { | ||
if (installation.team.id !== undefined) { | ||
// non enterprise org app installation | ||
myDB.set(installation.team.id, installation); | ||
return myDB.set(installation.team.id, installation); | ||
} else { | ||
throw new Error('Failed saving installation data to installationStore'); | ||
} | ||
return; | ||
}, | ||
@@ -198,15 +196,24 @@ // takes in an installQuery as an argument | ||
// replace myDB.get with your own database or OEM getter | ||
let result = undefined; | ||
// enterprise org app installation lookup | ||
if (InstallQuery.enterpriseId !== undefined) { | ||
result = await myDB.get(installQuery.enterpriseId); | ||
} | ||
// non enterprise org app lookup | ||
if (result === undefined && InstallQuery.teamId !== undefined) { | ||
result = await myDB.get(installQuery.teamId); | ||
return await myDB.get(installQuery.teamId); | ||
}, | ||
// takes in an installation object as an argument | ||
// returns nothing | ||
storeOrgInstallation: (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'); | ||
} | ||
return result; | ||
}, | ||
// 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); | ||
}, | ||
}, | ||
@@ -219,3 +226,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()` | ||
@@ -225,3 +232,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'}); | ||
/* | ||
@@ -242,3 +250,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. | ||
@@ -250,2 +258,3 @@ ```javascript | ||
const result = installer.installationStore.fetchInstallation({teamId:'my-team-ID', enterpriseId:'my-enterprise-ID'}); | ||
const orgResult = installer.installationStore.fetchOrgInstallation({enterpriseId:'my-enterprise-ID'}); | ||
``` | ||
@@ -252,0 +261,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
79004
837
379