Fix OIDC credential persistence issue
The issue was that OIDC users were getting a new random Data Encryption Key (DEK) on every login, which made previously encrypted credentials inaccessible. Changes: - Modified setupOIDCUserEncryption() to persist the DEK encrypted with a system-derived key - Updated authenticateOIDCUser() to properly retrieve and use the persisted DEK - Ensured OIDC users now have the same encryption persistence as password-based users This fix ensures that credentials created by OIDC users remain accessible across multiple login sessions.
This commit is contained in:
@@ -70,7 +70,34 @@ class UserCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setupOIDCUserEncryption(userId: string): Promise<void> {
|
async setupOIDCUserEncryption(userId: string): Promise<void> {
|
||||||
const DEK = crypto.randomBytes(UserCrypto.DEK_LENGTH);
|
// Check if DEK already exists for this OIDC user
|
||||||
|
const existingKEKSalt = await this.getKEKSalt(userId);
|
||||||
|
const existingEncryptedDEK = await this.getEncryptedDEK(userId);
|
||||||
|
|
||||||
|
let DEK: Buffer;
|
||||||
|
|
||||||
|
if (existingKEKSalt && existingEncryptedDEK) {
|
||||||
|
// User already has a persisted DEK, retrieve it
|
||||||
|
const systemKey = this.deriveOIDCSystemKey(userId);
|
||||||
|
DEK = this.decryptDEK(existingEncryptedDEK, systemKey);
|
||||||
|
systemKey.fill(0);
|
||||||
|
} else {
|
||||||
|
// First time setup - create and persist new DEK
|
||||||
|
DEK = crypto.randomBytes(UserCrypto.DEK_LENGTH);
|
||||||
|
|
||||||
|
// Generate a KEK salt for OIDC user (using a deterministic approach)
|
||||||
|
const kekSalt = await this.generateKEKSalt();
|
||||||
|
await this.storeKEKSalt(userId, kekSalt);
|
||||||
|
|
||||||
|
// Derive system key for OIDC user
|
||||||
|
const systemKey = this.deriveOIDCSystemKey(userId);
|
||||||
|
|
||||||
|
// Encrypt and store the DEK
|
||||||
|
const encryptedDEK = this.encryptDEK(DEK, systemKey);
|
||||||
|
await this.storeEncryptedDEK(userId, encryptedDEK);
|
||||||
|
|
||||||
|
systemKey.fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
this.userSessions.set(userId, {
|
this.userSessions.set(userId, {
|
||||||
@@ -135,34 +162,34 @@ class UserCrypto {
|
|||||||
async authenticateOIDCUser(userId: string): Promise<boolean> {
|
async authenticateOIDCUser(userId: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const kekSalt = await this.getKEKSalt(userId);
|
const kekSalt = await this.getKEKSalt(userId);
|
||||||
if (!kekSalt) {
|
|
||||||
await this.setupOIDCUserEncryption(userId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const systemKey = this.deriveOIDCSystemKey(userId);
|
|
||||||
const encryptedDEK = await this.getEncryptedDEK(userId);
|
const encryptedDEK = await this.getEncryptedDEK(userId);
|
||||||
if (!encryptedDEK) {
|
|
||||||
systemKey.fill(0);
|
if (!kekSalt || !encryptedDEK) {
|
||||||
|
// First time login or missing encryption data - set up encryption
|
||||||
await this.setupOIDCUserEncryption(userId);
|
await this.setupOIDCUserEncryption(userId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve persisted DEK
|
||||||
|
const systemKey = this.deriveOIDCSystemKey(userId);
|
||||||
const DEK = this.decryptDEK(encryptedDEK, systemKey);
|
const DEK = this.decryptDEK(encryptedDEK, systemKey);
|
||||||
systemKey.fill(0);
|
systemKey.fill(0);
|
||||||
|
|
||||||
if (!DEK || DEK.length === 0) {
|
if (!DEK || DEK.length === 0) {
|
||||||
|
// DEK decryption failed - recreate encryption
|
||||||
await this.setupOIDCUserEncryption(userId);
|
await this.setupOIDCUserEncryption(userId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Clear any existing session
|
||||||
const oldSession = this.userSessions.get(userId);
|
const oldSession = this.userSessions.get(userId);
|
||||||
if (oldSession) {
|
if (oldSession) {
|
||||||
oldSession.dataKey.fill(0);
|
oldSession.dataKey.fill(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create new session with the persisted DEK
|
||||||
this.userSessions.set(userId, {
|
this.userSessions.set(userId, {
|
||||||
dataKey: Buffer.from(DEK),
|
dataKey: Buffer.from(DEK),
|
||||||
lastActivity: now,
|
lastActivity: now,
|
||||||
@@ -173,6 +200,7 @@ class UserCrypto {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// On error, set up fresh encryption
|
||||||
await this.setupOIDCUserEncryption(userId);
|
await this.setupOIDCUserEncryption(userId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user