Merge pull request #4776 from Dokploy/fix/sso-trusted-origins-restart

fix(sso): apply trusted origin changes without server restart
This commit is contained in:
Mauricio Siu
2026-07-09 01:49:13 -06:00
committed by GitHub
3 changed files with 48 additions and 28 deletions

View File

@@ -8,6 +8,7 @@ import {
requestToHeaders,
} from "@dokploy/server/index";
import { auth } from "@dokploy/server/lib/auth";
import { invalidateTrustedOriginsCache } from "@dokploy/server/services/admin";
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
import { TRPCError } from "@trpc/server";
import { and, asc, eq } from "drizzle-orm";
@@ -319,6 +320,7 @@ export const ssoRouter = createTRPCRouter({
.update(user)
.set({ trustedOrigins: next })
.where(eq(user.id, ownerId));
invalidateTrustedOriginsCache();
return { success: true };
}),
removeTrustedOrigin: enterpriseProcedure
@@ -346,6 +348,7 @@ export const ssoRouter = createTRPCRouter({
.update(user)
.set({ trustedOrigins: next })
.where(eq(user.id, ownerId));
invalidateTrustedOriginsCache();
return { success: true };
}),
updateTrustedOrigin: enterpriseProcedure
@@ -379,6 +382,7 @@ export const ssoRouter = createTRPCRouter({
.update(user)
.set({ trustedOrigins: next })
.where(eq(user.id, ownerId));
invalidateTrustedOriginsCache();
return { success: true };
}),
});

View File

@@ -4,7 +4,7 @@ import { sso } from "@better-auth/sso";
import * as bcrypt from "bcrypt";
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { APIError } from "better-auth/api";
import { APIError, createAuthMiddleware } from "better-auth/api";
import { admin, organization, twoFactor } from "better-auth/plugins";
import { and, desc, eq } from "drizzle-orm";
import { IS_CLOUD } from "../constants";
@@ -29,6 +29,37 @@ import { getPublicIpWithFallback } from "../wss/utils";
import { ac, adminRole, memberRole, ownerRole } from "./access-control";
import { betterAuthSecret } from "./auth-secret";
const resolveTrustedOrigins = async () => {
try {
if (IS_CLOUD) {
return await getTrustedOrigins();
}
const [trustedOrigins, settings] = await Promise.all([
getTrustedOrigins(),
getWebServerSettings(),
]);
if (!settings) return [];
const devOrigins =
process.env.NODE_ENV === "development"
? [
"http://localhost:3000",
"https://absolutely-handy-falcon.ngrok-free.app",
]
: [];
return [
...(settings?.serverIp ? [`http://${settings?.serverIp}:3000`] : []),
...(settings?.host ? [`https://${settings?.host}`] : []),
...devOrigins,
...trustedOrigins,
];
} catch (error) {
console.error("Failed to resolve trusted origins:", error);
return [];
}
};
const { handler, api } = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
@@ -80,33 +111,14 @@ const { handler, api } = betterAuth({
logger: {
disabled: process.env.NODE_ENV === "production",
},
async trustedOrigins() {
try {
if (IS_CLOUD) {
return await getTrustedOrigins();
}
const [trustedOrigins, settings] = await Promise.all([
getTrustedOrigins(),
getWebServerSettings(),
]);
if (!settings) return [];
const devOrigins =
process.env.NODE_ENV === "development"
? [
"http://localhost:3000",
"https://absolutely-handy-falcon.ngrok-free.app",
]
: [];
return [
...(settings?.serverIp ? [`http://${settings?.serverIp}:3000`] : []),
...(settings?.host ? [`https://${settings?.host}`] : []),
...devOrigins,
...trustedOrigins,
];
} catch (error) {
console.error("Failed to resolve trusted origins:", error);
return [];
}
trustedOrigins: resolveTrustedOrigins,
hooks: {
before: createAuthMiddleware(async (ctx) => {
ctx.context.trustedOrigins = [
...(ctx.context.baseURL ? [new URL(ctx.context.baseURL).origin] : []),
...(await resolveTrustedOrigins()),
].filter(Boolean);
}),
},
emailVerification: {
sendOnSignUp: true,

View File

@@ -120,6 +120,10 @@ export const getDokployUrl = async () => {
const TRUSTED_ORIGINS_CACHE_TTL_MS = 30 * 60_000;
let trustedOriginsCache: { data: string[]; expiresAt: number } | null = null;
export const invalidateTrustedOriginsCache = () => {
trustedOriginsCache = null;
};
export const getTrustedOrigins = async () => {
const runQuery = async () => {
const rows = await db