mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-07-16 05:13:36 +00:00
feat(auth): opt-in OIDC DEK unlock for API-key requests (ALLOW_APIKEY_DATA_UNLOCK) (#1064)
* chore: fix release workflow to merge docs branch
* fix: svg donation generator push fail
* fix: svg donation generator push fail
* Update termix.rb
* fix: svg donation generator push fail
* chore: move donation badge to badges branch to avoid ruleset conflicts
* chore: remove unneeded token from donation badge workflow
* chore: debug donation badge commit step
* fix: escape < character in donation SVG
* fix: point donation badge to badges branch
* chore: remove unused donation badge svg from main
* Add Rack Genius logo to README
Added Rack Genius logo to the README.
* chore: improve donation goal svg generator to include stablecoins
* chore: donation goal generator syntax error
* chore: donation goal generator incorrect docs url usage
* chore: donation bar reporting wrong result
* feat: add Open File Manager to tab right-click menu (#1046)
* Revert "feat: add Open File Manager to tab right-click menu (#1046)" (#1050)
This reverts commit 0712fdd731.
* Remove donation badge from README
Removed donation badge from README.
* Delete .github/workflows/donation-goal.yml
* feat(auth): opt-in OIDC DEK unlock for API-key requests
API keys authenticate but cannot touch the encrypted credential/host store
('User data not unlocked') unless the user has a live interactive session,
making them unusable for headless automation. For OIDC users the DEK is
server-derivable (deriveOIDCSystemKey), so handleApiKeyAuth can unlock it
without a password.
Gated behind ALLOW_APIKEY_DATA_UNLOCK (default off) because enabling it widens
the blast radius of a leaked API key. OIDC-only; password users are untouched.
Refs #1063
---------
Co-authored-by: LukeGus <bugattiguy527@gmail.com>
Co-authored-by: Luke Gustafson <88517757+LukeGus@users.noreply.github.com>
Co-authored-by: Sankeerth Nara <sankeerthnara@gmail.com>
Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com>
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user