Impersonation
Running requests as another user — the two configuration keys, the headers they produce, and what it changes about signing out.
An administrator can run requests as another user, to see what that person sees. Support does it to reproduce a report of missing results; an administrator does it to verify a rights change.
Two configuration keys, and the second is the switch:
client.configure({
userOverride: { username: "alice", domain: "corp" },
userOverrideActive: true,
});This is an administrative capability
The server decides whether the signed-in user may impersonate. Setting these keys on an account without the
right produces a distinctive failure — see the pitfalls. Gate the control on
principal.isAdministrator or isDelegatedAdmin rather than offering it to everyone.
What it changes on the wire
While active, every request carries two headers:
sinequa-override-user: alice
sinequa-override-domain: corpAnd the request body says noUserOverride: false — the flag is the negation of userOverrideActive, so the
body and the headers agree. They used to disagree: the body sent a hard-coded true while the headers asked for
an override on the same request.
Results then come back as Alice would see them: her rights, her documents, her counts.
Turning it on and off
import { createAtomicClient } from "@sinequa/atomic";
const client = createAtomicClient({
app: "<your-app>",
backendUrl: "https://<your-sinequa-server>",
});
await client.initializeConfig();
await client.auth.login();
const me = await client.api.principal.get();
/** Only offer this where the server would allow it. */
export const mayImpersonate = me.isAdministrator || me.isDelegatedAdmin;
export function startImpersonating(username: string, domain: string) {
if (!mayImpersonate) throw new Error("You may not impersonate.");
client.configure({
userOverride: { username, domain },
userOverrideActive: true,
});
}
/** Clearing the flag is enough; the headers stop being sent immediately. */
export function stopImpersonating() {
client.configure({ userOverrideActive: false, userOverride: undefined });
}
/** What the banner should read. `config` is a live view, so this is current. */
export function impersonating() {
return client.config.userOverrideActive === true
? (client.config.userOverride as { username: string } | undefined)?.username
: undefined;
}import { Injectable, computed, inject, signal } from "@angular/core";
import { SINEQUA } from "./sinequa";
@Injectable({ providedIn: "root" })
export class ImpersonationService {
private readonly client = inject(SINEQUA);
/** Bumped on every change, so the computed values re-read the live config. */
private readonly version = signal(0);
readonly asUser = computed(() => {
this.version();
if (this.client.config.userOverrideActive !== true) return undefined;
return (this.client.config.userOverride as { username: string } | undefined)?.username;
});
start(username: string, domain: string) {
this.client.configure({ userOverride: { username, domain }, userOverrideActive: true });
this.version.update((v) => v + 1);
}
/** Clearing the flag is enough; the headers stop being sent immediately. */
stop() {
this.client.configure({ userOverrideActive: false, userOverride: undefined });
this.version.update((v) => v + 1);
}
}import { useCallback, useState } from "react";
import { sinequa } from "./sinequa";
export function useImpersonation() {
// The client's config is a live view, not reactive: mirror it into state so the
// banner re-renders.
const [asUser, setAsUser] = useState<string>();
const start = useCallback((username: string, domain: string) => {
sinequa.configure({ userOverride: { username, domain }, userOverrideActive: true });
setAsUser(username);
}, []);
/** Clearing the flag is enough; the headers stop being sent immediately. */
const stop = useCallback(() => {
sinequa.configure({ userOverrideActive: false, userOverride: undefined });
setAsUser(undefined);
}, []);
return { asUser, start, stop };
}Use Finding principals to pick who to impersonate rather than typing a username.