Fixes #3412 Previously the only way to restrict the domains for a Team were with the ALLOWED_DOMAINS environment variable for self hosted instances. This PR migrates this to be a database backed setting on the Team object. This is done through the creation of a TeamDomain model that is associated with the Team and contains the domain name This settings is updated on the Security Tab. Here domains can be added or removed from the Team. On the server side, we take the code paths that previously were using ALLOWED_DOMAINS and switched them to use the Team allowed domains instead
113 lines
2.9 KiB
TypeScript
113 lines
2.9 KiB
TypeScript
import passport from "@outlinewiki/koa-passport";
|
|
import { Request } from "koa";
|
|
import Router from "koa-router";
|
|
import { capitalize } from "lodash";
|
|
import { Profile } from "passport";
|
|
import { Strategy as GoogleStrategy } from "passport-google-oauth2";
|
|
import accountProvisioner, {
|
|
AccountProvisionerResult,
|
|
} from "@server/commands/accountProvisioner";
|
|
import env from "@server/env";
|
|
import { GoogleWorkspaceRequiredError } from "@server/errors";
|
|
import passportMiddleware from "@server/middlewares/passport";
|
|
import { User } from "@server/models";
|
|
import { StateStore } from "@server/utils/passport";
|
|
|
|
const router = new Router();
|
|
const providerName = "google";
|
|
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
|
|
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET;
|
|
const scopes = [
|
|
"https://www.googleapis.com/auth/userinfo.profile",
|
|
"https://www.googleapis.com/auth/userinfo.email",
|
|
];
|
|
|
|
export const config = {
|
|
name: "Google",
|
|
enabled: !!GOOGLE_CLIENT_ID,
|
|
};
|
|
|
|
type GoogleProfile = Profile & {
|
|
email: string;
|
|
picture: string;
|
|
_json: {
|
|
hd: string;
|
|
};
|
|
};
|
|
|
|
if (GOOGLE_CLIENT_ID && GOOGLE_CLIENT_SECRET) {
|
|
passport.use(
|
|
new GoogleStrategy(
|
|
{
|
|
clientID: GOOGLE_CLIENT_ID,
|
|
clientSecret: GOOGLE_CLIENT_SECRET,
|
|
callbackURL: `${env.URL}/auth/google.callback`,
|
|
passReqToCallback: true,
|
|
// @ts-expect-error StateStore
|
|
store: new StateStore(),
|
|
scope: scopes,
|
|
},
|
|
async function (
|
|
req: Request,
|
|
accessToken: string,
|
|
refreshToken: string,
|
|
profile: GoogleProfile,
|
|
done: (
|
|
err: Error | null,
|
|
user: User | null,
|
|
result?: AccountProvisionerResult
|
|
) => void
|
|
) {
|
|
try {
|
|
const domain = profile._json.hd;
|
|
|
|
if (!domain) {
|
|
throw GoogleWorkspaceRequiredError();
|
|
}
|
|
|
|
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,
|
|
scopes,
|
|
},
|
|
});
|
|
return done(null, result.user, result);
|
|
} catch (err) {
|
|
return done(err, null);
|
|
}
|
|
}
|
|
)
|
|
);
|
|
|
|
router.get(
|
|
"google",
|
|
passport.authenticate(providerName, {
|
|
accessType: "offline",
|
|
prompt: "select_account consent",
|
|
})
|
|
);
|
|
|
|
router.get("google.callback", passportMiddleware(providerName));
|
|
}
|
|
|
|
export default router;
|