fix: Logging in with email on a subdomain should not forward to other subdomains (#4305)

This commit is contained in:
Tom Moor
2022-10-16 11:20:46 -04:00
committed by GitHub
parent ac31850a53
commit 5d5fe66e77
2 changed files with 68 additions and 71 deletions

View File

@@ -33,9 +33,11 @@ describe("email", () => {
spy.mockRestore();
});
it("should respond with redirect location when user is SSO enabled on another subdomain", async () => {
it("should not send email when user is on another subdomain but respond with success", async () => {
env.URL = sharedEnv.URL = "http://localoutline.com";
env.SUBDOMAINS_ENABLED = sharedEnv.SUBDOMAINS_ENABLED = true;
env.DEPLOYMENT = "hosted";
const user = await buildUser();
const spy = jest.spyOn(WelcomeEmail, "schedule");
await buildTeam({
@@ -49,20 +51,29 @@ describe("email", () => {
host: "example.localoutline.com",
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.redirect).toMatch("slack");
expect(body.success).toEqual(true);
expect(spy).not.toHaveBeenCalled();
spy.mockRestore();
});
it("should respond with success when user is not SSO enabled", async () => {
it("should respond with success and email to be sent when user is not SSO enabled", async () => {
const spy = jest.spyOn(SigninEmail, "schedule");
const user = await buildGuestUser();
const team = await buildTeam({
subdomain: "example",
});
const user = await buildGuestUser({
teamId: team.id,
});
const res = await server.post("/auth/email", {
body: {
email: user.email,
},
headers: {
host: "example.localoutline.com",
},
});
const body = await res.json();
expect(res.status).toEqual(200);
@@ -73,10 +84,16 @@ describe("email", () => {
it("should respond with success regardless of whether successful to prevent crawling email logins", async () => {
const spy = jest.spyOn(WelcomeEmail, "schedule");
await buildTeam({
subdomain: "example",
});
const res = await server.post("/auth/email", {
body: {
email: "user@example.com",
},
headers: {
host: "example.localoutline.com",
},
});
const body = await res.json();
expect(res.status).toEqual(200);

View File

@@ -31,83 +31,63 @@ router.post(
async (ctx) => {
const { email } = ctx.body;
assertEmail(email, "email is required");
const users = await User.scope("withAuthentications").findAll({
const domain = parseDomain(ctx.request.hostname);
let team: Team | null | undefined;
if (env.DEPLOYMENT !== "hosted") {
team = await Team.scope("withAuthenticationProviders").findOne();
} else if (domain.custom) {
team = await Team.scope("withAuthenticationProviders").findOne({
where: { domain: domain.host },
});
} else if (env.SUBDOMAINS_ENABLED && domain.teamSubdomain) {
team = await Team.scope("withAuthenticationProviders").findOne({
where: { subdomain: domain.teamSubdomain },
});
}
if (!team?.emailSigninEnabled) {
throw AuthorizationError();
}
const user = await User.scope("withAuthentications").findOne({
where: {
teamId: team.id,
email: email.toLowerCase(),
},
});
if (users.length) {
let team!: Team | null;
const domain = parseDomain(ctx.request.hostname);
if (!user) {
ctx.body = {
success: true,
};
return;
}
if (domain.custom) {
team = await Team.scope("withAuthenticationProviders").findOne({
where: {
domain: ctx.request.hostname,
},
});
} else if (env.SUBDOMAINS_ENABLED && domain.teamSubdomain) {
team = await Team.scope("withAuthenticationProviders").findOne({
where: {
subdomain: domain.teamSubdomain,
},
});
}
// If there are multiple users with this email address then give precedence
// to the one that is active on this subdomain/domain (if any)
let user = users.find((user) => team && user.teamId === team.id);
// A user was found for the email address, but they don't belong to the team
// that this subdomain belongs to, we load their team and allow the logic to
// continue
if (!user) {
user = users[0];
team = await Team.scope("withAuthenticationProviders").findByPk(
user.teamId
);
}
if (!team) {
team = await Team.scope("withAuthenticationProviders").findByPk(
user.teamId
);
}
if (!team) {
ctx.redirect(`/?notice=auth-error`);
// If the user matches an email address associated with an SSO
// provider then just forward them directly to that sign-in page
if (user.authentications.length) {
const authProvider = find(team.authenticationProviders, {
id: user.authentications[0].authenticationProviderId,
});
if (authProvider?.enabled) {
ctx.body = {
redirect: `${team.url}/auth/${authProvider?.name}`,
};
return;
}
// If the user matches an email address associated with an SSO
// provider then just forward them directly to that sign-in page
if (user.authentications.length) {
const authProvider = find(team.authenticationProviders, {
id: user.authentications[0].authenticationProviderId,
});
if (authProvider?.enabled) {
ctx.body = {
redirect: `${team.url}/auth/${authProvider?.name}`,
};
return;
}
}
if (!team.emailSigninEnabled) {
throw AuthorizationError();
}
// send email to users registered address with a short-lived token
await SigninEmail.schedule({
to: user.email,
token: user.getEmailSigninToken(),
teamUrl: team.url,
});
user.lastSigninEmailSentAt = new Date();
await user.save();
}
// send email to users registered address with a short-lived token
await SigninEmail.schedule({
to: user.email,
token: user.getEmailSigninToken(),
teamUrl: team.url,
});
user.lastSigninEmailSentAt = new Date();
await user.save();
// respond with success regardless of whether an email was sent
ctx.body = {
success: true,