diff --git a/server/db/sqlite/schema/privateSchema.ts b/server/db/sqlite/schema/privateSchema.ts
index 265cee10..40f6d713 100644
--- a/server/db/sqlite/schema/privateSchema.ts
+++ b/server/db/sqlite/schema/privateSchema.ts
@@ -79,6 +79,7 @@ export const subscriptionItems = sqliteTable("subscriptionItems", {
subscriptionItemId: integer("subscriptionItemId").primaryKey({
autoIncrement: true
}),
+ stripeSubscriptionItemId: text("stripeSubscriptionItemId"),
subscriptionId: text("subscriptionId")
.notNull()
.references(() => subscriptions.subscriptionId, {
diff --git a/server/private/lib/config.ts b/server/private/lib/config.ts
index a1baf5a6..2c3490ba 100644
--- a/server/private/lib/config.ts
+++ b/server/private/lib/config.ts
@@ -65,6 +65,11 @@ export class PrivateConfig {
this.rawPrivateConfig.branding?.logo?.dark_path || undefined;
}
+ if (this.rawPrivateConfig.app.identity_provider_mode) {
+ process.env.IDENTITY_PROVIDER_MODE =
+ this.rawPrivateConfig.app.identity_provider_mode;
+ }
+
process.env.BRANDING_LOGO_AUTH_WIDTH = this.rawPrivateConfig.branding
?.logo?.auth_page?.width
? this.rawPrivateConfig.branding?.logo?.auth_page?.width.toString()
@@ -129,10 +134,8 @@ export class PrivateConfig {
process.env.USE_PANGOLIN_DNS =
this.rawPrivateConfig.flags.use_pangolin_dns.toString();
}
- if (this.rawPrivateConfig.flags.use_org_only_idp) {
- process.env.USE_ORG_ONLY_IDP =
- this.rawPrivateConfig.flags.use_org_only_idp.toString();
- }
+
+ console.log(this.rawPrivateConfig.app.identity_provider_mode);
}
public getRawPrivateConfig() {
diff --git a/server/private/lib/readConfigFile.ts b/server/private/lib/readConfigFile.ts
index ac528e73..e5efa498 100644
--- a/server/private/lib/readConfigFile.ts
+++ b/server/private/lib/readConfigFile.ts
@@ -25,7 +25,8 @@ export const privateConfigSchema = z.object({
app: z
.object({
region: z.string().optional().default("default"),
- base_domain: z.string().optional()
+ base_domain: z.string().optional(),
+ identity_provider_mode: z.enum(["global", "org"]).optional()
})
.optional()
.default({
@@ -95,7 +96,7 @@ export const privateConfigSchema = z.object({
.object({
enable_redis: z.boolean().optional().default(false),
use_pangolin_dns: z.boolean().optional().default(false),
- use_org_only_idp: z.boolean().optional().default(false),
+ use_org_only_idp: z.boolean().optional()
})
.optional()
.prefault({}),
@@ -181,7 +182,29 @@ export const privateConfigSchema = z.object({
// localFilePath: z.string().optional()
})
.optional()
-});
+})
+ .transform((data) => {
+ // this to maintain backwards compatibility with the old config file
+ const identityProviderMode = data.app?.identity_provider_mode;
+ const useOrgOnlyIdp = data.flags?.use_org_only_idp;
+
+ if (identityProviderMode !== undefined) {
+ return data;
+ }
+ if (useOrgOnlyIdp === true) {
+ return {
+ ...data,
+ app: { ...data.app, identity_provider_mode: "org" as const }
+ };
+ }
+ if (useOrgOnlyIdp === false) {
+ return {
+ ...data,
+ app: { ...data.app, identity_provider_mode: "global" as const }
+ };
+ }
+ return data;
+ });
export function readPrivateConfigFile() {
if (build == "oss") {
diff --git a/server/private/routers/orgIdp/createOrgOidcIdp.ts b/server/private/routers/orgIdp/createOrgOidcIdp.ts
index d1874033..77346fd9 100644
--- a/server/private/routers/orgIdp/createOrgOidcIdp.ts
+++ b/server/private/routers/orgIdp/createOrgOidcIdp.ts
@@ -27,6 +27,7 @@ import config from "@server/lib/config";
import { CreateOrgIdpResponse } from "@server/routers/orgIdp/types";
import { isSubscribed } from "#private/lib/isSubscribed";
import { tierMatrix } from "@server/lib/billing/tierMatrix";
+import privateConfig from "#private/lib/config";
const paramsSchema = z.strictObject({ orgId: z.string().nonempty() });
@@ -92,6 +93,18 @@ export async function createOrgOidcIdp(
);
}
+ if (
+ privateConfig.getRawPrivateConfig().app.identity_provider_mode !==
+ "org"
+ ) {
+ return next(
+ createHttpError(
+ HttpCode.BAD_REQUEST,
+ "Organization-specific IdP creation is not allowed in the current identity provider mode. Set app.identity_provider_mode to 'org' in the private configuration to enable this feature."
+ )
+ );
+ }
+
const {
clientId,
clientSecret,
diff --git a/server/private/routers/orgIdp/deleteOrgIdp.ts b/server/private/routers/orgIdp/deleteOrgIdp.ts
index 176f4238..2d6b0899 100644
--- a/server/private/routers/orgIdp/deleteOrgIdp.ts
+++ b/server/private/routers/orgIdp/deleteOrgIdp.ts
@@ -22,6 +22,7 @@ import { fromError } from "zod-validation-error";
import { idp, idpOidcConfig, idpOrg } from "@server/db";
import { eq } from "drizzle-orm";
import { OpenAPITags, registry } from "@server/openApi";
+import privateConfig from "#private/lib/config";
const paramsSchema = z
.object({
@@ -59,6 +60,18 @@ export async function deleteOrgIdp(
const { idpId } = parsedParams.data;
+ if (
+ privateConfig.getRawPrivateConfig().app.identity_provider_mode !==
+ "org"
+ ) {
+ return next(
+ createHttpError(
+ HttpCode.BAD_REQUEST,
+ "Organization-specific IdP creation is not allowed in the current identity provider mode. Set app.identity_provider_mode to 'org' in the private configuration to enable this feature."
+ )
+ );
+ }
+
// Check if IDP exists
const [existingIdp] = await db
.select()
diff --git a/server/private/routers/orgIdp/updateOrgOidcIdp.ts b/server/private/routers/orgIdp/updateOrgOidcIdp.ts
index c5619460..804afbe6 100644
--- a/server/private/routers/orgIdp/updateOrgOidcIdp.ts
+++ b/server/private/routers/orgIdp/updateOrgOidcIdp.ts
@@ -26,6 +26,7 @@ import { encrypt } from "@server/lib/crypto";
import config from "@server/lib/config";
import { isSubscribed } from "#private/lib/isSubscribed";
import { tierMatrix } from "@server/lib/billing/tierMatrix";
+import privateConfig from "#private/lib/config";
const paramsSchema = z
.object({
@@ -97,6 +98,18 @@ export async function updateOrgOidcIdp(
);
}
+ if (
+ privateConfig.getRawPrivateConfig().app.identity_provider_mode !==
+ "org"
+ ) {
+ return next(
+ createHttpError(
+ HttpCode.BAD_REQUEST,
+ "Organization-specific IdP creation is not allowed in the current identity provider mode. Set app.identity_provider_mode to 'org' in the private configuration to enable this feature."
+ )
+ );
+ }
+
const { idpId, orgId } = parsedParams.data;
const {
clientId,
diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx
index 121e7196..b00ce1ee 100644
--- a/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx
+++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx
@@ -132,7 +132,7 @@ export default function ResourceAuthenticationPage() {
const { data: orgIdps = [], isLoading: isLoadingOrgIdps } = useQuery(
orgQueries.identityProviders({
orgId: org.org.orgId,
- useOrgOnlyIdp: env.flags.useOrgOnlyIdp
+ useOrgOnlyIdp: env.app.identityProviderMode === "org"
})
);
diff --git a/src/app/auth/login/page.tsx b/src/app/auth/login/page.tsx
index 2ba4d7f8..bfb552df 100644
--- a/src/app/auth/login/page.tsx
+++ b/src/app/auth/login/page.tsx
@@ -76,12 +76,13 @@ export default async function Page(props: {
// Only use SmartLoginForm if NOT (OSS build OR org-only IdP enabled)
const useSmartLogin =
- build === "saas" || (build === "enterprise" && env.flags.useOrgOnlyIdp);
+ build === "saas" ||
+ (build === "enterprise" && env.app.identityProviderMode === "org");
let loginIdps: LoginFormIDP[] = [];
if (!useSmartLogin) {
// Load IdPs for DashboardLoginForm (OSS or org-only IdP mode)
- if (build === "oss" || !env.flags.useOrgOnlyIdp) {
+ if (build === "oss" || env.app.identityProviderMode !== "org") {
const idpsRes = await cache(
async () =>
await priv.get
{getSubtitle()}
-