Refactor to get user info from a single endpoint

This commit is contained in:
Leo Vasanko 2025-07-14 12:30:10 -06:00
parent 3567b7802b
commit 19bcddca30
4 changed files with 21 additions and 70 deletions

View File

@ -17,12 +17,12 @@
<div v-if="authStore.isLoading"> <div v-if="authStore.isLoading">
<p>Loading credentials...</p> <p>Loading credentials...</p>
</div> </div>
<div v-else-if="userCredentialsData.credentials.length === 0"> <div v-else-if="authStore.currentCredentials.length === 0">
<p>No passkeys found.</p> <p>No passkeys found.</p>
</div> </div>
<div v-else> <div v-else>
<div <div
v-for="credential in userCredentialsData.credentials" v-for="credential in authStore.currentCredentials"
:key="credential.credential_id" :key="credential.credential_id"
:class="['credential-item', { 'current-session': credential.is_current_session }]" :class="['credential-item', { 'current-session': credential.is_current_session }]"
> >
@ -84,36 +84,21 @@ import { formatDate } from '@/utils/helpers'
import { registerCredential } from '@/utils/passkey' import { registerCredential } from '@/utils/passkey'
const authStore = useAuthStore() const authStore = useAuthStore()
const currentCredentials = ref([])
const userCredentialsData = ref({ credentials: [], aaguid_info: {} })
const updateInterval = ref(null) const updateInterval = ref(null)
onMounted(async () => { onMounted(async () => {
try { try {
await authStore.loadUserInfo() await authStore.loadUserInfo()
currentCredentials.value = await authStore.loadCredentials()
} catch (error) { } catch (error) {
authStore.showMessage(`Failed to load user info: ${error.message}`, 'error') authStore.showMessage(`Failed to load user info: ${error.message}`, 'error')
authStore.currentView = 'login' authStore.currentView = 'login'
return return
} }
// Fetch user credentials from the server
try {
const response = await fetch('/auth/user-credentials')
const result = await response.json()
console.log('Fetch Response:', result) // Log the entire response
if (result.error) throw new Error(result.error)
Object.assign(userCredentialsData.value, result) // Store the entire response
} catch (error) {
console.error('Failed to fetch user credentials:', error)
}
updateInterval.value = setInterval(() => { updateInterval.value = setInterval(() => {
// Trigger Vue reactivity to update formatDate fields // Trigger Vue reactivity to update formatDate fields
authStore.currentUser = { ...authStore.currentUser } authStore.currentUser = { ...authStore.currentUser }
userCredentialsData.value.credentials = [...userCredentialsData.value.credentials] authStore.currentCredentials = [...authStore.currentCredentials]
}, 60000) // Update every minute }, 60000) // Update every minute
}) })
@ -124,12 +109,12 @@ onUnmounted(() => {
}) })
const getCredentialAuthName = (credential) => { const getCredentialAuthName = (credential) => {
const authInfo = userCredentialsData.value.aaguid_info[credential.aaguid] const authInfo = authStore.aaguidInfo[credential.aaguid]
return authInfo ? authInfo.name : 'Unknown Authenticator' return authInfo ? authInfo.name : 'Unknown Authenticator'
} }
const getCredentialAuthIcon = (credential) => { const getCredentialAuthIcon = (credential) => {
const authInfo = userCredentialsData.value.aaguid_info[credential.aaguid] const authInfo = authStore.aaguidInfo[credential.aaguid]
if (!authInfo) return null if (!authInfo) return null
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
@ -142,7 +127,7 @@ const addNewCredential = async () => {
authStore.isLoading = true authStore.isLoading = true
authStore.showMessage('Adding new passkey...', 'info') authStore.showMessage('Adding new passkey...', 'info')
const result = await registerCredential() const result = await registerCredential()
currentCredentials.value = await authStore.loadCredentials() await authStore.loadUserInfo()
authStore.showMessage('New passkey added successfully!', 'success', 3000) authStore.showMessage('New passkey added successfully!', 'success', 3000)
} catch (error) { } catch (error) {
console.error('Failed to add new passkey:', error) console.error('Failed to add new passkey:', error)
@ -157,7 +142,6 @@ const deleteCredential = async (credentialId) => {
try { try {
await authStore.deleteCredential(credentialId) await authStore.deleteCredential(credentialId)
currentCredentials.value = await authStore.loadCredentials()
authStore.showMessage('Passkey deleted successfully!', 'success', 3000) authStore.showMessage('Passkey deleted successfully!', 'success', 3000)
} catch (error) { } catch (error) {
authStore.showMessage(`Failed to delete passkey: ${error.message}`, 'error') authStore.showMessage(`Failed to delete passkey: ${error.message}`, 'error')

View File

@ -5,6 +5,8 @@ export const useAuthStore = defineStore('auth', {
state: () => ({ state: () => ({
// Auth State // Auth State
currentUser: null, currentUser: null,
currentCredentials: [],
aaguidInfo: {},
isLoading: false, isLoading: false,
// UI State // UI State
@ -87,19 +89,8 @@ export const useAuthStore = defineStore('auth', {
if (result.error) throw new Error(`Server: ${result.error}`) if (result.error) throw new Error(`Server: ${result.error}`)
this.currentUser = result.user this.currentUser = result.user
}, this.currentCredentials = result.credentials || []
async loadCredentials() { this.aaguidInfo = result.aaguid_info || {}
this.isLoading = true
try {
const response = await fetch('/auth/user-credentials')
const result = await response.json()
if (result.error) throw new Error(`Server: ${result.error}`)
this.currentCredentials = result.credentials
this.aaguidInfo = result.aaguid_info || {}
} finally {
this.isLoading = false
}
}, },
async deleteCredential(credentialId) { async deleteCredential(credentialId) {
const response = await fetch('/auth/delete-credential', { const response = await fetch('/auth/delete-credential', {
@ -112,7 +103,7 @@ export const useAuthStore = defineStore('auth', {
const result = await response.json() const result = await response.json()
if (result.error) throw new Error(`Server: ${result.error}`) if (result.error) throw new Error(`Server: ${result.error}`)
await this.loadCredentials() await this.loadUserInfo()
}, },
async logout() { async logout() {
try { try {

View File

@ -23,28 +23,7 @@ from .session_manager import (
async def get_user_info(request: Request) -> dict: async def get_user_info(request: Request) -> dict:
"""Get user information from session cookie.""" """Get user information and credentials from session cookie."""
try:
user = await get_current_user(request)
if not user:
return {"error": "Not authenticated"}
return {
"status": "success",
"user": {
"user_id": str(user.user_id),
"user_name": user.user_name,
"created_at": user.created_at.isoformat() if user.created_at else None,
"last_seen": user.last_seen.isoformat() if user.last_seen else None,
"visits": user.visits,
},
}
except Exception as e:
return {"error": f"Failed to get user info: {str(e)}"}
async def get_user_credentials(request: Request) -> dict:
"""Get all credentials for a user using session cookie."""
try: try:
user = await get_current_user(request) user = await get_current_user(request)
if not user: if not user:
@ -98,11 +77,18 @@ async def get_user_credentials(request: Request) -> dict:
return { return {
"status": "success", "status": "success",
"user": {
"user_id": str(user.user_id),
"user_name": user.user_name,
"created_at": user.created_at.isoformat() if user.created_at else None,
"last_seen": user.last_seen.isoformat() if user.last_seen else None,
"visits": user.visits,
},
"credentials": credentials, "credentials": credentials,
"aaguid_info": aaguid_info, "aaguid_info": aaguid_info,
} }
except Exception as e: except Exception as e:
return {"error": f"Failed to get credentials: {str(e)}"} return {"error": f"Failed to get user info: {str(e)}"}
async def refresh_token(request: Request, response: Response) -> dict: async def refresh_token(request: Request, response: Response) -> dict:

View File

@ -30,7 +30,6 @@ from fastapi.staticfiles import StaticFiles
from ..db import sql from ..db import sql
from .api_handlers import ( from .api_handlers import (
delete_credential, delete_credential,
get_user_credentials,
get_user_info, get_user_info,
logout, logout,
refresh_token, refresh_token,
@ -55,21 +54,12 @@ app = FastAPI(title="Passkey Auth", lifespan=lifespan)
app.mount("/auth/ws", ws_app) app.mount("/auth/ws", ws_app)
@app.get("/auth/user-info") @app.get("/auth/user-info")
async def api_get_user_info(request: Request): async def api_get_user_info(request: Request):
"""Get user information from session cookie.""" """Get user information and credentials from session cookie."""
return await get_user_info(request) return await get_user_info(request)
@app.get("/auth/user-credentials")
async def api_get_user_credentials(request: Request):
"""Get all credentials for a user using session cookie."""
return await get_user_credentials(request)
@app.post("/auth/refresh-token") @app.post("/auth/refresh-token")
async def api_refresh_token(request: Request, response: Response): async def api_refresh_token(request: Request, response: Response):
"""Refresh the session token.""" """Refresh the session token."""