{"record":{"id":"36819c073da6c079","repo":"cube-js/cube","slug":"you-cannot-change-security-context-via-user-from","errorCode":null,"errorMessage":"You cannot change security context via __user from ${session.user} to ${request.meta.changeUser}, because it's not allowed.","messagePattern":"You cannot change security context via __user from (.+?) to (.+?), because it's not allowed\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cubejs-api-gateway/src/sql-server.ts","lineNumber":121,"sourceCode":"    const canSwitchSqlUser: CanSwitchSQLUserFn = options.canSwitchSqlUser\n      || this.createDefaultCanSwitchSqlUserFn(options);\n\n    const contextByRequest = async (request, session) => {\n      let userForContext = session.user;\n      let { securityContext } = session;\n\n      if (request.meta.changeUser && request.meta.changeUser !== session.user) {\n        const sqlAuthRequest: SqlAuthServiceAuthenticateRequest = {\n          protocol: request.meta.protocol,\n          method: 'password',\n        };\n        const canSwitch = session.superuser || await canSwitchSqlUser(session.user, request.meta.changeUser);\n        if (canSwitch) {\n          userForContext = request.meta.changeUser;\n          const current = await checkSqlAuth({ ...request, ...sqlAuthRequest }, userForContext, null);\n          securityContext = current.securityContext;\n        } else {\n          throw new Error(\n            `You cannot change security context via __user from ${session.user} to ${request.meta.changeUser}, because it's not allowed.`\n          );\n        }\n      }\n      return this.contextByNativeReq(request, securityContext, request.id);\n    };\n\n    const canSwitchUserForSession = async (session, user) => session.superuser || canSwitchSqlUser(session.user, user);\n\n    this.sqlInterfaceInstance = await registerInterface({\n      gatewayPort: this.gatewayPort,\n      pgPort: options.pgSqlPort,\n      contextToApiScopes: async ({ securityContext }) => this.apiGateway.contextToApiScopesFn(\n        securityContext,\n        getEnv('defaultApiScope') || await this.apiGateway.contextToApiScopesDefFn()\n      ),\n      checkAuth: async ({ request, token }) => {\n        const { securityContext } = await this.apiGateway.checkAuthFn(request, token);","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-api-gateway/src/sql-server.ts#L103-L139","documentation":"When a SQL client sends the special __user meta property (session variable in the SQL query) with a different username, Cube tries to switch the security context to that user. Switching is only allowed for superusers or when the canSwitchSqlUser callback permits it; otherwise the SQL server throws this error naming the originating and requested users.","triggerScenarios":"A SQL client issues a query whose __user session variable differs from the authenticated SQL user (e.g. `SET __user = 'other'; SELECT ...`) while the authenticated session is not a superuser and no canSwitchSqlUser option authorizes the switch.","commonSituations":"BI tools configured with a tenant/user template variable that injects __user while connecting as a shared SQL account; developers imitating multi-tenant impersonation without defining canSwitchSqlUser; connecting as a non-superuser account (sqlUser) instead of the superuser (sqlSuperUser) when impersonation is intended.","solutions":["Configure canSwitchSqlUser in SQLServer options to authorize the specific user switch.","Connect with the SQL superuser account (sqlSuperUser) when impersonation via __user is required.","Remove or correct the __user variable in the client's session settings/BI tool configuration so it matches the authenticated user."],"exampleFix":"// before\nconst server = new SQLServer(apiGateway, { gatewayPort });\n// after\nconst server = new SQLServer(apiGateway, {\n  gatewayPort,\n  canSwitchSqlUser: async (fromUser, toUser) => toUser.startsWith('tenant_')\n});","handlingStrategy":"try-catch","validationCode":"const mayImpersonate = async (session, targetUser) =>\n  session.superuser || (typeof canSwitchSqlUser === 'function' && await canSwitchSqlUser(session.user, targetUser));\n// check mayImpersonate(session, request.meta.changeUser) before sending __user","typeGuard":"function requestsImpersonation(msg: { meta?: { changeUser?: string, protocol?: string } }, sessionUser: string): boolean {\n  return !!msg.meta?.changeUser && msg.meta.changeUser !== sessionUser;\n}","tryCatchPattern":"try {\n  await runSqlWithImpersonation(query, targetUser);\n} catch (e) {\n  if (e.message.startsWith(\"You cannot change security context via __user\")) {\n    console.error(`Impersonation from ${session.user} to ${targetUser} denied; connect as superuser or configure canSwitchSqlUser`);\n  } else throw e;\n}","preventionTips":["Configure canSwitchSqlUser whenever BI tools inject __user variables.","Use the sqlSuperUser account for impersonating workflows.","Audit BI tool connection settings for templated __user variables.","Never rely on __user impersonation with non-superuser accounts unless explicitly allowed."],"tags":["auth","security","sql","impersonation"],"backgroundTag":"unauthorized-user-impersonation","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}