refactor(matrix): consolidate auth config sources (#117794)

This commit is contained in:
Peter Steinberger
2026-08-01 21:30:00 -07:00
committed by GitHub
parent ea01fffc0c
commit 88d19d6cc4
3 changed files with 258 additions and 371 deletions
-1
View File
@@ -133,7 +133,6 @@ extensions/lmstudio/src/setup.test.ts
extensions/lmstudio/src/setup.ts
extensions/matrix/src/cli.test.ts
extensions/matrix/src/matrix/actions/verification.test.ts
extensions/matrix/src/matrix/client/config.ts
extensions/matrix/src/matrix/monitor/events.test.ts
extensions/matrix/src/matrix/monitor/handler.test.ts
extensions/matrix/src/matrix/sdk.test.ts
@@ -556,7 +556,7 @@ describe("Matrix auth/config live surfaces", () => {
).toThrow(/Matrix account id "!!!" is invalid/i);
});
it("rejects explicitly selected disabled accounts instead of borrowing another account", () => {
it("rejects explicitly selected disabled accounts before resolving their secrets", () => {
const cfg = {
channels: {
matrix: {
@@ -566,7 +566,11 @@ describe("Matrix auth/config live surfaces", () => {
disabled: {
enabled: false,
homeserver: "https://disabled.example.org",
accessToken: "disabled-token",
accessToken: {
source: "env",
provider: "default",
id: "MATRIX_DISABLED_ACCESS_TOKEN",
},
},
},
},
+252 -368
View File
@@ -14,7 +14,10 @@ import {
requiresExplicitMatrixDefaultAccount,
resolveMatrixDefaultOrOnlyAccountId,
} from "../../account-selection.js";
import { resolveMatrixAccountStringValues } from "../../auth-precedence.js";
import {
resolveMatrixAccountStringValues,
type MatrixResolvedStringField,
} from "../../auth-precedence.js";
import { getMatrixScopedEnvVarNames } from "../../env-vars.js";
import type { CoreConfig } from "../../types.js";
import {
@@ -36,12 +39,7 @@ import { repairCurrentTokenStorageMetaDeviceId } from "./storage.js";
import type { MatrixAuth, MatrixResolvedConfig } from "./types.js";
import { resolveValidatedMatrixHomeserverUrl } from "./url-validation.js";
type MatrixAuthClientDeps = {
MatrixClient: typeof import("../sdk.js").MatrixClient;
ensureMatrixSdkLoggingConfigured: typeof import("./logging.js").ensureMatrixSdkLoggingConfigured;
};
const loadDefaultMatrixAuthClientDeps = createLazyRuntimeModule(() =>
const loadMatrixAuthClientDeps = createLazyRuntimeModule(() =>
Promise.all([import("../sdk.js"), import("./logging.js")]).then(([sdkModule, loggingModule]) => ({
MatrixClient: sdkModule.MatrixClient,
ensureMatrixSdkLoggingConfigured: loggingModule.ensureMatrixSdkLoggingConfigured,
@@ -50,25 +48,16 @@ const loadDefaultMatrixAuthClientDeps = createLazyRuntimeModule(() =>
const MATRIX_AUTH_REQUEST_RETRY_RE =
/\b(fetch failed|econnreset|econnrefused|enotfound|etimedout|ehostunreach|enetunreach|eai_again|und_err_|socket hang up|network|headers timeout|body timeout|connect timeout)\b/i;
async function loadMatrixAuthClientDeps(): Promise<MatrixAuthClientDeps> {
return await loadDefaultMatrixAuthClientDeps();
}
const loadMatrixCredentialsReadDeps = createLazyRuntimeModule(() =>
import("../credentials-read.js").then((credentialsReadModule) => ({
loadMatrixCredentials: credentialsReadModule.loadMatrixCredentials,
credentialsMatchConfig: credentialsReadModule.credentialsMatchConfig,
})),
const loadMatrixCredentialsReadDeps = createLazyRuntimeModule(
() => import("../credentials-read.js"),
);
const loadMatrixCredentialsWriteRuntime = createLazyRuntimeModule(
() => import("../credentials-write.runtime.js"),
);
const loadMatrixSecretInputDeps = createLazyRuntimeModule(() =>
import("./config-secret-input.runtime.js").then((runtime) => ({
resolveConfiguredSecretInputString: runtime.resolveConfiguredSecretInputString,
})),
const loadMatrixSecretInputDeps = createLazyRuntimeModule(
() => import("./config-secret-input.runtime.js"),
);
function shouldRetryMatrixAuthRequest(err: unknown): boolean {
@@ -109,6 +98,9 @@ async function retryMatrixAuthRequest<T>(
});
}
type MatrixWhoamiIdentity = { user_id?: string; device_id?: string };
type MatrixLoginResponse = { access_token?: string; user_id?: string; device_id?: string };
async function fetchMatrixWhoamiIdentity(params: {
homeserver: string;
accessToken: string;
@@ -116,10 +108,7 @@ async function fetchMatrixWhoamiIdentity(params: {
ssrfPolicy?: MatrixResolvedConfig["ssrfPolicy"];
dispatcherPolicy?: PinnedDispatcherPolicy;
signal?: AbortSignal;
}): Promise<{
user_id?: string;
device_id?: string;
}> {
}): Promise<MatrixWhoamiIdentity> {
const { MatrixClient, ensureMatrixSdkLoggingConfigured } = await loadMatrixAuthClientDeps();
ensureMatrixSdkLoggingConfigured();
const tempClient = new MatrixClient(params.homeserver, params.accessToken, {
@@ -127,204 +116,101 @@ async function fetchMatrixWhoamiIdentity(params: {
ssrfPolicy: params.ssrfPolicy,
dispatcherPolicy: params.dispatcherPolicy,
});
return (await retryMatrixAuthRequest(
return await retryMatrixAuthRequest(
"matrix auth whoami",
async () => {
return (await tempClient.doRequest("GET", "/_matrix/client/v3/account/whoami")) as {
user_id?: string;
device_id?: string;
};
},
async () =>
(await tempClient.doRequest(
"GET",
"/_matrix/client/v3/account/whoami",
)) as MatrixWhoamiIdentity,
params.signal,
)) as {
user_id?: string;
device_id?: string;
};
}
function readEnvSecretRefFallback(params: {
value: unknown;
env?: NodeJS.ProcessEnv;
config?: Pick<CoreConfig, "secrets">;
}): string | undefined {
const ref = coerceSecretRef(params.value, params.config?.secrets?.defaults);
if (!ref || ref.source !== "env" || !params.env) {
return undefined;
}
const providerConfig = params.config?.secrets?.providers?.[ref.provider];
if (providerConfig) {
if (providerConfig.source !== "env") {
throw new Error(
`Secret provider "${ref.provider}" has source "${providerConfig.source}" but ref requests "env".`,
);
}
if (providerConfig.allowlist && !providerConfig.allowlist.includes(ref.id)) {
throw new Error(
`Environment variable "${ref.id}" is not allowlisted in secrets.providers.${ref.provider}.allowlist.`,
);
}
} else if (ref.provider !== (params.config?.secrets?.defaults?.env?.trim() || "default")) {
throw new Error(
`Secret provider "${ref.provider}" is not configured (ref: ${ref.source}:${ref.provider}:${ref.id}).`,
);
}
const resolved = params.env[ref.id];
if (typeof resolved !== "string") {
return undefined;
}
const trimmed = resolved.trim();
return trimmed.length > 0 ? trimmed : undefined;
}
function clean(
value: unknown,
path: string,
opts?: {
env?: NodeJS.ProcessEnv;
config?: Pick<CoreConfig, "secrets">;
allowEnvSecretRefFallback?: boolean;
suppressSecretRef?: boolean;
},
): string {
const ref = coerceSecretRef(value, opts?.config?.secrets?.defaults);
if (opts?.suppressSecretRef && ref) {
return "";
}
const normalizedValue = opts?.allowEnvSecretRefFallback
? ref?.source === "env"
? (readEnvSecretRefFallback({
value,
env: opts.env,
config: opts.config,
}) ?? value)
: ref
? ""
: value
: value;
return (
normalizeResolvedSecretInputString({
value: normalizedValue,
path,
defaults: opts?.config?.secrets?.defaults,
}) ?? ""
);
}
type MatrixConfigStringField =
| "homeserver"
| "userId"
| "accessToken"
| "password"
| "deviceId"
| "deviceName";
const MATRIX_CONFIG_STRING_FIELDS = [
"homeserver",
"userId",
"accessToken",
"password",
"deviceId",
"deviceName",
] as const satisfies readonly MatrixResolvedStringField[];
type MatrixConfigStringField = (typeof MATRIX_CONFIG_STRING_FIELDS)[number];
const MATRIX_AUTH_SECRET_FIELDS = ["accessToken", "password"] as const;
type MatrixAuthSecretField = (typeof MATRIX_AUTH_SECRET_FIELDS)[number];
type MatrixConfiguredAuthInput = { value: unknown; path: string };
type MatrixAuthInputs = Readonly<Partial<Record<MatrixAuthSecretField, MatrixConfiguredAuthInput>>>;
function readMatrixEnvSecretRef(params: {
ref: NonNullable<ReturnType<typeof coerceSecretRef>>;
cfg: Pick<CoreConfig, "secrets">;
env: NodeJS.ProcessEnv;
}): string | undefined {
const provider = params.cfg.secrets?.providers?.[params.ref.provider];
if (provider) {
if (provider.source !== "env") {
throw new Error(
`Secret provider "${params.ref.provider}" has source "${provider.source}" but ref requests "env".`,
);
}
if (provider.allowlist && !provider.allowlist.includes(params.ref.id)) {
throw new Error(
`Environment variable "${params.ref.id}" is not allowlisted in secrets.providers.${params.ref.provider}.allowlist.`,
);
}
} else if (params.ref.provider !== (params.cfg.secrets?.defaults?.env?.trim() || "default")) {
throw new Error(
`Secret provider "${params.ref.provider}" is not configured (ref: env:${params.ref.provider}:${params.ref.id}).`,
);
}
return params.env[params.ref.id]?.trim() || undefined;
}
function readMatrixConfigString(params: {
value: unknown;
path: string;
cfg: Pick<CoreConfig, "secrets">;
env: NodeJS.ProcessEnv;
allowEnvSecretRef?: boolean;
suppressSecretRef?: boolean;
}): string {
const ref = coerceSecretRef(params.value, params.cfg.secrets?.defaults);
if (params.suppressSecretRef && ref) {
return "";
}
const value = params.allowEnvSecretRef
? ref?.source === "env"
? (readMatrixEnvSecretRef({ ref, cfg: params.cfg, env: params.env }) ?? params.value)
: ref
? ""
: params.value
: params.value;
return (
normalizeResolvedSecretInputString({
value,
path: params.path,
defaults: params.cfg.secrets?.defaults,
}) ?? ""
);
}
function resolveMatrixBaseConfigFieldPath(field: MatrixConfigStringField): string {
return `channels.matrix.${field}`;
}
function shouldAllowEnvSecretRefFallback(field: MatrixConfigStringField): boolean {
return field === "accessToken" || field === "password";
}
type MatrixAuthSecretField = "accessToken" | "password";
type MatrixConfiguredAuthInput = {
value: unknown;
path: string;
};
function hasConfiguredSecretInputValue(value: unknown, cfg: Pick<CoreConfig, "secrets">): boolean {
function hasConfiguredMatrixSecret(value: unknown, cfg: Pick<CoreConfig, "secrets">): boolean {
return (
(typeof value === "string" && value.trim().length > 0) ||
Boolean(coerceSecretRef(value, cfg.secrets?.defaults))
);
}
function hasConfiguredMatrixAccessTokenSource(params: {
cfg: CoreConfig;
env: NodeJS.ProcessEnv;
accountId: string;
}): boolean {
const normalizedAccountId = normalizeAccountId(params.accountId);
const account = findMatrixAccountConfig(params.cfg, normalizedAccountId) ?? {};
const scopedAccessTokenVar = getMatrixScopedEnvVarNames(normalizedAccountId).accessToken;
if (
hasConfiguredSecretInputValue(account.accessToken, params.cfg) ||
clean(params.env[scopedAccessTokenVar], scopedAccessTokenVar).length > 0
) {
return true;
}
if (normalizedAccountId !== DEFAULT_ACCOUNT_ID) {
return false;
}
const matrix = resolveMatrixBaseConfig(params.cfg);
return (
hasConfiguredSecretInputValue(matrix.accessToken, params.cfg) ||
clean(params.env.MATRIX_ACCESS_TOKEN, "MATRIX_ACCESS_TOKEN").length > 0
);
}
function resolveConfiguredMatrixAuthInput(params: {
cfg: CoreConfig;
env: NodeJS.ProcessEnv;
accountId: string;
field: MatrixAuthSecretField;
}): MatrixConfiguredAuthInput | undefined {
const normalizedAccountId = normalizeAccountId(params.accountId);
const account = findMatrixAccountConfig(params.cfg, normalizedAccountId) ?? {};
const accountValue = account[params.field];
if (accountValue !== undefined) {
return {
value: accountValue,
path: resolveMatrixConfigFieldPath(params.cfg, normalizedAccountId, params.field),
};
}
const scopedKeys = getMatrixScopedEnvVarNames(normalizedAccountId);
const scopedEnv = resolveScopedMatrixEnvConfig(normalizedAccountId, params.env);
const scopedValue = scopedEnv[params.field];
if (scopedValue !== undefined) {
return {
value: scopedValue,
path: params.field === "accessToken" ? scopedKeys.accessToken : scopedKeys.password,
};
}
if (normalizedAccountId !== DEFAULT_ACCOUNT_ID) {
return undefined;
}
const matrix = resolveMatrixBaseConfig(params.cfg);
const baseValue = matrix[params.field];
if (baseValue !== undefined) {
return {
value: baseValue,
path: resolveMatrixBaseConfigFieldPath(params.field),
};
}
const globalValue =
params.field === "accessToken" ? params.env.MATRIX_ACCESS_TOKEN : params.env.MATRIX_PASSWORD;
if (globalValue !== undefined) {
return {
value: globalValue,
path: params.field === "accessToken" ? "MATRIX_ACCESS_TOKEN" : "MATRIX_PASSWORD",
};
}
return undefined;
}
async function resolveConfiguredMatrixAuthSecretInput(params: {
cfg: CoreConfig;
env: NodeJS.ProcessEnv;
accountId: string;
field: MatrixAuthSecretField;
configured?: MatrixConfiguredAuthInput;
}): Promise<string | undefined> {
const configured = resolveConfiguredMatrixAuthInput(params);
const configured = params.configured;
if (!configured) {
return undefined;
}
@@ -355,42 +241,6 @@ async function resolveConfiguredMatrixAuthSecretInput(params: {
);
}
function readMatrixBaseConfigField(
matrix: ReturnType<typeof resolveMatrixBaseConfig>,
field: MatrixConfigStringField,
opts?: {
env?: NodeJS.ProcessEnv;
config?: Pick<CoreConfig, "secrets">;
suppressSecretRef?: boolean;
},
): string {
return clean(matrix[field], resolveMatrixBaseConfigFieldPath(field), {
env: opts?.env,
config: opts?.config,
allowEnvSecretRefFallback: shouldAllowEnvSecretRefFallback(field),
suppressSecretRef: opts?.suppressSecretRef,
});
}
function readMatrixAccountConfigField(
cfg: CoreConfig,
accountId: string,
account: Partial<Record<MatrixConfigStringField, unknown>>,
field: MatrixConfigStringField,
opts?: {
env?: NodeJS.ProcessEnv;
config?: Pick<CoreConfig, "secrets">;
suppressSecretRef?: boolean;
},
): string {
return clean(account[field], resolveMatrixConfigFieldPath(cfg, accountId, field), {
env: opts?.env,
config: opts?.config,
allowEnvSecretRefFallback: shouldAllowEnvSecretRefFallback(field),
suppressSecretRef: opts?.suppressSecretRef,
});
}
function clampMatrixInitialSyncLimit(value: unknown): number | undefined {
return resolveOptionalIntegerOption(value, { min: 0 });
}
@@ -417,6 +267,25 @@ function buildMatrixNetworkFields(params: {
};
}
function buildResolvedMatrixAuth(
resolved: MatrixResolvedConfig,
auth: Pick<
MatrixAuth,
"accountId" | "homeserver" | "userId" | "accessToken" | "password" | "deviceId"
>,
): MatrixAuth {
return {
...auth,
deviceName: resolved.deviceName,
initialSyncLimit: resolved.initialSyncLimit,
encryption: resolved.encryption,
...buildMatrixNetworkFields({
allowPrivateNetwork: resolved.allowPrivateNetwork,
dispatcherPolicy: resolved.dispatcherPolicy,
}),
};
}
export {
hasReadyMatrixEnvAuth,
resolveMatrixEnvAuthReadiness,
@@ -439,79 +308,111 @@ function hasScopedMatrixEnvConfig(accountId: string, env: NodeJS.ProcessEnv): bo
);
}
function readMatrixConfigStrings(params: {
cfg: CoreConfig;
env: NodeJS.ProcessEnv;
values: Partial<Record<MatrixConfigStringField, unknown>>;
path: (field: MatrixConfigStringField) => string;
suppressPasswordSecretRef: boolean;
}): Record<MatrixConfigStringField, string> {
return Object.fromEntries(
MATRIX_CONFIG_STRING_FIELDS.map((field) => [
field,
readMatrixConfigString({
value: params.values[field],
path: params.path(field),
cfg: params.cfg,
env: params.env,
allowEnvSecretRef: MATRIX_AUTH_SECRET_FIELDS.includes(field as MatrixAuthSecretField),
suppressSecretRef: field === "password" && params.suppressPasswordSecretRef,
}),
]),
) as Record<MatrixConfigStringField, string>;
}
function resolveMatrixAccountConfigSnapshot(
cfg: CoreConfig,
accountId: string,
env: NodeJS.ProcessEnv,
) {
const normalizedAccountId = normalizeAccountId(accountId);
const matrix = resolveMatrixBaseConfig(cfg);
const account = findMatrixAccountConfig(cfg, normalizedAccountId) ?? {};
const scopedKeys = getMatrixScopedEnvVarNames(normalizedAccountId);
const scopedEnv = resolveScopedMatrixEnvConfig(normalizedAccountId, env);
const globalEnv = resolveGlobalMatrixEnvConfig(env);
const authCandidates = (field: MatrixAuthSecretField): MatrixConfiguredAuthInput[] => [
{ value: account[field], path: resolveMatrixConfigFieldPath(cfg, accountId, field) },
{ value: scopedEnv[field], path: scopedKeys[field] },
...(normalizedAccountId === DEFAULT_ACCOUNT_ID
? [
{ value: matrix[field], path: resolveMatrixBaseConfigFieldPath(field) },
{
value: globalEnv[field],
path: field === "accessToken" ? "MATRIX_ACCESS_TOKEN" : "MATRIX_PASSWORD",
},
]
: []),
];
const accessTokenCandidates = authCandidates("accessToken");
const passwordCandidates = authCandidates("password");
const suppressPasswordSecretRef = accessTokenCandidates.some((source) =>
hasConfiguredMatrixSecret(source.value, cfg),
);
const resolvedStrings = resolveMatrixAccountStringValues({
accountId: normalizedAccountId,
account: readMatrixConfigStrings({
cfg,
env,
values: account,
path: (field) => resolveMatrixConfigFieldPath(cfg, normalizedAccountId, field),
suppressPasswordSecretRef,
}),
scopedEnv,
channel: readMatrixConfigStrings({
cfg,
env,
values: matrix,
path: resolveMatrixBaseConfigFieldPath,
suppressPasswordSecretRef,
}),
globalEnv,
});
const accountInitialSyncLimit = clampMatrixInitialSyncLimit(account.initialSyncLimit);
const allowPrivateNetwork =
isPrivateNetworkOptInEnabled(account) || isPrivateNetworkOptInEnabled(matrix)
? true
: undefined;
return {
resolved: {
homeserver: resolvedStrings.homeserver,
userId: resolvedStrings.userId,
accessToken: resolvedStrings.accessToken || undefined,
password: resolvedStrings.password || undefined,
deviceId: resolvedStrings.deviceId || undefined,
deviceName: resolvedStrings.deviceName || undefined,
initialSyncLimit:
accountInitialSyncLimit ?? clampMatrixInitialSyncLimit(matrix.initialSyncLimit),
encryption:
typeof account.encryption === "boolean" ? account.encryption : (matrix.encryption ?? false),
...buildMatrixNetworkFields({
allowPrivateNetwork,
proxy: account.proxy ?? matrix.proxy,
}),
},
authInputs: {
accessToken: accessTokenCandidates.find((source) => source.value !== undefined),
password: passwordCandidates.find((source) => source.value !== undefined),
} satisfies MatrixAuthInputs,
};
}
export function resolveMatrixConfigForAccount(
cfg: CoreConfig,
accountId: string,
env: NodeJS.ProcessEnv = process.env,
): MatrixResolvedConfig {
const matrix = resolveMatrixBaseConfig(cfg);
const account = findMatrixAccountConfig(cfg, accountId) ?? {};
const normalizedAccountId = normalizeAccountId(accountId);
const suppressInactivePasswordSecretRef = hasConfiguredMatrixAccessTokenSource({
cfg,
env,
accountId: normalizedAccountId,
});
const fieldReadOptions = {
env,
config: cfg,
};
const scopedEnv = resolveScopedMatrixEnvConfig(normalizedAccountId, env);
const globalEnv = resolveGlobalMatrixEnvConfig(env);
const accountField = (field: MatrixConfigStringField) =>
readMatrixAccountConfigField(cfg, normalizedAccountId, account, field, {
...fieldReadOptions,
suppressSecretRef: field === "password" ? suppressInactivePasswordSecretRef : undefined,
});
const resolvedStrings = resolveMatrixAccountStringValues({
accountId: normalizedAccountId,
account: {
homeserver: accountField("homeserver"),
userId: accountField("userId"),
accessToken: accountField("accessToken"),
password: accountField("password"),
deviceId: accountField("deviceId"),
deviceName: accountField("deviceName"),
},
scopedEnv,
channel: {
homeserver: readMatrixBaseConfigField(matrix, "homeserver", fieldReadOptions),
userId: readMatrixBaseConfigField(matrix, "userId", fieldReadOptions),
accessToken: readMatrixBaseConfigField(matrix, "accessToken", fieldReadOptions),
password: readMatrixBaseConfigField(matrix, "password", {
...fieldReadOptions,
suppressSecretRef: suppressInactivePasswordSecretRef,
}),
deviceId: readMatrixBaseConfigField(matrix, "deviceId", fieldReadOptions),
deviceName: readMatrixBaseConfigField(matrix, "deviceName", fieldReadOptions),
},
globalEnv,
});
const accountInitialSyncLimit = clampMatrixInitialSyncLimit(account.initialSyncLimit);
const initialSyncLimit =
accountInitialSyncLimit ?? clampMatrixInitialSyncLimit(matrix.initialSyncLimit);
const encryption =
typeof account.encryption === "boolean" ? account.encryption : (matrix.encryption ?? false);
const allowPrivateNetwork =
isPrivateNetworkOptInEnabled(account) || isPrivateNetworkOptInEnabled(matrix)
? true
: undefined;
return {
homeserver: resolvedStrings.homeserver,
userId: resolvedStrings.userId,
accessToken: resolvedStrings.accessToken || undefined,
password: resolvedStrings.password || undefined,
deviceId: resolvedStrings.deviceId || undefined,
deviceName: resolvedStrings.deviceName || undefined,
initialSyncLimit,
encryption,
...buildMatrixNetworkFields({
allowPrivateNetwork,
proxy: account.proxy ?? matrix.proxy,
}),
};
return resolveMatrixAccountConfigSnapshot(cfg, accountId, env).resolved;
}
function resolveImplicitMatrixAccountId(
@@ -524,16 +425,18 @@ function resolveImplicitMatrixAccountId(
return normalizeAccountId(resolveMatrixDefaultOrOnlyAccountId(cfg, env));
}
export function resolveMatrixAuthContext(params: {
cfg: CoreConfig;
env?: NodeJS.ProcessEnv;
accountId?: string | null;
}): {
type MatrixAuthContext = {
cfg: CoreConfig;
env: NodeJS.ProcessEnv;
accountId: string;
resolved: MatrixResolvedConfig;
} {
};
function resolveMatrixAuthState(params: {
cfg: CoreConfig;
env?: NodeJS.ProcessEnv;
accountId?: string | null;
}): { context: MatrixAuthContext; authInputs: MatrixAuthInputs } {
const cfg = requireRuntimeConfig(params.cfg, "Matrix auth context") as CoreConfig;
const env = params?.env ?? process.env;
const requestedAccountId = params?.accountId?.trim();
@@ -562,16 +465,26 @@ export function resolveMatrixAuthContext(params: {
if (matrix.enabled === false || account?.enabled === false) {
throw new Error(`Matrix account "${effectiveAccountId}" is disabled.`);
}
const resolved = resolveMatrixConfigForAccount(cfg, effectiveAccountId, env);
const snapshot = resolveMatrixAccountConfigSnapshot(cfg, effectiveAccountId, env);
return {
cfg,
env,
accountId: effectiveAccountId,
resolved,
context: {
cfg,
env,
accountId: effectiveAccountId,
resolved: snapshot.resolved,
},
authInputs: snapshot.authInputs,
};
}
export function resolveMatrixAuthContext(params: {
cfg: CoreConfig;
env?: NodeJS.ProcessEnv;
accountId?: string | null;
}): MatrixAuthContext {
return resolveMatrixAuthState(params).context;
}
export async function resolveMatrixAuth(params?: {
cfg?: CoreConfig;
env?: NodeJS.ProcessEnv;
@@ -582,17 +495,17 @@ export async function resolveMatrixAuth(params?: {
"Matrix auth requires a resolved runtime config. Load and resolve config at the command or gateway boundary, then pass cfg through the runtime path.",
);
}
const { cfg, env, accountId, resolved } = resolveMatrixAuthContext({
const { context, authInputs } = resolveMatrixAuthState({
cfg: params.cfg,
env: params.env,
accountId: params.accountId,
});
const { cfg, env, accountId, resolved } = context;
const accessToken =
(await resolveConfiguredMatrixAuthSecretInput({
cfg,
env,
accountId,
field: "accessToken",
configured: authInputs.accessToken,
})) ?? resolved.accessToken;
const tokenAuthPassword = resolved.password;
const homeserver = await resolveValidatedMatrixHomeserverUrl(resolved.homeserver, {
@@ -657,41 +570,27 @@ export async function resolveMatrixAuth(params?: {
const { touchMatrixCredentials } = await loadMatrixCredentialsWriteRuntime();
await touchMatrixCredentials(env, accountId);
}
return {
return buildResolvedMatrixAuth(resolved, {
accountId,
homeserver,
userId,
accessToken,
password: tokenAuthPassword,
deviceId: knownDeviceId,
deviceName: resolved.deviceName,
initialSyncLimit: resolved.initialSyncLimit,
encryption: resolved.encryption,
...buildMatrixNetworkFields({
allowPrivateNetwork: resolved.allowPrivateNetwork,
dispatcherPolicy: resolved.dispatcherPolicy,
}),
};
});
}
if (cachedCredentials) {
const { touchMatrixCredentials } = await loadMatrixCredentialsWriteRuntime();
await touchMatrixCredentials(env, accountId);
return {
return buildResolvedMatrixAuth(resolved, {
accountId,
homeserver: cachedCredentials.homeserver,
userId: cachedCredentials.userId,
accessToken: cachedCredentials.accessToken,
password: tokenAuthPassword,
deviceId: cachedCredentials.deviceId || resolved.deviceId,
deviceName: resolved.deviceName,
initialSyncLimit: resolved.initialSyncLimit,
encryption: resolved.encryption,
...buildMatrixNetworkFields({
allowPrivateNetwork: resolved.allowPrivateNetwork,
dispatcherPolicy: resolved.dispatcherPolicy,
}),
};
});
}
if (!resolved.userId) {
@@ -702,8 +601,7 @@ export async function resolveMatrixAuth(params?: {
(await resolveConfiguredMatrixAuthSecretInput({
cfg,
env,
accountId,
field: "password",
configured: authInputs.password,
})) ?? resolved.password;
if (!password) {
throw new Error(
@@ -718,44 +616,31 @@ export async function resolveMatrixAuth(params?: {
ssrfPolicy: resolved.ssrfPolicy,
dispatcherPolicy: resolved.dispatcherPolicy,
});
const login = (await retryMatrixAuthRequest("matrix auth login", async () => {
return (await loginClient.doRequest("POST", "/_matrix/client/v3/login", undefined, {
type: "m.login.password",
identifier: { type: "m.id.user", user: resolved.userId },
password,
device_id: resolved.deviceId,
initial_device_display_name: resolved.deviceName ?? "OpenClaw Gateway",
})) as {
access_token?: string;
user_id?: string;
device_id?: string;
};
})) as {
access_token?: string;
user_id?: string;
device_id?: string;
};
const login = await retryMatrixAuthRequest(
"matrix auth login",
async () =>
(await loginClient.doRequest("POST", "/_matrix/client/v3/login", undefined, {
type: "m.login.password",
identifier: { type: "m.id.user", user: resolved.userId },
password,
device_id: resolved.deviceId,
initial_device_display_name: resolved.deviceName ?? "OpenClaw Gateway",
})) as MatrixLoginResponse,
);
const loginAccessToken = login.access_token?.trim();
if (!loginAccessToken) {
throw new Error("Matrix login did not return an access token");
}
const auth: MatrixAuth = {
const auth = buildResolvedMatrixAuth(resolved, {
accountId,
homeserver,
userId: login.user_id ?? resolved.userId,
accessToken: loginAccessToken,
password,
deviceId: login.device_id ?? resolved.deviceId,
deviceName: resolved.deviceName,
initialSyncLimit: resolved.initialSyncLimit,
encryption: resolved.encryption,
...buildMatrixNetworkFields({
allowPrivateNetwork: resolved.allowPrivateNetwork,
dispatcherPolicy: resolved.dispatcherPolicy,
}),
};
});
const { saveMatrixCredentials } = await loadMatrixCredentialsWriteRuntime();
await saveMatrixCredentials(
@@ -850,4 +735,3 @@ export async function backfillMatrixAuthDeviceIdAfterStartup(params: {
);
return saved === "saved" ? deviceId : undefined;
}
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */