Refactor authentication architecture to eliminate dual auth complexity
This major refactor consolidates the authentication system to use a single source of truth, eliminating timing issues and architectural complexity that was causing chat and payment functionality problems. Key Changes: • Remove old global useAuth composable and replace with useAuthService wrapper • Update all 25+ files to use consistent auth pattern via dependency injection • Eliminate dual auth detection workarounds from services (ChatService, PaymentService, etc.) • Fix TypeScript errors and add proper Uint8Array conversion for Nostr private keys • Consolidate auth state management to AuthService as single source of truth Benefits: • Resolves chat peer loading and message subscription timing issues • Fixes wallet detection problems for Lightning payments • Eliminates race conditions between global and injected auth • Maintains API compatibility while improving architecture • Reduces code complexity and improves maintainability 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5633aa154b
commit
4feb5459cc
27 changed files with 210 additions and 518 deletions
|
|
@ -1,168 +0,0 @@
|
|||
import { ref, computed } from 'vue'
|
||||
import { lnbitsAPI, type User, type LoginCredentials, type RegisterData } from '@/lib/api/lnbits'
|
||||
import { useMultiAsyncOperation } from '@/core/composables/useAsyncOperation'
|
||||
|
||||
const currentUser = ref<User | null>(null)
|
||||
|
||||
// Shared async operations for auth actions
|
||||
const authOperations = useMultiAsyncOperation<{
|
||||
initialize: User | null
|
||||
login: User
|
||||
register: User
|
||||
logout: void
|
||||
}>()
|
||||
|
||||
export function useAuth() {
|
||||
const isAuthenticated = computed(() => !!currentUser.value)
|
||||
|
||||
// Get operation instances
|
||||
const initializeOp = authOperations.createOperation('initialize')
|
||||
const loginOp = authOperations.createOperation('login')
|
||||
const registerOp = authOperations.createOperation('register')
|
||||
const logoutOp = authOperations.createOperation('logout')
|
||||
|
||||
/**
|
||||
* Initialize authentication on app start
|
||||
*/
|
||||
async function initialize(): Promise<void> {
|
||||
try {
|
||||
await initializeOp.execute(async () => {
|
||||
if (lnbitsAPI.isAuthenticated()) {
|
||||
const user = await lnbitsAPI.getCurrentUser()
|
||||
currentUser.value = user
|
||||
return user
|
||||
}
|
||||
return null
|
||||
}, {
|
||||
errorMessage: 'Failed to initialize authentication',
|
||||
showToast: false // Don't show toast for initialization errors
|
||||
})
|
||||
} catch {
|
||||
// Clear invalid token on error
|
||||
await logout()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Login with username and password
|
||||
*/
|
||||
async function login(credentials: LoginCredentials): Promise<void> {
|
||||
await loginOp.execute(async () => {
|
||||
await lnbitsAPI.login(credentials)
|
||||
|
||||
// Get user details
|
||||
const user = await lnbitsAPI.getCurrentUser()
|
||||
currentUser.value = user
|
||||
return user
|
||||
}, {
|
||||
errorMessage: 'Login failed'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Register new user
|
||||
*/
|
||||
async function register(data: RegisterData): Promise<void> {
|
||||
await registerOp.execute(async () => {
|
||||
await lnbitsAPI.register(data)
|
||||
|
||||
// Get user details
|
||||
const user = await lnbitsAPI.getCurrentUser()
|
||||
currentUser.value = user
|
||||
return user
|
||||
}, {
|
||||
errorMessage: 'Registration failed'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout and clear user data
|
||||
*/
|
||||
async function logout(): Promise<void> {
|
||||
await logoutOp.execute(async () => {
|
||||
// Clear local state
|
||||
lnbitsAPI.logout()
|
||||
currentUser.value = null
|
||||
// Clear all auth operation states
|
||||
authOperations.clearAll()
|
||||
}, {
|
||||
showToast: false // Don't show toast for logout
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user password
|
||||
*/
|
||||
async function updatePassword(currentPassword: string, newPassword: string): Promise<void> {
|
||||
const updatePasswordOp = authOperations.createOperation('updatePassword' as any)
|
||||
|
||||
return await updatePasswordOp.execute(async () => {
|
||||
const updatedUser = await lnbitsAPI.updatePassword(currentPassword, newPassword)
|
||||
currentUser.value = updatedUser
|
||||
return updatedUser
|
||||
}, {
|
||||
errorMessage: 'Failed to update password'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user profile
|
||||
*/
|
||||
async function updateProfile(data: Partial<User>): Promise<void> {
|
||||
const updateProfileOp = authOperations.createOperation('updateProfile' as any)
|
||||
|
||||
return await updateProfileOp.execute(async () => {
|
||||
const updatedUser = await lnbitsAPI.updateProfile(data)
|
||||
currentUser.value = updatedUser
|
||||
return updatedUser
|
||||
}, {
|
||||
errorMessage: 'Failed to update profile'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is authenticated
|
||||
*/
|
||||
function checkAuth(): boolean {
|
||||
return lnbitsAPI.isAuthenticated()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user display info
|
||||
*/
|
||||
const userDisplay = computed(() => {
|
||||
if (!currentUser.value) return null
|
||||
|
||||
return {
|
||||
name: currentUser.value.username || currentUser.value.email || 'Anonymous',
|
||||
username: currentUser.value.username,
|
||||
email: currentUser.value.email,
|
||||
id: currentUser.value.id,
|
||||
shortId: currentUser.value.id.slice(0, 8) + '...' + currentUser.value.id.slice(-8)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
return {
|
||||
// State
|
||||
currentUser: computed(() => currentUser.value),
|
||||
isAuthenticated,
|
||||
isLoading: computed(() => authOperations.isAnyLoading()),
|
||||
error: computed(() => authOperations.hasAnyError() ?
|
||||
(initializeOp.error.value || loginOp.error.value || registerOp.error.value || logoutOp.error.value) : null),
|
||||
userDisplay,
|
||||
|
||||
// Actions
|
||||
initialize,
|
||||
login,
|
||||
register,
|
||||
logout,
|
||||
updatePassword,
|
||||
updateProfile,
|
||||
checkAuth
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance for global state
|
||||
export const auth = useAuth()
|
||||
72
src/composables/useAuthService.ts
Normal file
72
src/composables/useAuthService.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
|
||||
import type { AuthService } from '@/modules/base/auth/auth-service'
|
||||
|
||||
/**
|
||||
* Composable to access the injected auth service
|
||||
* This replaces the global auth composable with the injected service
|
||||
*/
|
||||
export function useAuth() {
|
||||
const authService = injectService(SERVICE_TOKENS.AUTH_SERVICE) as AuthService
|
||||
|
||||
if (!authService) {
|
||||
throw new Error('AuthService not available. Make sure base module is installed.')
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
currentUser: authService.currentUser,
|
||||
isAuthenticated: authService.isAuthenticated,
|
||||
isLoading: authService.isLoading,
|
||||
error: authService.error,
|
||||
userDisplay: authService.userDisplay,
|
||||
|
||||
// Compatibility aliases
|
||||
user: authService.user,
|
||||
|
||||
// Actions
|
||||
initialize: () => authService.initialize(),
|
||||
login: (credentials: any) => authService.login(credentials),
|
||||
register: (data: any) => authService.register(data),
|
||||
logout: () => authService.logout(),
|
||||
updatePassword: (current: string, newPass: string) => authService.updatePassword(current, newPass),
|
||||
updateProfile: (data: any) => authService.updateProfile(data),
|
||||
checkAuth: () => authService.checkAuth(),
|
||||
refresh: () => authService.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton reference for compatibility
|
||||
export function getAuthService() {
|
||||
return injectService(SERVICE_TOKENS.AUTH_SERVICE) as AuthService
|
||||
}
|
||||
|
||||
// For files that import { auth } directly
|
||||
export const auth = {
|
||||
get currentUser() {
|
||||
const service = getAuthService()
|
||||
return service?.currentUser
|
||||
},
|
||||
get isAuthenticated() {
|
||||
const service = getAuthService()
|
||||
return service?.isAuthenticated
|
||||
},
|
||||
get isLoading() {
|
||||
const service = getAuthService()
|
||||
return service?.isLoading
|
||||
},
|
||||
get error() {
|
||||
const service = getAuthService()
|
||||
return service?.error
|
||||
},
|
||||
get userDisplay() {
|
||||
const service = getAuthService()
|
||||
return service?.userDisplay
|
||||
},
|
||||
initialize: () => getAuthService()?.initialize(),
|
||||
login: (credentials: any) => getAuthService()?.login(credentials),
|
||||
register: (data: any) => getAuthService()?.register(data),
|
||||
logout: () => getAuthService()?.logout(),
|
||||
updatePassword: (current: string, newPass: string) => getAuthService()?.updatePassword(current, newPass),
|
||||
updateProfile: (data: any) => getAuthService()?.updateProfile(data),
|
||||
checkAuth: () => getAuthService()?.checkAuth()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue