WIP: Successful Google Auth, broke pretty much everything else in the process

This commit is contained in:
Tom Moor
2018-05-28 11:36:37 -07:00
parent 1ba5c1cf96
commit ddd2b82d20
33 changed files with 443 additions and 387 deletions

86
server/auth/google.js Normal file
View File

@@ -0,0 +1,86 @@
// @flow
import Router from 'koa-router';
import addMonths from 'date-fns/add_months';
import { OAuth2Client } from 'google-auth-library';
import { User, Team } from '../models';
const router = new Router();
const client = new OAuth2Client(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
`${process.env.URL}/auth/google.callback`
);
// start the oauth process and redirect user to Google
router.get('google', async ctx => {
// Generate the url that will be used for the consent dialog.
const authorizeUrl = client.generateAuthUrl({
access_type: 'offline',
scope: [
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email',
],
prompt: 'consent',
});
ctx.redirect(authorizeUrl);
});
// signin callback from Google
router.get('google.callback', async ctx => {
const { code } = ctx.request.query;
ctx.assertPresent(code, 'code is required');
const response = await client.getToken(code);
client.setCredentials(response.tokens);
console.log('Tokens acquired.');
console.log(response.tokens);
const profile = await client.request({
url: 'https://www.googleapis.com/oauth2/v1/userinfo',
});
const teamName = profile.data.hd.split('.')[0];
const [team, isFirstUser] = await Team.findOrCreate({
where: {
slackId: profile.data.hd,
},
defaults: {
name: teamName,
avatarUrl: `https://logo.clearbit.com/${profile.data.hd}`,
},
});
const [user, isFirstSignin] = await User.findOrCreate({
where: {
slackId: profile.data.id,
teamId: team.id,
},
defaults: {
name: profile.data.name,
email: profile.data.email,
isAdmin: isFirstUser,
avatarUrl: profile.data.picture,
},
});
if (!isFirstSignin) {
await user.save();
}
if (isFirstUser) {
await team.createFirstCollection(user.id);
}
ctx.cookies.set('lastLoggedIn', 'google', {
httpOnly: false,
expires: new Date('2100'),
});
ctx.cookies.set('accessToken', user.getJwtToken(), {
httpOnly: false,
expires: addMonths(new Date(), 6),
});
ctx.redirect('/');
});
export default router;

20
server/auth/index.js Normal file
View File

@@ -0,0 +1,20 @@
// @flow
import bodyParser from 'koa-bodyparser';
import Koa from 'koa';
import Router from 'koa-router';
import validation from '../middlewares/validation';
import slack from './slack';
import google from './google';
const auth = new Koa();
const router = new Router();
router.use('/', slack.routes());
router.use('/', google.routes());
auth.use(bodyParser());
auth.use(validation());
auth.use(router.routes());
export default auth;

145
server/auth/slack.js Normal file
View File

@@ -0,0 +1,145 @@
// @flow
import Router from 'koa-router';
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 => {
const state = Math.random()
.toString(36)
.substring(7);
ctx.cookies.set('state', state, {
httpOnly: false,
expires: new Date('2100'),
});
ctx.redirect(slackAuth(state));
});
router.post('auth.slack', async ctx => {
const { code } = ctx.body;
ctx.assertPresent(code, 'code is required');
const data = await Slack.oauthAccess(code);
let user = await User.findOne({ where: { slackId: 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,
slackId: data.team.id,
slackData: data.team,
});
}
if (user) {
user.slackAccessToken = data.access_token;
user.slackData = data.user;
await user.save();
} else {
user = await User.create({
slackId: data.user.id,
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();
}
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', {
httpOnly: false,
expires: new Date('2100'),
});
ctx.body = {
data: {
user: await presentUser(ctx, user),
team: await presentTeam(ctx, team),
accessToken: user.getJwtToken(),
},
};
});
router.post('auth.slackCommands', 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 data = await Slack.oauthAccess(code, endpoint);
const serviceId = 'slack';
const authentication = await Authentication.create({
serviceId,
userId: user.id,
teamId: user.teamId,
token: data.access_token,
scopes: data.scope.split(','),
});
await Integration.create({
serviceId,
type: 'command',
userId: user.id,
teamId: user.teamId,
authenticationId: authentication.id,
});
});
router.post('auth.slackPost', 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 data = await Slack.oauthAccess(code, endpoint);
const serviceId = 'slack';
const authentication = await Authentication.create({
serviceId,
userId: user.id,
teamId: user.teamId,
token: data.access_token,
scopes: data.scope.split(','),
});
await Integration.create({
serviceId,
type: 'post',
userId: user.id,
teamId: user.teamId,
authenticationId: authentication.id,
collectionId,
events: [],
settings: {
url: data.incoming_webhook.url,
channel: data.incoming_webhook.channel,
channelId: data.incoming_webhook.channel_id,
},
});
});
export default router;