New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@slack/oauth

Package Overview
Dependencies
Maintainers
15
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@slack/oauth - npm Package Compare versions

Comparing version 1.1.0 to 1.2.0-orgAppsBeta.3

53

dist/index.d.ts
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from 'http';
import { WebClientOptions } from '@slack/web-api';
import { CodedError } from './errors';

@@ -23,8 +24,14 @@ import { Logger, LogLevel } from './logger';

private logger;
constructor({ clientId, clientSecret, stateSecret, stateStore, installationStore, authVersion, logger, logLevel, }: InstallProviderOptions);
private clientOptions;
private authorizationUrl;
constructor({ clientId, clientSecret, stateSecret, stateStore, installationStore, authVersion, logger, logLevel, clientOptions, authorizationUrl, }: InstallProviderOptions);
/**
* 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

@@ -45,3 +52,3 @@ * Uses stateStore to generate a value for the state query param.

}
interface InstallProviderOptions {
export interface InstallProviderOptions {
clientId: string;

@@ -55,2 +62,4 @@ clientSecret: string;

logLevel?: LogLevel;
clientOptions?: Omit<WebClientOptions, 'logLevel' | 'logger'>;
authorizationUrl?: string;
}

@@ -65,3 +74,3 @@ export interface InstallURLOptions {

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;

@@ -75,3 +84,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>;
}

@@ -87,2 +98,3 @@ export interface Installation {

};
isEnterpriseInstall?: boolean;
bot?: {

@@ -108,2 +120,28 @@ token: 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 {

@@ -115,2 +153,8 @@ teamId: string;

}
export interface OrgInstallationQuery {
enterpriseId: string;
teamId?: string;
userId?: string;
conversationId?: string;
}
export interface AuthorizeResult {

@@ -121,4 +165,5 @@ botToken?: string;

botUserId?: string;
teamId?: string;
}
export { Logger, LogLevel } from './logger';
//# sourceMappingURL=index.d.ts.map

219

dist/index.js
"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) {

@@ -57,3 +68,3 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }

function InstallProvider(_a) {
var clientId = _a.clientId, clientSecret = _a.clientSecret, _b = _a.stateSecret, stateSecret = _b === void 0 ? undefined : _b, _c = _a.stateStore, stateStore = _c === void 0 ? undefined : _c, _d = _a.installationStore, installationStore = _d === void 0 ? new MemoryInstallationStore() : _d, _e = _a.authVersion, authVersion = _e === void 0 ? 'v2' : _e, _f = _a.logger, logger = _f === void 0 ? undefined : _f, _g = _a.logLevel, logLevel = _g === void 0 ? logger_1.LogLevel.INFO : _g;
var clientId = _a.clientId, clientSecret = _a.clientSecret, _b = _a.stateSecret, stateSecret = _b === void 0 ? undefined : _b, _c = _a.stateStore, stateStore = _c === void 0 ? undefined : _c, _d = _a.installationStore, installationStore = _d === void 0 ? new MemoryInstallationStore() : _d, _e = _a.authVersion, authVersion = _e === void 0 ? 'v2' : _e, _f = _a.logger, logger = _f === void 0 ? undefined : _f, _g = _a.logLevel, logLevel = _g === void 0 ? logger_1.LogLevel.INFO : _g, _h = _a.clientOptions, clientOptions = _h === void 0 ? {} : _h, _j = _a.authorizationUrl, authorizationUrl = _j === void 0 ? 'https://slack.com/oauth/v2/authorize' : _j;
if (clientId === undefined || clientSecret === undefined) {

@@ -87,6 +98,15 @@ throw new errors_1.InstallerInitializationError('You must provide a valid clientId and clientSecret');

this.authorize = this.authorize.bind(this);
this.orgAuthorize = this.orgAuthorize.bind(this);
this.authVersion = authVersion;
this.authorizationUrl = authorizationUrl;
if (authorizationUrl !== 'https://slack.com/oauth/v2/authorize' && authVersion === 'v1') {
this.logger.info('You provided both an authorizationUrl and an authVersion! The authVersion will be ignored in favor of the authorizationUrl.');
}
else if (authVersion === 'v1') {
this.authorizationUrl = 'https://slack.com/oauth/authorize';
}
this.clientOptions = __assign({ logLevel: this.logger.getLevel() }, clientOptions);
}
/**
* Fetches data from the installationStore.
* Fetches data from the installationStore for non Org Installations.
*/

