fix: harden login for MLPS requirements

This commit is contained in:
2026-05-14 11:05:20 +08:00
parent ecf3ee1ef0
commit 879677516f
39 changed files with 1230 additions and 71 deletions
+29 -5
View File
@@ -1,7 +1,8 @@
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import { http, resetUnauthorizedHandling } from '@/lib/http'
import { http, OpsApiError, resetUnauthorizedHandling } from '@/lib/http'
import { browserSupportsPasswordCipher, encryptPasswordForLogin } from '@/lib/password-cipher'
import {
type OperatorView,
clearStoredSession,
@@ -17,6 +18,12 @@ interface LoginResponse {
operator: OperatorView
}
interface PasswordPublicKeyResponse {
key_id: string
algorithm: 'RSA-OAEP-256'
public_key: string
}
export const useAuthStore = defineStore('ops-auth', () => {
const accessToken = ref<string | null>(null)
const expiresAt = ref<number | null>(null)
@@ -41,11 +48,9 @@ export const useAuthStore = defineStore('ops-auth', () => {
}
const payload = { username, password, remember }
const loginPayload = await buildSecureLoginPayload(username, password)
loginPromise = http
.post<LoginResponse>('/auth/login', {
username: payload.username,
password: payload.password,
})
.post<LoginResponse>('/auth/login', loginPayload)
.then((result) => {
resetUnauthorizedHandling()
accessToken.value = result.access_token
@@ -69,6 +74,25 @@ export const useAuthStore = defineStore('ops-auth', () => {
return loginPromise
}
async function buildSecureLoginPayload(
username: string,
password: string,
): Promise<{ username: string; password?: string; encrypted_password?: string; password_key_id?: string }> {
if (!browserSupportsPasswordCipher()) {
return { username, password }
}
try {
const key = await http.get<PasswordPublicKeyResponse>('/auth/password-key')
const encrypted = await encryptPasswordForLogin(password, key)
return { username, ...encrypted }
} catch (error) {
if (error instanceof OpsApiError && error.status === 404) {
return { username, password }
}
throw error
}
}
async function refreshSelf(): Promise<void> {
if (!accessToken.value) return
try {