Enroll Passkey PRF key on first login recovery phase
This commit is contained in:
@@ -147,6 +147,7 @@ export interface LoginResult {
|
||||
encryptedMasterKeyRecTag: string
|
||||
userId: string
|
||||
username: string
|
||||
prfFirst?: ArrayBuffer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +232,8 @@ export async function loginUser(username?: string): Promise<LoginResult> {
|
||||
encryptedMasterKeyRecIv: result.encryptedMasterKeyRecIv,
|
||||
encryptedMasterKeyRecTag: result.encryptedMasterKeyRecTag,
|
||||
userId: result.userId,
|
||||
username: resolvedUsername
|
||||
username: resolvedUsername,
|
||||
prfFirst: prfResults?.results?.first
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,6 +247,7 @@ export async function completeLoginWithRecovery(
|
||||
encryptedMasterKeyRecIv: string
|
||||
encryptedMasterKeyRecTag: string
|
||||
userId: string
|
||||
prfFirst?: ArrayBuffer
|
||||
}
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
@@ -255,6 +258,32 @@ export async function completeLoginWithRecovery(
|
||||
encryptedPayloads.encryptedMasterKeyRecTag,
|
||||
recoveryKey
|
||||
)
|
||||
|
||||
// If PRF results are available from the login challenge, enroll them now
|
||||
if (encryptedPayloads.prfFirst) {
|
||||
try {
|
||||
const prfKey = await deriveKeyFromPrf(encryptedPayloads.prfFirst)
|
||||
const encryptedPrf = await encryptBuffer(decryptedMaster, prfKey)
|
||||
const enrollRes = await fetch(`${API_BASE}/enroll-prf`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-User-Id': encryptedPayloads.userId
|
||||
},
|
||||
body: JSON.stringify({
|
||||
encryptedMasterKeyPrf: encryptedPrf.ciphertext,
|
||||
encryptedMasterKeyPrfIv: encryptedPrf.iv,
|
||||
encryptedMasterKeyPrfTag: encryptedPrf.tag
|
||||
})
|
||||
})
|
||||
if (!enrollRes.ok) {
|
||||
console.warn('Server rejected PRF enrollment')
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to encrypt/enroll master key with PRF key:', err)
|
||||
}
|
||||
}
|
||||
|
||||
setActiveMasterKey(decryptedMaster)
|
||||
localStorage.setItem('active_username', username)
|
||||
localStorage.setItem('active_userid', encryptedPayloads.userId)
|
||||
|
||||
@@ -253,4 +253,41 @@ router.delete('/delete-account', async (req: any, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 6. Enroll PRF encrypted master key
|
||||
router.post('/enroll-prf', async (req: any, res) => {
|
||||
try {
|
||||
const userId = req.headers['x-user-id']
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: 'Unauthorized: X-User-Id header missing' })
|
||||
}
|
||||
|
||||
const { encryptedMasterKeyPrf, encryptedMasterKeyPrfIv, encryptedMasterKeyPrfTag } = req.body
|
||||
if (!encryptedMasterKeyPrf || !encryptedMasterKeyPrfIv || !encryptedMasterKeyPrfTag) {
|
||||
return res.status(400).json({ error: 'Missing required PRF key fields' })
|
||||
}
|
||||
|
||||
if (
|
||||
typeof encryptedMasterKeyPrf !== 'string' ||
|
||||
typeof encryptedMasterKeyPrfIv !== 'string' ||
|
||||
typeof encryptedMasterKeyPrfTag !== 'string'
|
||||
) {
|
||||
return res.status(400).json({ error: 'Invalid PRF key fields format' })
|
||||
}
|
||||
|
||||
await prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: {
|
||||
encryptedMasterKeyPrf,
|
||||
encryptedMasterKeyPrfIv,
|
||||
encryptedMasterKeyPrfTag
|
||||
}
|
||||
})
|
||||
|
||||
return res.json({ success: true })
|
||||
} catch (error: any) {
|
||||
console.error('Error enrolling PRF key:', error)
|
||||
return res.status(500).json({ error: error.message || 'Internal server error' })
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
Reference in New Issue
Block a user