@@ -108,2 +128,3 @@ InstallProvider.prototype.authorize = function (source) {

authResult.userToken = queryResult.user.token;
authResult.teamId = queryResult.team.id;
if (queryResult.bot !== undefined) {

@@ -124,2 +145,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

@@ -134,6 +190,3 @@ * Uses stateStore to generate a value for the state query param.

case 0:
slackURL = new url_1.URL('https://slack.com/oauth/v2/authorize');
if (this.authVersion === 'v1') {
slackURL.href = 'https://slack.com/oauth/authorize';
}
slackURL = new url_1.URL(this.authorizationUrl);
if (options.scopes === undefined) {

@@ -191,7 +244,7 @@ throw new errors_1.GenerateInstallUrlError('You must provide a scope parameter when calling generateInstallUrl');

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) {

@@ -211,3 +264,3 @@ parsedUrl = url_1.parse(req.url, true);

installOptions = _a.sent();
client = new web_api_1.WebClient();
client = new web_api_1.WebClient('', this.clientOptions);
resp = void 0;

@@ -236,3 +289,3 @@ installation = void 0;

if (!(resp.bot !== undefined)) return [3 /*break*/, 4];
return [4 /*yield*/, getBotId(resp.bot.bot_access_token)];
return [4 /*yield*/, getBotId(resp.bot.bot_access_token, this.clientOptions)];
case 3:

@@ -263,28 +316,39 @@ botId = _a.sent();

resp = (_a.sent());
return [4 /*yield*/, getBotId(resp.access_token)];
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,
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,
};
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;

@@ -300,7 +364,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) {

@@ -314,16 +391,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*/];
}

@@ -374,10 +451,32 @@ });

// db write
this.devDB[installation.team.id] = installation;
return [2 /*return*/];
if (isNotOrgInstall(installation)) {
this.devDB[installation.team.id] = installation;
}
else {
throw new Error('Failed saving installation data to installationStore');
}
return [2 /*return*/, Promise.resolve()];
});
});
};
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) {

@@ -387,7 +486,23 @@ 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');
});
});
};
// 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) {
return [2 /*return*/, this.devDB[query.enterpriseId]];
}
throw new Error('Failed fetching installation');
});
});
};
return MemoryInstallationStore;

@@ -398,3 +513,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

@@ -419,3 +534,3 @@ // Changes to the workspace app was installed to, to the app home

// Gets the bot_id using the `auth.test` method.
function getBotId(token) {
function getBotId(token, clientOptions) {
return __awaiter(this, void 0, void 0, function () {

@@ -426,3 +541,3 @@ var client, authResult;

case 0:
client = new web_api_1.WebClient(token);
client = new web_api_1.WebClient(token, clientOptions);
return [4 /*yield*/, client.auth.test()];

@@ -441,4 +556,8 @@ case 1:

}
// 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",
"version": "1.2.0-orgAppsBeta.3",
"description": "Official library for interacting with Slack's Oauth endpoints",

@@ -35,3 +35,5 @@ "author": "Slack Technologies, Inc.",

"test": "nyc mocha --config .mocharc.json src/*.spec.js",
"coverage": "codecov -F oauthhelper --root=$PWD"
"coverage": "codecov -F oauthhelper --root=$PWD",
"ref-docs:model": "api-extractor run",
"watch": "npx nodemon --watch 'src' --ext 'ts' --exec npm run build"
},

@@ -48,2 +50,3 @@ "dependencies": {

"@types/chai": "^4.2.11",
"@microsoft/api-extractor": "^7.3.4",
"chai": "^4.2.0",

@@ -50,0 +53,0 @@ "codecov": "^3.0.4",

@@ -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,4 +183,8 @@ const installer = new InstallProvider({

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

@@ -188,6 +194,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: (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);
},
},

@@ -200,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()`

@@ -206,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'});
/*

@@ -223,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.

@@ -230,3 +257,4 @@ ```javascript

// returns an installation object
const result = installer.installationStore.fetchInstallation({teamId:'my-Team-ID'});
const result = installer.installationStore.fetchInstallation({teamId:'my-team-ID', enterpriseId:'my-enterprise-ID'});
const orgResult = installer.installationStore.fetchOrgInstallation({enterpriseId:'my-enterprise-ID'});
```

@@ -233,0 +261,0 @@ </details>

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc