mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-25 08:32:11 +03:00
98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
/**
|
|
* db/webSessionDedup.ts — pure helpers for de-duplicating web-session
|
|
* (cookie/token) provider credentials. Extracted from providers.ts so the
|
|
* cookie-dedup wiring there stays thin (#3368 PR6). No DB access here.
|
|
*/
|
|
|
|
/**
|
|
* Reduce a `provider_specific_data` record to a single comparable credential
|
|
* value. Cookie/token credentials are mirrored across a provider's storage
|
|
* keys (e.g. `cookie`, `sessionToken`, `token`) with the same secret value, so
|
|
* any one of them identifies the session. Returns the trimmed value, or null
|
|
* when no usable string credential is present.
|
|
*/
|
|
const PREFERRED_CREDENTIAL_KEYS = [
|
|
"cookie",
|
|
"token",
|
|
"sessionToken",
|
|
"session-token",
|
|
"sso",
|
|
"access_token",
|
|
"accessToken",
|
|
];
|
|
|
|
/** First trimmed non-empty string value among `keys` of `rec`, else null. */
|
|
function firstNonEmptyString(rec: Record<string, unknown>, keys: readonly string[]): string | null {
|
|
for (const key of keys) {
|
|
const value = rec[key];
|
|
if (typeof value === "string" && value.trim()) return value.trim();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function webSessionCredentialKey(psd: unknown): string | null {
|
|
if (!psd || typeof psd !== "object") return null;
|
|
const rec = psd as Record<string, unknown>;
|
|
// Prefer canonical credential keys, then fall back to the first non-empty
|
|
// string value (sorted for determinism).
|
|
return (
|
|
firstNonEmptyString(rec, PREFERRED_CREDENTIAL_KEYS) ??
|
|
firstNonEmptyString(rec, Object.keys(rec).sort())
|
|
);
|
|
}
|
|
|
|
/** Parse a stored `provider_specific_data` column (JSON string or object). */
|
|
export function parseProviderSpecificData(raw: unknown): Record<string, unknown> | null {
|
|
if (!raw) return null;
|
|
if (typeof raw === "object") return raw as Record<string, unknown>;
|
|
if (typeof raw === "string") {
|
|
try {
|
|
const parsed = JSON.parse(raw);
|
|
return parsed && typeof parsed === "object" ? (parsed as Record<string, unknown>) : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** Trimmed non-empty string, else null — local to avoid a cross-module import for one coercion. */
|
|
function nonEmptyString(value: unknown): string | null {
|
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
}
|
|
|
|
/**
|
|
* Two-sided disambiguator match: `true` when both sides agree, `false` when
|
|
* both carry a value and it differs, `undefined` when the field can't decide
|
|
* (at most one side carries it) — the caller then defers to other fields.
|
|
*/
|
|
function fieldMatch(incoming: string | null, existing: string | null): boolean | undefined {
|
|
if (incoming && existing) return incoming === existing;
|
|
if (incoming || existing) return false;
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Decide whether `row` (an existing `provider_connections` record) is the
|
|
* same OAuth identity as an incoming connection carrying `incomingUsername`
|
|
* and `incomingProfileArn` (#10815).
|
|
*
|
|
* Two independent disambiguators, either of which can prove "different
|
|
* account": `providerSpecificData.username` (Raycast-style IdP dedup) and
|
|
* `providerSpecificData.profileArn` (Kiro/AWS profile dedup — Kiro never
|
|
* sets `username`). A field only rules a match IN/OUT when both the
|
|
* incoming and existing record carry it; when neither carries either field
|
|
* the legacy bare-email match still applies unchanged.
|
|
*/
|
|
export function isMatchingOauthIdentity(
|
|
row: { provider_specific_data?: unknown },
|
|
incomingUsername: string | null,
|
|
incomingProfileArn: string | null
|
|
): boolean {
|
|
const existingPsd = parseProviderSpecificData(row.provider_specific_data);
|
|
const usernameMatch = fieldMatch(incomingUsername, nonEmptyString(existingPsd?.username));
|
|
const profileArnMatch = fieldMatch(incomingProfileArn, nonEmptyString(existingPsd?.profileArn));
|
|
if (usernameMatch === false || profileArnMatch === false) return false;
|
|
return true;
|
|
}
|