feat: allow personal gmail accounts to be used to sign into teams with an existing invite (#3652)

* feat: allow personal gmail accounts to be used to sign into teams with an existing invite

* address comments

* add comment for appDomain

* address comments
This commit is contained in:
Nan Yu
2022-06-20 01:33:16 -07:00
committed by GitHub
parent 188c1e409b
commit e0d2b6cace
6 changed files with 158 additions and 56 deletions

View File

@@ -1,17 +1,18 @@
import passport from "@outlinewiki/koa-passport";
import { Request } from "koa";
import type { Request } from "express";
import Router from "koa-router";
import { capitalize } from "lodash";
import { Profile } from "passport";
import { Strategy as GoogleStrategy } from "passport-google-oauth2";
import { parseDomain } from "@shared/utils/domains";
import accountProvisioner, {
AccountProvisionerResult,
} from "@server/commands/accountProvisioner";
import env from "@server/env";
import { GoogleWorkspaceRequiredError } from "@server/errors";
import { InviteRequiredError, TeamDomainRequiredError } from "@server/errors";
import passportMiddleware from "@server/middlewares/passport";
import { User } from "@server/models";
import { StateStore } from "@server/utils/passport";
import { Team, User } from "@server/models";
import { StateStore, parseState } from "@server/utils/passport";
const router = new Router();
const providerName = "google";
@@ -58,38 +59,82 @@ if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
) => void
) {
try {
const state = req.cookies.get("state");
const host = state ? parseState(state).host : req.hostname;
// appDomain is the domain the user originated from when attempting auth
const appDomain = parseDomain(host);
let result;
const domain = profile._json.hd;
if (!domain) {
throw GoogleWorkspaceRequiredError();
// Existence of domain means this is a Google Workspaces account
// so we'll attempt to provision an account (team and user)
if (domain) {
const subdomain = domain.split(".")[0];
const teamName = capitalize(subdomain);
result = await accountProvisioner({
ip: req.ip,
team: {
name: teamName,
domain,
subdomain,
},
user: {
email: profile.email,
name: profile.displayName,
avatarUrl: profile.picture,
},
authenticationProvider: {
name: providerName,
providerId: domain,
},
authentication: {
providerId: profile.id,
accessToken,
refreshToken,
expiresIn: params.expires_in,
scopes,
},
});
} else {
// No domain means it's a personal Gmail account
// We only allow sign-in to existing invites here
let team;
if (appDomain.custom) {
team = await Team.findOne({ where: { domain: appDomain.host } });
} else if (env.SUBDOMAINS_ENABLED && appDomain.teamSubdomain) {
team = await Team.findOne({
where: { subdomain: appDomain.teamSubdomain },
});
} else if (env.DEPLOYMENT !== "hosted") {
team = await Team.findOne();
}
if (!team) {
throw TeamDomainRequiredError();
}
const user = await User.findOne({
where: { teamId: team.id, email: profile.email.toLowerCase() },
});
if (!user) {
throw InviteRequiredError();
}
await user.update({
lastActiveAt: new Date(),
});
result = {
user,
team,
isNewUser: false,
isNewTeam: false,
};
}
const subdomain = domain.split(".")[0];
const teamName = capitalize(subdomain);
const result = await accountProvisioner({
ip: req.ip,
team: {
name: teamName,
domain,
subdomain,
},
user: {
email: profile.email,
name: profile.displayName,
avatarUrl: profile.picture,
},
authenticationProvider: {
name: providerName,
providerId: domain,
},
authentication: {
providerId: profile.id,
accessToken,
refreshToken,
expiresIn: params.expires_in,
scopes,
},
});
return done(null, result.user, result);
} catch (err) {
return done(err, null);