From 415b05fd29d302f22ea6d3aae046c23cef28d721 Mon Sep 17 00:00:00 2001 From: _ <50262751+hunzlahmalik@users.noreply.github.com> Date: Mon, 18 May 2026 14:06:21 +0500 Subject: [PATCH] fix(auth): derive SSO display name from email local-part MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit oauth2-proxy was putting the Cognito sub UUID into x-auth-request-user instead of a human-readable username, so newly-provisioned SSO users landed with a UUID as their User.name. Drop the header read in the ForwardAuth middleware and use the email local-part instead — the same value both apps already fell back to when the header was absent; we're promoting that fallback to the only source. Existing users with UUID names are not auto-corrected: the middleware does not re-sync the name field on subsequent logins. Co-Authored-By: Claude Opus 4.7 --- server/env.ts | 9 ++++--- server/middlewares/authentication.test.ts | 33 ----------------------- server/middlewares/authentication.ts | 3 +-- 3 files changed, 6 insertions(+), 39 deletions(-) diff --git a/server/env.ts b/server/env.ts index bba2e35bdab0..36dcaa34c91a 100644 --- a/server/env.ts +++ b/server/env.ts @@ -520,10 +520,11 @@ export class Environment { /** * The authentication type to use. When set to "SSO", the server will trust - * X-Auth-Request-Email and X-Auth-Request-User headers injected by a reverse - * proxy (e.g. oauth2-proxy, Authelia) for authentication and automatic user - * provisioning. Only enable this when Outline is deployed behind a trusted - * authenticating proxy on a self-hosted instance. + * the X-Auth-Request-Email header injected by a reverse proxy + * (e.g. oauth2-proxy, Authelia) for authentication and automatic user + * provisioning. The display name is derived from the email local-part. + * Only enable this when Outline is deployed behind a trusted authenticating + * proxy on a self-hosted instance. */ @Public @IsOptional() diff --git a/server/middlewares/authentication.test.ts b/server/middlewares/authentication.test.ts index f26eba9551c9..7b4931cb7298 100644 --- a/server/middlewares/authentication.test.ts +++ b/server/middlewares/authentication.test.ts @@ -436,9 +436,6 @@ describe("Authentication middleware", () => { if (header === "x-auth-request-email") { return newEmail; } - if (header === "x-auth-request-user") { - return "New User"; - } return ""; }), }, @@ -455,36 +452,6 @@ describe("Authentication middleware", () => { where: { email: newEmail.toLowerCase() }, }); expect(provisioned).not.toBeNull(); - expect(state.auth.user.email).toEqual(newEmail.toLowerCase()); - expect(state.auth.user.name).toEqual("New User"); - }); - - it("should use email prefix as name when X-Auth-Request-User is absent", async () => { - await buildTeam(); - const state = {} as DefaultState; - const authMiddleware = auth(); - const newEmail = `prefix-${randomString(6)}@example.com`; - - await authMiddleware( - { - // @ts-expect-error mock request - request: { - get: jest.fn((header: string) => { - if (header === "x-auth-request-email") { - return newEmail; - } - return ""; - }), - }, - // @ts-expect-error mock cookies - cookies: { get: jest.fn(() => undefined), set: jest.fn() }, - state, - ip: "127.0.0.1", - cache: {}, - }, - jest.fn() - ); - expect(state.auth.user.email).toEqual(newEmail.toLowerCase()); expect(state.auth.user.name).toEqual( newEmail.toLowerCase().split("@")[0] diff --git a/server/middlewares/authentication.ts b/server/middlewares/authentication.ts index 9fc103f79b36..7dd08083f3b9 100644 --- a/server/middlewares/authentication.ts +++ b/server/middlewares/authentication.ts @@ -375,7 +375,6 @@ async function validateAuthentication( const email = normalizeProxyEmail(token.slice(4)); const localPart = email.split("@")[0]; - const displayName = ctx.request.get("x-auth-request-user") || localPart; const { domain } = parseEmail(email); // Concurrent-creation race guard. The SPA on first-ever login fires @@ -448,7 +447,7 @@ async function validateAuthentication( }); const created = await User.create( { - name: displayName, + name: localPart, email, teamId: team.id, // First user into a brand-new team becomes admin.