feat(slack): use oauth2 authentication flow

This commit is contained in:
Ali BARIN
2022-11-02 20:24:25 +01:00
parent e740d4fe25
commit f057501611
4 changed files with 241 additions and 25 deletions

View File

@@ -0,0 +1,61 @@
import { IField, IGlobalVariable } from '@automatisch/types';
import qs from 'qs';
export default async function createAuthData($: IGlobalVariable) {
const scopes = [
'channels:manage',
'channels:read',
'channels:join',
'chat:write',
'chat:write.customize',
'chat:write.public',
'files:write',
'im:write',
'mpim:write',
'team:read',
'users.profile:read',
'users:read',
'workflow.steps:execute',
'users:read.email',
'commands',
];
const userScopes = [
'channels:history',
'channels:read',
'channels:write',
'chat:write',
'emoji:read',
'files:read',
'files:write',
'groups:history',
'groups:read',
'groups:write',
'im:write',
'mpim:write',
'reactions:read',
'reminders:write',
'search:read',
'stars:read',
'team:read',
// 'users.profile:read',
'users.profile:write',
'users:read',
'users:read.email',
];
const oauthRedirectUrlField = $.app.auth.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const searchParams = qs.stringify({
client_id: $.auth.data.consumerKey as string,
redirect_uri: redirectUri,
scope: scopes.join(','),
user_scope: userScopes.join(','),
});
const url = `${$.app.baseUrl}/oauth/v2/authorize?${searchParams}`;
await $.auth.set({
url,
});
};

View File

