Compare commits

...

3 Commits

Author SHA1 Message Date
Tom Moor 30cdbb561c 0.79.2-0 2024-09-14 15:24:23 -04:00
Tom Moor 62c1ef6959 refactor 2024-09-14 15:19:18 -04:00
Tom Moor 70e189ec9b Refactor OIDC signin to prevent duplicate auth providers 2024-09-14 15:09:01 -04:00
5 changed files with 34 additions and 20 deletions
+1 -1
View File
@@ -366,5 +366,5 @@
"qs": "6.9.7",
"rollup": "^4.5.1"
},
"version": "0.79.1"
"version": "0.79.2-0"
}
+20 -6
View File
@@ -11,7 +11,7 @@ import {
AuthenticationError,
} from "@server/errors";
import passportMiddleware from "@server/middlewares/passport";
import { User } from "@server/models";
import { AuthenticationProvider, User } from "@server/models";
import { AuthenticationResult } from "@server/types";
import {
StateStore,
@@ -93,6 +93,21 @@ if (
}
const team = await getTeamFromContext(ctx);
const client = getClientFromContext(ctx);
// Only a single OIDC provider is supported find the existing, if any.
const authenticationProvider = team
? await AuthenticationProvider.findOne({
where: {
name: "oidc",
teamId: team.id,
},
})
: undefined;
// Derive a providerId from the OIDC location if there is no existing provider.
const oidcURL = new URL(env.OIDC_AUTH_URI!);
const providerId =
authenticationProvider?.providerId ?? oidcURL.hostname;
const { domain } = parseEmail(profile.email);
if (!domain) {
@@ -106,7 +121,7 @@ if (
// Default is 'preferred_username' as per OIDC spec.
const username = get(profile, env.OIDC_USERNAME_CLAIM);
const name = profile.name || username || profile.username;
const providerId = profile.sub ? profile.sub : profile.id;
const profileId = profile.sub ? profile.sub : profile.id;
if (!name) {
throw AuthenticationError(
@@ -118,8 +133,7 @@ if (
ip: ctx.ip,
team: {
teamId: team?.id,
// https://github.com/outline/outline/pull/2388#discussion_r681120223
name: "Wiki",
name: env.APP_NAME,
domain,
subdomain,
},
@@ -130,10 +144,10 @@ if (
},
authenticationProvider: {
name: config.id,
providerId: domain,
providerId,
},
authentication: {
providerId,
providerId: profileId,
accessToken,
refreshToken,
expiresIn: params.expires_in,
+3 -1
View File
@@ -1,4 +1,5 @@
import { faker } from "@faker-js/faker";
import { Team } from "@server/models";
import TeamDomain from "@server/models/TeamDomain";
import { buildTeam, buildUser } from "@server/test/factories";
import { setSelfHosted } from "@server/test/support";
@@ -128,8 +129,9 @@ describe("teamProvisioner", () => {
});
});
describe.skip("self hosted", () => {
describe("self hosted", () => {
beforeEach(setSelfHosted);
beforeEach(() => Team.truncate());
it("should allow creating first team", async () => {
const subdomain = faker.internet.domainWord();
+7 -11
View File
@@ -81,15 +81,15 @@ async function teamProvisioner({
throw InvalidAuthenticationError();
}
// This team has never been seen before, if self hosted the logic is different
// to the multi-tenant version, we want to restrict to a single team that MAY
// have multiple authentication providers
const team = await Team.findOne();
// This team + auth provider combination has not been seen before in self hosted
const team = await Team.findByPk(teamId, {
rejectOnEmpty: true,
});
// If the self-hosted installation has a single team and the domain for the
// new team is allowed then assign the authentication provider to the
// existing team
if (team && domain) {
if (domain) {
if (await team.isDomainAllowed(domain)) {
authP = await team.$create<AuthenticationProvider>(
"authenticationProvider",
@@ -100,14 +100,10 @@ async function teamProvisioner({
team,
isNewTeam: false,
};
} else {
throw DomainNotAllowedError();
}
throw DomainNotAllowedError();
}
if (team) {
throw InvalidAuthenticationError();
}
throw InvalidAuthenticationError();
}
// We cannot find an existing team, so we create a new one
+3 -1
View File
@@ -114,7 +114,9 @@ export async function getTeamFromContext(ctx: Context) {
if (env.ENVIRONMENT === "test") {
team = await Team.findOne({ where: { domain: env.URL } });
} else {
team = await Team.findOne();
team = await Team.findOne({
order: [["createdAt", "ASC"]],
});
}
} else if (ctx.state?.rootShare) {
team = await Team.findByPk(ctx.state.rootShare.teamId);