Reduce image size, update feature requset yamls and fix OIDC
This commit is contained in:
@@ -75,12 +75,8 @@ async function verifyOIDCToken(
|
||||
);
|
||||
}
|
||||
} else {
|
||||
authLogger.error(
|
||||
`JWKS fetch failed from ${url}: ${response.status} ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
authLogger.error(`JWKS fetch error from ${url}:`, error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -117,7 +113,6 @@ async function verifyOIDCToken(
|
||||
|
||||
return payload;
|
||||
} catch (error) {
|
||||
authLogger.error("OIDC token verification failed:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -655,10 +650,6 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
config.client_id,
|
||||
);
|
||||
} catch (error) {
|
||||
authLogger.error(
|
||||
"OIDC token verification failed, trying userinfo endpoints",
|
||||
error,
|
||||
);
|
||||
try {
|
||||
const parts = tokenData.id_token.split(".");
|
||||
if (parts.length === 3) {
|
||||
@@ -767,6 +758,23 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
scopes: config.scopes,
|
||||
});
|
||||
|
||||
try {
|
||||
await authManager.registerOIDCUser(id);
|
||||
} catch (encryptionError) {
|
||||
await db.delete(users).where(eq(users.id, id));
|
||||
authLogger.error(
|
||||
"Failed to setup OIDC user encryption, user creation rolled back",
|
||||
encryptionError,
|
||||
{
|
||||
operation: "oidc_user_create_encryption_failed",
|
||||
userId: id,
|
||||
},
|
||||
);
|
||||
return res.status(500).json({
|
||||
error: "Failed to setup user security - user creation cancelled",
|
||||
});
|
||||
}
|
||||
|
||||
user = await db.select().from(users).where(eq(users.id, id));
|
||||
} else {
|
||||
await db
|
||||
@@ -779,6 +787,15 @@ router.get("/oidc/callback", async (req, res) => {
|
||||
|
||||
const userRecord = user[0];
|
||||
|
||||
try {
|
||||
await authManager.authenticateOIDCUser(userRecord.id);
|
||||
} catch (setupError) {
|
||||
authLogger.error("Failed to setup OIDC user encryption", setupError, {
|
||||
operation: "oidc_user_encryption_setup_failed",
|
||||
userId: userRecord.id,
|
||||
});
|
||||
}
|
||||
|
||||
const token = await authManager.generateJWTToken(userRecord.id, {
|
||||
expiresIn: "50d",
|
||||
});
|
||||
|
||||
@@ -53,6 +53,20 @@ class AuthManager {
|
||||
await this.userCrypto.setupUserEncryption(userId, password);
|
||||
}
|
||||
|
||||
async registerOIDCUser(userId: string): Promise<void> {
|
||||
await this.userCrypto.setupOIDCUserEncryption(userId);
|
||||
}
|
||||
|
||||
async authenticateOIDCUser(userId: string): Promise<boolean> {
|
||||
const authenticated = await this.userCrypto.authenticateOIDCUser(userId);
|
||||
|
||||
if (authenticated) {
|
||||
await this.performLazyEncryptionMigration(userId);
|
||||
}
|
||||
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
async authenticateUser(userId: string, password: string): Promise<boolean> {
|
||||
const authenticated = await this.userCrypto.authenticateUser(
|
||||
userId,
|
||||
|
||||
@@ -69,6 +69,19 @@ class UserCrypto {
|
||||
DEK.fill(0);
|
||||
}
|
||||
|
||||
async setupOIDCUserEncryption(userId: string): Promise<void> {
|
||||
const DEK = crypto.randomBytes(UserCrypto.DEK_LENGTH);
|
||||
|
||||
const now = Date.now();
|
||||
this.userSessions.set(userId, {
|
||||
dataKey: Buffer.from(DEK),
|
||||
lastActivity: now,
|
||||
expiresAt: now + UserCrypto.SESSION_DURATION,
|
||||
});
|
||||
|
||||
DEK.fill(0);
|
||||
}
|
||||
|
||||
async authenticateUser(userId: string, password: string): Promise<boolean> {
|
||||
try {
|
||||
const kekSalt = await this.getKEKSalt(userId);
|
||||
@@ -119,6 +132,52 @@ class UserCrypto {
|
||||
}
|
||||
}
|
||||
|
||||
async authenticateOIDCUser(userId: string): Promise<boolean> {
|
||||
try {
|
||||
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);
|
||||
if (!encryptedDEK) {
|
||||
systemKey.fill(0);
|
||||
await this.setupOIDCUserEncryption(userId);
|
||||
return true;
|
||||
}
|
||||
|
||||
const DEK = this.decryptDEK(encryptedDEK, systemKey);
|
||||
systemKey.fill(0);
|
||||
|
||||
if (!DEK || DEK.length === 0) {
|
||||
await this.setupOIDCUserEncryption(userId);
|
||||
return true;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
const oldSession = this.userSessions.get(userId);
|
||||
if (oldSession) {
|
||||
oldSession.dataKey.fill(0);
|
||||
}
|
||||
|
||||
this.userSessions.set(userId, {
|
||||
dataKey: Buffer.from(DEK),
|
||||
lastActivity: now,
|
||||
expiresAt: now + UserCrypto.SESSION_DURATION,
|
||||
});
|
||||
|
||||
DEK.fill(0);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
await this.setupOIDCUserEncryption(userId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
getUserDataKey(userId: string): Buffer | null {
|
||||
const session = this.userSessions.get(userId);
|
||||
if (!session) {
|
||||
@@ -259,6 +318,18 @@ class UserCrypto {
|
||||
);
|
||||
}
|
||||
|
||||
private deriveOIDCSystemKey(userId: string): Buffer {
|
||||
const systemSecret = process.env.OIDC_SYSTEM_SECRET || "termix-oidc-system-secret-default";
|
||||
const salt = Buffer.from(userId, "utf8");
|
||||
return crypto.pbkdf2Sync(
|
||||
systemSecret,
|
||||
salt,
|
||||
100000,
|
||||
UserCrypto.KEK_LENGTH,
|
||||
"sha256",
|
||||
);
|
||||
}
|
||||
|
||||
private encryptDEK(dek: Buffer, kek: Buffer): EncryptedDEK {
|
||||
const iv = crypto.randomBytes(16);
|
||||
const cipher = crypto.createCipheriv("aes-256-gcm", kek, iv);
|
||||
|
||||
@@ -428,12 +428,10 @@ export function HomepageAuth({
|
||||
return;
|
||||
}
|
||||
|
||||
if (success && token) {
|
||||
if (success) {
|
||||
setOidcLoading(true);
|
||||
setError(null);
|
||||
|
||||
// JWT token is now automatically set as HttpOnly cookie by backend
|
||||
console.log("OIDC login successful - JWT set as secure HttpOnly cookie");
|
||||
getUserInfo()
|
||||
.then((meRes) => {
|
||||
setInternalLoggedIn(true);
|
||||
@@ -455,13 +453,11 @@ export function HomepageAuth({
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t("errors.failedUserInfo"));
|
||||
setInternalLoggedIn(false);
|
||||
setLoggedIn(false);
|
||||
setIsAdmin(false);
|
||||
setUsername(null);
|
||||
setUserId(null);
|
||||
// HttpOnly cookies cannot be cleared from JavaScript - backend handles this
|
||||
window.history.replaceState(
|
||||
{},
|
||||
document.title,
|
||||
|
||||
Reference in New Issue
Block a user