Slack commands and post working agagain with new flow
This commit is contained in:
@@ -47,7 +47,7 @@ class Slack extends React.Component<Props> {
|
||||
) : (
|
||||
<SlackButton
|
||||
scopes={['commands', 'links:read', 'links:write']}
|
||||
redirectUri={`${BASE_URL}/auth/slack/commands`}
|
||||
redirectUri={`${BASE_URL}/auth/slack.commands`}
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
@@ -83,7 +83,7 @@ class Slack extends React.Component<Props> {
|
||||
<strong>{collection.name}</strong>
|
||||
<SlackButton
|
||||
scopes={['incoming-webhook']}
|
||||
redirectUri={`${BASE_URL}/auth/slack/post`}
|
||||
redirectUri={`${BASE_URL}/auth/slack.post`}
|
||||
state={collection.id}
|
||||
label="Connect"
|
||||
/>
|
||||
|
||||
@@ -17,11 +17,7 @@ type Props = {
|
||||
|
||||
function SlackButton({ auth, state, label, scopes, redirectUri }: Props) {
|
||||
const handleClick = () =>
|
||||
(window.location.href = slackAuth(
|
||||
state ? auth.saveOauthState(state) : auth.genOauthState(),
|
||||
scopes,
|
||||
redirectUri
|
||||
));
|
||||
(window.location.href = slackAuth(state, scopes, redirectUri));
|
||||
|
||||
return (
|
||||
<Button onClick={handleClick} icon={<SpacedSlackLogo size={24} />} neutral>
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import type { Location } from 'react-router-dom';
|
||||
import queryString from 'query-string';
|
||||
import { observable } from 'mobx';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import { client } from 'utils/ApiClient';
|
||||
import { slackAuth } from 'shared/utils/routeHelpers';
|
||||
|
||||
import AuthStore from 'stores/AuthStore';
|
||||
|
||||
type Props = {
|
||||
auth: AuthStore,
|
||||
location: Location,
|
||||
};
|
||||
|
||||
@observer
|
||||
class SlackAuth extends React.Component<Props> {
|
||||
@observable redirectTo: string;
|
||||
|
||||
componentDidMount() {
|
||||
this.redirect();
|
||||
}
|
||||
|
||||
async redirect() {
|
||||
const { error, code, state } = queryString.parse(
|
||||
this.props.location.search
|
||||
);
|
||||
|
||||
if (error) {
|
||||
if (error === 'access_denied') {
|
||||
// User selected "Deny" access on Slack OAuth
|
||||
this.redirectTo = '/dashboard';
|
||||
} else {
|
||||
this.redirectTo = '/auth/error';
|
||||
}
|
||||
} else if (code) {
|
||||
if (this.props.location.pathname === '/auth/slack/commands') {
|
||||
// incoming webhooks from Slack
|
||||
try {
|
||||
await client.post('/auth.slackCommands', { code });
|
||||
this.redirectTo = '/settings/integrations/slack';
|
||||
} catch (e) {
|
||||
this.redirectTo = '/auth/error';
|
||||
}
|
||||
} else if (this.props.location.pathname === '/auth/slack/post') {
|
||||
// outgoing webhooks to Slack
|
||||
try {
|
||||
await client.post('/auth.slackPost', {
|
||||
code,
|
||||
collectionId: this.props.auth.oauthState,
|
||||
});
|
||||
this.redirectTo = '/settings/integrations/slack';
|
||||
} catch (e) {
|
||||
this.redirectTo = '/auth/error';
|
||||
}
|
||||
} else {
|
||||
// Slack authentication
|
||||
const redirectTo = sessionStorage.getItem('redirectTo');
|
||||
sessionStorage.removeItem('redirectTo');
|
||||
|
||||
const { success } = await this.props.auth.authWithSlack(code, state);
|
||||
success
|
||||
? (this.redirectTo = redirectTo || '/dashboard')
|
||||
: (this.redirectTo = '/auth/error');
|
||||
}
|
||||
} else {
|
||||
// signing in
|
||||
window.location.href = slackAuth(this.props.auth.genOauthState());
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('auth')(SlackAuth);
|
||||
@@ -1,3 +0,0 @@
|
||||
// @flow
|
||||
import SlackAuth from './SlackAuth';
|
||||
export default SlackAuth;
|
||||
@@ -12,7 +12,6 @@ class AuthStore {
|
||||
@observable user: ?User;
|
||||
@observable team: ?Team;
|
||||
@observable token: ?string;
|
||||
@observable oauthState: string;
|
||||
@observable isLoading: boolean = false;
|
||||
@observable isSuspended: boolean = false;
|
||||
@observable suspendedContactEmail: ?string;
|
||||
@@ -29,7 +28,6 @@ class AuthStore {
|
||||
return JSON.stringify({
|
||||
user: this.user,
|
||||
team: this.team,
|
||||
oauthState: this.oauthState,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,56 +61,6 @@ class AuthStore {
|
||||
window.location.href = `${BASE_URL}?done=${new Date().getTime()}`;
|
||||
};
|
||||
|
||||
@action
|
||||
genOauthState = () => {
|
||||
const state = Math.random()
|
||||
.toString(36)
|
||||
.substring(7);
|
||||
this.oauthState = state;
|
||||
return this.oauthState;
|
||||
};
|
||||
|
||||
@action
|
||||
saveOauthState = (state: string) => {
|
||||
this.oauthState = state;
|
||||
return this.oauthState;
|
||||
};
|
||||
|
||||
@action
|
||||
authWithSlack = async (code: string, state: string) => {
|
||||
// in the case of direct install from the Slack app store the state is
|
||||
// created on the server and set as a cookie
|
||||
const serverState = Cookie.get('state');
|
||||
if (state !== this.oauthState && state !== serverState) {
|
||||
return {
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
let res;
|
||||
try {
|
||||
res = await client.post('/auth.slack', { code });
|
||||
} catch (e) {
|
||||
return {
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
|
||||
// State can only ever be used once so now's the time to remove it.
|
||||
Cookie.remove('state', { path: '/' });
|
||||
|
||||
invariant(
|
||||
res && res.data && res.data.user && res.data.team && res.data.accessToken,
|
||||
'All values should be available'
|
||||
);
|
||||
this.user = res.data.user;
|
||||
this.team = res.data.team;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
};
|
||||
|
||||
constructor() {
|
||||
// Rehydrate
|
||||
let data = {};
|
||||
@@ -123,7 +71,6 @@ class AuthStore {
|
||||
}
|
||||
this.user = data.user;
|
||||
this.team = data.team;
|
||||
this.oauthState = data.oauthState;
|
||||
|
||||
// load token from state for backwards compatability with
|
||||
// sessions created pre-google auth
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import Router from 'koa-router';
|
||||
import addHours from 'date-fns/add_hours';
|
||||
import addMonths from 'date-fns/add_months';
|
||||
import auth from '../middlewares/authentication';
|
||||
import { slackAuth } from '../../shared/utils/routeHelpers';
|
||||
import { Authentication, Integration, User, Team } from '../models';
|
||||
import * as Slack from '../slack';
|
||||
@@ -75,17 +74,19 @@ router.get('slack.callback', async ctx => {
|
||||
ctx.redirect('/');
|
||||
});
|
||||
|
||||
router.post('slack.commands', auth(), async ctx => {
|
||||
const { code } = ctx.body;
|
||||
router.get('slack.commands', async ctx => {
|
||||
const { code } = ctx.request.query;
|
||||
ctx.assertPresent(code, 'code is required');
|
||||
|
||||
const user = ctx.state.user;
|
||||
const endpoint = `${process.env.URL || ''}/auth/slack.commands`;
|
||||
const data = await Slack.oauthAccess(code, endpoint);
|
||||
const serviceId = 'slack';
|
||||
const user = await User.find({
|
||||
service: 'slack',
|
||||
serviceId: data.user_id,
|
||||
});
|
||||
|
||||
const authentication = await Authentication.create({
|
||||
serviceId,
|
||||
serviceId: 'slack',
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
token: data.access_token,
|
||||
@@ -93,25 +94,33 @@ router.post('slack.commands', auth(), async ctx => {
|
||||
});
|
||||
|
||||
await Integration.create({
|
||||
serviceId,
|
||||
serviceId: 'slack',
|
||||
type: 'command',
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
authenticationId: authentication.id,
|
||||
});
|
||||
|
||||
ctx.redirect('/settings/integrations/slack');
|
||||
});
|
||||
|
||||
router.post('slack.post', auth(), async ctx => {
|
||||
const { code, collectionId } = ctx.body;
|
||||
router.get('slack.post', async ctx => {
|
||||
const { code, state } = ctx.request.query;
|
||||
ctx.assertPresent(code, 'code is required');
|
||||
|
||||
const user = ctx.state.user;
|
||||
const collectionId = state;
|
||||
ctx.assertUuid(collectionId, 'collectionId must be an uuid');
|
||||
|
||||
const endpoint = `${process.env.URL || ''}/auth/slack.post`;
|
||||
const data = await Slack.oauthAccess(code, endpoint);
|
||||
const serviceId = 'slack';
|
||||
|
||||
const user = await User.find({
|
||||
service: 'slack',
|
||||
serviceId: data.user_id,
|
||||
});
|
||||
|
||||
const authentication = await Authentication.create({
|
||||
serviceId,
|
||||
serviceId: 'slack',
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
token: data.access_token,
|
||||
@@ -119,7 +128,7 @@ router.post('slack.post', auth(), async ctx => {
|
||||
});
|
||||
|
||||
await Integration.create({
|
||||
serviceId,
|
||||
serviceId: 'slack',
|
||||
type: 'post',
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
@@ -132,6 +141,8 @@ router.post('slack.post', auth(), async ctx => {
|
||||
channelId: data.incoming_webhook.channel_id,
|
||||
},
|
||||
});
|
||||
|
||||
ctx.redirect('/settings/integrations/slack');
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user