DB migrations
Google button
This commit is contained in:
@ -16,8 +16,10 @@ let authenticatedStores;
|
||||
|
||||
const Auth = ({ children }: Props) => {
|
||||
if (stores.auth.authenticated) {
|
||||
stores.auth.fetch();
|
||||
|
||||
// TODO: Show loading state
|
||||
if (!stores.auth.team || !stores.auth.user) {
|
||||
stores.auth.fetch();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,9 @@ router.post('hooks.unfurl', async ctx => {
|
||||
throw new AuthenticationError('Invalid token');
|
||||
|
||||
// TODO: Everything from here onwards will get moved to an async job
|
||||
const user = await User.find({ where: { slackId: event.user } });
|
||||
const user = await User.find({
|
||||
where: { service: 'slack', serviceId: event.user },
|
||||
});
|
||||
if (!user) return;
|
||||
|
||||
const auth = await Authentication.find({
|
||||
@ -55,7 +57,8 @@ router.post('hooks.slack', async ctx => {
|
||||
|
||||
const user = await User.find({
|
||||
where: {
|
||||
slackId: user_id,
|
||||
service: 'slack',
|
||||
serviceId: user_id,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -32,7 +32,7 @@ describe('#hooks.unfurl', async () => {
|
||||
event: {
|
||||
type: 'link_shared',
|
||||
channel: 'Cxxxxxx',
|
||||
user: user.slackId,
|
||||
user: user.serviceId,
|
||||
message_ts: '123456789.9875',
|
||||
links: [
|
||||
{
|
||||
@ -55,7 +55,7 @@ describe('#hooks.slack', async () => {
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
body: {
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
user_id: user.slackId,
|
||||
user_id: user.serviceId,
|
||||
text: 'dsfkndfskndsfkn',
|
||||
},
|
||||
});
|
||||
@ -70,7 +70,7 @@ describe('#hooks.slack', async () => {
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
body: {
|
||||
token: process.env.SLACK_VERIFICATION_TOKEN,
|
||||
user_id: user.slackId,
|
||||
user_id: user.serviceId,
|
||||
text: document.title,
|
||||
},
|
||||
});
|
||||
@ -98,7 +98,7 @@ describe('#hooks.slack', async () => {
|
||||
const res = await server.post('/api/hooks.slack', {
|
||||
body: {
|
||||
token: 'wrong-verification-token',
|
||||
user_id: user.slackId,
|
||||
user_id: user.serviceId,
|
||||
text: 'Welcome',
|
||||
},
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
import Router from 'koa-router';
|
||||
import addMonths from 'date-fns/add_months';
|
||||
import { capitalize } from 'lodash';
|
||||
import { OAuth2Client } from 'google-auth-library';
|
||||
import { User, Team } from '../models';
|
||||
|
||||
@ -32,17 +33,14 @@ router.get('google.callback', async ctx => {
|
||||
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 teamName = capitalize(profile.data.hd.split('.')[0]);
|
||||
const [team, isFirstUser] = await Team.findOrCreate({
|
||||
where: {
|
||||
slackId: profile.data.hd,
|
||||
googleId: profile.data.hd,
|
||||
},
|
||||
defaults: {
|
||||
name: teamName,
|
||||
@ -50,9 +48,10 @@ router.get('google.callback', async ctx => {
|
||||
},
|
||||
});
|
||||
|
||||
const [user, isFirstSignin] = await User.findOrCreate({
|
||||
const [user] = await User.findOrCreate({
|
||||
where: {
|
||||
slackId: profile.data.id,
|
||||
service: 'google',
|
||||
serviceId: profile.data.id,
|
||||
teamId: team.id,
|
||||
},
|
||||
defaults: {
|
||||
@ -63,10 +62,6 @@ router.get('google.callback', async ctx => {
|
||||
},
|
||||
});
|
||||
|
||||
if (!isFirstSignin) {
|
||||
await user.save();
|
||||
}
|
||||
|
||||
if (isFirstUser) {
|
||||
await team.createFirstCollection(user.id);
|
||||
}
|
||||
@ -77,7 +72,7 @@ router.get('google.callback', async ctx => {
|
||||
});
|
||||
ctx.cookies.set('accessToken', user.getJwtToken(), {
|
||||
httpOnly: false,
|
||||
expires: addMonths(new Date(), 6),
|
||||
expires: addMonths(new Date(), 1),
|
||||
});
|
||||
|
||||
ctx.redirect('/');
|
||||
|
@ -26,7 +26,9 @@ router.post('auth.slack', async ctx => {
|
||||
|
||||
const data = await Slack.oauthAccess(code);
|
||||
|
||||
let user = await User.findOne({ where: { slackId: data.user.id } });
|
||||
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;
|
||||
|
||||
@ -48,7 +50,8 @@ router.post('auth.slack', async ctx => {
|
||||
await user.save();
|
||||
} else {
|
||||
user = await User.create({
|
||||
slackId: data.user.id,
|
||||
service: 'slack',
|
||||
serviceId: data.user.id,
|
||||
name: data.user.name,
|
||||
email: data.user.email,
|
||||
teamId: team.id,
|
||||
|
27
server/migrations/20180528233909-google-auth.js
Normal file
27
server/migrations/20180528233909-google-auth.js
Normal file
@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.addColumn('teams', 'googleId', {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true,
|
||||
unique: true
|
||||
});
|
||||
await queryInterface.addColumn('teams', 'avatarUrl', {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('users', 'service', {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: 'slack'
|
||||
});
|
||||
await queryInterface.renameColumn('users', 'slackId', 'serviceId');
|
||||
await queryInterface.addIndex('teams', ['googleId']);
|
||||
},
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.removeColumn('teams', 'googleId');
|
||||
await queryInterface.removeColumn('teams', 'avatarUrl');
|
||||
await queryInterface.removeColumn('users', 'service');
|
||||
await queryInterface.renameColumn('users', 'serviceId', 'slackId');
|
||||
await queryInterface.removeIndex('teams', ['googleId']);
|
||||
}
|
||||
}
|
@ -13,6 +13,8 @@ const Team = sequelize.define(
|
||||
},
|
||||
name: DataTypes.STRING,
|
||||
slackId: { type: DataTypes.STRING, allowNull: true },
|
||||
googleId: { type: DataTypes.STRING, allowNull: true },
|
||||
avatarUrl: { type: DataTypes.STRING, allowNull: true },
|
||||
slackData: DataTypes.JSONB,
|
||||
},
|
||||
{
|
||||
|
@ -25,7 +25,8 @@ const User = sequelize.define(
|
||||
passwordDigest: DataTypes.STRING,
|
||||
isAdmin: DataTypes.BOOLEAN,
|
||||
slackAccessToken: encryptedFields.vault('slackAccessToken'),
|
||||
slackId: { type: DataTypes.STRING, allowNull: true, unique: true },
|
||||
service: { type: DataTypes.STRING, allowNull: true, unique: true },
|
||||
serviceId: { type: DataTypes.STRING, allowNull: true, unique: true },
|
||||
slackData: DataTypes.JSONB,
|
||||
jwtSecret: encryptedFields.vault('jwtSecret'),
|
||||
suspendedAt: DataTypes.DATE,
|
||||
|
@ -3,6 +3,7 @@ import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { signin } from '../../../shared/utils/routeHelpers';
|
||||
import Flex from '../../../shared/components/Flex';
|
||||
import GoogleLogo from '../../../shared/components/GoogleLogo';
|
||||
import SlackLogo from '../../../shared/components/SlackLogo';
|
||||
import { color } from '../../../shared/styles/constants';
|
||||
|
||||
@ -10,22 +11,27 @@ type Props = {
|
||||
lastLoggedIn: string,
|
||||
};
|
||||
|
||||
const SlackSignin = ({ lastLoggedIn }: Props) => {
|
||||
const SignupButton = ({ lastLoggedIn }: Props) => {
|
||||
return (
|
||||
<Flex justify="center">
|
||||
<Flex>
|
||||
<Flex column>
|
||||
<Button href={signin('slack')}>
|
||||
<SlackLogo />
|
||||
<Spacer>Sign In with Slack</Spacer>
|
||||
</Button>
|
||||
{lastLoggedIn === 'slack' && 'You signed in with Slack previously'}
|
||||
<LastLogin>
|
||||
{lastLoggedIn === 'slack' && 'You signed in with Slack previously'}
|
||||
</LastLogin>
|
||||
</Flex>
|
||||
|
||||
<Flex>
|
||||
<Flex column>
|
||||
<Button href={signin('google')}>
|
||||
<GoogleLogo />
|
||||
<Spacer>Sign In with Google</Spacer>
|
||||
</Button>
|
||||
{lastLoggedIn === 'google' && 'You signed in with Google previously'}
|
||||
<LastLogin>
|
||||
{lastLoggedIn === 'google' && 'You signed in with Google previously'}
|
||||
</LastLogin>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
@ -43,6 +49,13 @@ const Button = styled.a`
|
||||
background: ${color.black};
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
height: 56px;
|
||||
`;
|
||||
|
||||
export default SlackSignin;
|
||||
const LastLogin = styled.p`
|
||||
font-size: 12px;
|
||||
color: ${color.slate};
|
||||
padding-top: 4px;
|
||||
`;
|
||||
|
||||
export default SignupButton;
|
||||
|
@ -40,7 +40,8 @@ export async function buildUser(overrides: Object = {}) {
|
||||
username: `user${count}`,
|
||||
name: `User ${count}`,
|
||||
password: 'test123!',
|
||||
slackId: uuid.v4(),
|
||||
service: 'slack',
|
||||
serviceId: uuid.v4(),
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
@ -30,7 +30,8 @@ const seed = async () => {
|
||||
name: 'User 1',
|
||||
password: 'test123!',
|
||||
teamId: team.id,
|
||||
slackId: 'U2399UF2P',
|
||||
service: 'slack',
|
||||
serviceId: 'U2399UF2P',
|
||||
slackData: {
|
||||
id: 'U2399UF2P',
|
||||
image_192: 'http://example.com/avatar.png',
|
||||
@ -45,7 +46,8 @@ const seed = async () => {
|
||||
password: 'test123!',
|
||||
teamId: team.id,
|
||||
isAdmin: true,
|
||||
slackId: 'U2399UF1P',
|
||||
service: 'slack',
|
||||
serviceId: 'U2399UF1P',
|
||||
slackData: {
|
||||
id: 'U2399UF1P',
|
||||
image_192: 'http://example.com/avatar.png',
|
||||
|
27
shared/components/GoogleLogo.js
Normal file
27
shared/components/GoogleLogo.js
Normal file
@ -0,0 +1,27 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
|
||||
type Props = {
|
||||
size?: number,
|
||||
fill?: string,
|
||||
className?: string,
|
||||
};
|
||||
|
||||
function GoogleLogo({ size = 34, fill = '#FFF', className }: Props) {
|
||||
return (
|
||||
<svg
|
||||
fill={fill}
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 34 34"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<g>
|
||||
<path d="M32.6162791,13.9090909 L16.8837209,13.9090909 L16.8837209,20.4772727 L25.9395349,20.4772727 C25.0953488,24.65 21.5651163,27.0454545 16.8837209,27.0454545 C11.3581395,27.0454545 6.90697674,22.5636364 6.90697674,17 C6.90697674,11.4363636 11.3581395,6.95454545 16.8837209,6.95454545 C19.2627907,6.95454545 21.4116279,7.80454545 23.1,9.19545455 L28.0116279,4.25 C25.0186047,1.62272727 21.1813953,0 16.8837209,0 C7.52093023,0 0,7.57272727 0,17 C0,26.4272727 7.52093023,34 16.8837209,34 C25.3255814,34 33,27.8181818 33,17 C33,15.9954545 32.8465116,14.9136364 32.6162791,13.9090909 Z" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default GoogleLogo;
|
Reference in New Issue
Block a user