diff --git a/app/index.js b/app/index.js index f90af9838..25f0bd6ac 100644 --- a/app/index.js +++ b/app/index.js @@ -25,8 +25,6 @@ import Members from 'scenes/Settings/Members'; import Slack from 'scenes/Settings/Slack'; import Shares from 'scenes/Settings/Shares'; import Tokens from 'scenes/Settings/Tokens'; -import SlackAuth from 'scenes/SlackAuth'; -import ErrorAuth from 'scenes/ErrorAuth'; import Error404 from 'scenes/Error404'; import ErrorBoundary from 'components/ErrorBoundary'; @@ -61,14 +59,6 @@ if (element) { - - - - diff --git a/app/scenes/ErrorAuth/ErrorAuth.js b/app/scenes/ErrorAuth/ErrorAuth.js deleted file mode 100644 index 97e19f2e5..000000000 --- a/app/scenes/ErrorAuth/ErrorAuth.js +++ /dev/null @@ -1,23 +0,0 @@ -// @flow -import * as React from 'react'; -import { Link } from 'react-router-dom'; - -import CenteredContent from 'components/CenteredContent'; -import PageTitle from 'components/PageTitle'; - -class ErrorAuth extends React.Component<*> { - render() { - return ( - - -

Authentication failed

- -

- We were unable to log you in. Please try again. -

-
- ); - } -} - -export default ErrorAuth; diff --git a/app/scenes/ErrorAuth/index.js b/app/scenes/ErrorAuth/index.js deleted file mode 100644 index 0e961642b..000000000 --- a/app/scenes/ErrorAuth/index.js +++ /dev/null @@ -1,3 +0,0 @@ -// @flow -import ErrorAuth from './ErrorAuth'; -export default ErrorAuth; diff --git a/server/auth/slack.js b/server/auth/slack.js index eb3eb58fc..ac1eb89fa 100644 --- a/server/auth/slack.js +++ b/server/auth/slack.js @@ -1,97 +1,86 @@ // @flow 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 { presentUser, presentTeam } from '../presenters'; import { Authentication, Integration, User, Team } from '../models'; import * as Slack from '../slack'; const router = new Router(); -router.get('auth.slack', async ctx => { +// start the oauth process and redirect user to Slack +router.get('slack', async ctx => { const state = Math.random() .toString(36) .substring(7); ctx.cookies.set('state', state, { httpOnly: false, - expires: new Date('2100'), + expires: addHours(new Date(), 1), }); ctx.redirect(slackAuth(state)); }); -router.post('auth.slack', async ctx => { - const { code } = ctx.body; - ctx.assertPresent(code, 'code is required'); +// signin callback from Slack +router.get('slack.callback', async ctx => { + const { code, error, state } = ctx.request.query; + ctx.assertPresent(code || error, 'code is required'); + ctx.assertPresent(state, 'state is required'); + + if (state !== ctx.cookies.get('state') || error) { + ctx.redirect('/?notice=auth-error'); + return; + } const data = await Slack.oauthAccess(code); - let user = await User.findOne({ - where: { service: 'slack', serviceId: data.user.id }, - }); - let team = await Team.findOne({ where: { slackId: data.team.id } }); - const isFirstUser = !team; - - if (team) { - team.name = data.team.name; - team.slackData = data.team; - await team.save(); - } else { - team = await Team.create({ - name: data.team.name, + const [team, isFirstUser] = await Team.findOrCreate({ + where: { slackId: data.team.id, - slackData: data.team, - }); - } + }, + defaults: { + name: data.team.name, + avatarUrl: data.team.image_88, + }, + }); - if (user) { - user.slackAccessToken = data.access_token; - user.slackData = data.user; - await user.save(); - } else { - user = await User.create({ + const [user] = await User.findOrCreate({ + where: { service: 'slack', serviceId: data.user.id, + teamId: team.id, + }, + defaults: { name: data.user.name, email: data.user.email, - teamId: team.id, isAdmin: isFirstUser, - slackData: data.user, - slackAccessToken: data.access_token, - }); - - // Set initial avatar - await user.updateAvatar(); - await user.save(); - } + avatarUrl: data.user.image_192, + }, + }); if (isFirstUser) { await team.createFirstCollection(user.id); } - // Signal to backend that the user is logged in. - // This is only used to signal SSR rendering, not - // used for auth. - ctx.cookies.set('loggedIn', 'true', { + ctx.cookies.set('lastSignedIn', 'slack', { httpOnly: false, expires: new Date('2100'), }); + ctx.cookies.set('accessToken', user.getJwtToken(), { + httpOnly: false, + expires: addMonths(new Date(), 1), + }); - ctx.body = { - data: { - user: await presentUser(ctx, user), - team: await presentTeam(ctx, team), - accessToken: user.getJwtToken(), - }, - }; + ctx.redirect('/'); }); -router.post('auth.slackCommands', auth(), async ctx => { +router.post('slack.commands', auth(), async ctx => { const { code } = ctx.body; ctx.assertPresent(code, 'code is required'); const user = ctx.state.user; - const endpoint = `${process.env.URL || ''}/auth/slack/commands`; + const endpoint = `${process.env.URL || ''}/auth/slack.commands`; const data = await Slack.oauthAccess(code, endpoint); const serviceId = 'slack'; @@ -112,12 +101,12 @@ router.post('auth.slackCommands', auth(), async ctx => { }); }); -router.post('auth.slackPost', auth(), async ctx => { +router.post('slack.post', auth(), async ctx => { const { code, collectionId } = ctx.body; ctx.assertPresent(code, 'code is required'); const user = ctx.state.user; - const endpoint = `${process.env.URL || ''}/auth/slack/post`; + const endpoint = `${process.env.URL || ''}/auth/slack.post`; const data = await Slack.oauthAccess(code, endpoint); const serviceId = 'slack'; diff --git a/server/pages/Home.js b/server/pages/Home.js index 43ff10962..43511c561 100644 --- a/server/pages/Home.js +++ b/server/pages/Home.js @@ -10,7 +10,7 @@ import { developers, githubUrl } from '../../shared/utils/routeHelpers'; import { color } from '../../shared/styles/constants'; type Props = { - notice?: 'google-hd', + notice?: 'google-hd' | 'auth-error', lastSignedIn: string, googleSigninEnabled: boolean, slackSigninEnabled: boolean, @@ -38,6 +38,12 @@ function Home(props: Props) { try signing in with your company Google account. )} + {props.notice === 'auth-error' && ( + + Authentication failed - we were unable to sign you in at this + time. Please try again. + + )} diff --git a/server/slack.js b/server/slack.js index 2d06d26aa..d33ebf485 100644 --- a/server/slack.js +++ b/server/slack.js @@ -44,7 +44,7 @@ export async function request(endpoint: string, body: Object) { export async function oauthAccess( code: string, - redirect_uri: string = `${process.env.URL || ''}/auth/slack` + redirect_uri: string = `${process.env.URL || ''}/auth/slack.callback` ) { return request('oauth.access', { client_id: process.env.SLACK_KEY, diff --git a/shared/utils/routeHelpers.js b/shared/utils/routeHelpers.js index a9c7772a9..ff05000de 100644 --- a/shared/utils/routeHelpers.js +++ b/shared/utils/routeHelpers.js @@ -8,7 +8,7 @@ export function slackAuth( 'identity.avatar', 'identity.team', ], - redirectUri: string = `${process.env.URL}/auth/slack` + redirectUri: string = `${process.env.URL}/auth/slack.callback` ): string { const baseUrl = 'https://slack.com/oauth/authorize'; const params = {