diff --git a/README.md b/README.md index e1ac7b30..1bff5ab4 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,6 @@ Termix is free and open source. If you find it useful, consider [donating](https://donate.termix.site/) to help cover server costs and development time. -Monthly donation goal -
Termix Banner @@ -392,6 +390,10 @@ See [Projects](https://github.com/orgs/Termix-SSH/projects/5) for all planned fe AWS +    + + AWS + diff --git a/src/backend/utils/auth-manager.ts b/src/backend/utils/auth-manager.ts index 9c73a7f8..d730379d 100644 --- a/src/backend/utils/auth-manager.ts +++ b/src/backend/utils/auth-manager.ts @@ -881,6 +881,41 @@ class AuthManager { }); }); + // Auto-unlock the per-user Data Encryption Key for OIDC-backed users so + // API-key (headless / automation) requests can access encrypted + // credentials/hosts. Without this, every encrypted-table op throws + // "User data not unlocked" because the API-key path never established a + // DEK session. OIDC DEKs are wrapped with a server-derived system key + // (deriveOIDCSystemKey), so no user password is required. Password-based + // users are deliberately NOT unlocked here (their KEK is password-derived + // and cannot be reproduced server-side). + // + // Opt-in and OFF by default: enabling this widens the blast radius of a + // leaked API key (it can then read that user's stored SSH secrets in + // clear text), so operators must turn it on explicitly. + if ( + process.env.ALLOW_APIKEY_DATA_UNLOCK === "true" && + !this.userCrypto.isUserUnlocked(matchedKey.userId) + ) { + try { + const { users } = await import("../database/db/schema.js"); + const oidcRows = await db + .select() + .from(users) + .where(eq(users.id, matchedKey.userId)) + .limit(1); + if (oidcRows[0]?.isOidc) { + await this.authenticateOIDCUser(matchedKey.userId); + } + } catch (err) { + databaseLogger.warn("API-key OIDC auto-unlock failed", { + operation: "api_key_oidc_unlock_failed", + userId: matchedKey.userId, + error: err instanceof Error ? err.message : "Unknown", + }); + } + } + req.userId = matchedKey.userId; req.apiKeyId = matchedKey.id; next();