@@ -1,17 +1,41 @@
import createAuthData from './create-auth-data';
import verifyCredentials from './verify-credentials'; import verifyCredentials from './verify-credentials';
import isStillVerified from './is-still-verified'; import isStillVerified from './is-still-verified';
export default { export default {
fields: [ fields: [
{ {
key: 'accessToken', key: 'oAuthRedirectUrl',
label: 'Access Token', label: 'OAuth Redirect URL',
type: 'string' as const,
required: true,
readOnly: true,
value: '{WEB_APP_URL}/app/slack/connections/add',
placeholder: null,
description:
'When asked to input an OAuth callback or redirect URL in Slack OAuth, enter the URL above.',
clickToCopy: true,
},
{
key: 'consumerKey',
label: 'API Key',
type: 'string' as const, type: 'string' as const,
required: true, required: true,
readOnly: false, readOnly: false,
value: null, value: null,
placeholder: null, placeholder: null,
description: 'Access token of slack that Automatisch will connect to.', description: null,
clickToCopy: false,
},
{
key: 'consumerSecret',
label: 'API Secret',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: null,
clickToCopy: false, clickToCopy: false,
}, },
], ],
@@ -30,8 +54,12 @@ export default {
value: null, value: null,
properties: [ properties: [
{ {
name: 'accessToken', name: 'consumerKey',
value: '{fields.accessToken}', value: '{fields.consumerKey}',
},
{
name: 'consumerSecret',
value: '{fields.consumerSecret}',
}, },
], ],
}, },
@@ -40,6 +68,53 @@ export default {
{ {
step: 2, step: 2,
type: 'mutation' as const, type: 'mutation' as const,
name: 'createAuthData',
arguments: [
{
name: 'id',
value: '{createConnection.id}',
},
],
},
{
step: 3,
type: 'openWithPopup' as const,
name: 'openAuthPopup',
arguments: [
{
name: 'url',
value: '{createAuthData.url}',
},
],
},
{
step: 4,
type: 'mutation' as const,
name: 'updateConnection',
arguments: [
{
name: 'id',
value: '{createConnection.id}',
},
{
name: 'formattedData',
value: null,
properties: [
{
name: 'code',
value: '{openAuthPopup.code}',
},
{
name: 'state',
value: '{openAuthPopup.state}',
},
],
},
],
},
{
step: 5,
type: 'mutation' as const,
name: 'verifyConnection', name: 'verifyConnection',
arguments: [ arguments: [
{ {
@@ -75,8 +150,12 @@ export default {
value: null, value: null,
properties: [ properties: [
{ {
name: 'accessToken', name: 'consumerKey',
value: '{fields.accessToken}', value: '{fields.consumerKey}',
},
{
name: 'consumerSecret',
value: '{fields.consumerSecret}',
}, },
], ],
}, },
@@ -85,6 +164,53 @@ export default {
{ {
step: 3, step: 3,
type: 'mutation' as const, type: 'mutation' as const,
name: 'createAuthData',
arguments: [
{
name: 'id',
value: '{connection.id}',
},
],
},
{
step: 4,
type: 'openWithPopup' as const,
name: 'openAuthPopup',
arguments: [
{
name: 'url',
value: '{createAuthData.url}',
},
],
},
{
step: 5,
type: 'mutation' as const,
name: 'updateConnection',
arguments: [
{
name: 'id',
value: '{connection.id}',
},
{
name: 'formattedData',
value: null,
properties: [
{
name: 'code',
value: '{openAuthPopup.code}',
},
{
name: 'state',
value: '{openAuthPopup.state}',
},
],
},
],
},
{
step: 6,
type: 'mutation' as const,
name: 'verifyConnection', name: 'verifyConnection',
arguments: [ arguments: [
{ {
@@ -95,6 +221,7 @@ export default {
}, },
], ],
createAuthData,
verifyCredentials, verifyCredentials,
isStillVerified, isStillVerified,
}; };

View File

@@ -1,34 +1,51 @@
import qs from 'qs';
import { IGlobalVariable } from '@automatisch/types'; import { IGlobalVariable } from '@automatisch/types';
import getCurrentUser from '../common/get-current-user';
const verifyCredentials = async ($: IGlobalVariable) => { const verifyCredentials = async ($: IGlobalVariable) => {
const headers = { const oauthRedirectUrlField = $.app.auth.fields.find(
'Content-Type': 'application/x-www-form-urlencoded', (field) => field.key == 'oAuthRedirectUrl'
);
const redirectUri = oauthRedirectUrlField.value as string;
const params = {
code: $.auth.data.code,
client_id: $.auth.data.consumerKey,
client_secret: $.auth.data.consumerSecret,
redirect_uri: redirectUri,
}; };
const response = await $.http.post('/oauth.v2.access', null, { params });
const stringifiedBody = qs.stringify({
token: $.auth.data.accessToken,
});
const response = await $.http.post('/auth.test', stringifiedBody, {
headers,
});
if (response.data.ok === false) { if (response.data.ok === false) {
throw new Error( throw new Error(
`Error occured while verifying credentials: ${response.data.error}.(More info: https://api.slack.com/methods/auth.test#errors)` `Error occured while verifying credentials: ${response.data.error}. (More info: https://api.slack.com/methods/oauth.v2.access#errors)`
); );
} }
const { bot_id: botId, user: screenName } = response.data; const {
bot_user_id: botId,
authed_user: {
id: userId,
access_token: userAccessToken,
},
access_token: botAccessToken,
team: {
name: teamName,
}
} = response.data;
$.auth.set({ await $.auth.set({
botId, botId,
screenName, userId,
userAccessToken,
botAccessToken,
screenName: teamName,
token: $.auth.data.accessToken, token: $.auth.data.accessToken,
}); });
return response.data; const currentUser = await getCurrentUser($);
await $.auth.set({
screenName: `${currentUser.real_name} @ ${teamName}`
});
}; };
export default verifyCredentials; export default verifyCredentials;

View File

@@ -1,10 +1,21 @@
import { TBeforeRequest } from '@automatisch/types'; import { TBeforeRequest } from '@automatisch/types';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => { const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
if (requestConfig.headers && $.auth.data?.accessToken) { const authData = $.auth.data;
requestConfig.headers.Authorization = `Bearer ${$.auth.data.accessToken}`; if (
requestConfig.headers
&& authData?.userAccessToken
&& authData?.botAccessToken
) {
if (requestConfig.additionalProperties?.sendAsBot) {
requestConfig.headers.Authorization = `Bearer ${authData.botAccessToken}`;
} else {
requestConfig.headers.Authorization = `Bearer ${authData.userAccessToken}`;
}
} }
requestConfig.headers['Content-Type'] = requestConfig.headers['Content-Type'] || 'application/json; charset=utf-8';
return requestConfig; return requestConfig;
}; };