- Restyle organizer auth pages: Dutch text, remove placeholder social login - Restyle portal auth pages to Vuexy v1 centered card pattern with decorative shapes - MFA challenge card component with VOtpInput, method tabs, backup code input, trusted device checkbox, and session countdown timer - Login pages handle mfa_required response with device fingerprint header - Security settings page with TOTP setup (QR code), email setup, disable MFA, backup codes regeneration, and trusted devices management - Portal profile page includes MFA security section - Admin user detail page shows MFA status with reset button - MFA enforcement route guard redirects to security settings when required - Device fingerprint utility for trusted device identification - MFA types, composables with TanStack Query for both apps Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
50 lines
1.2 KiB
TypeScript
50 lines
1.2 KiB
TypeScript
export function generateDeviceFingerprint(): string {
|
|
const components = [
|
|
navigator.userAgent,
|
|
`${screen.width}x${screen.height}`,
|
|
Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
navigator.language,
|
|
navigator.hardwareConcurrency?.toString() ?? '',
|
|
]
|
|
|
|
const str = components.join('|')
|
|
let hash = 0
|
|
|
|
for (let i = 0; i < str.length; i++) {
|
|
const char = str.charCodeAt(i)
|
|
|
|
hash = ((hash << 5) - hash) + char
|
|
hash = hash & hash
|
|
}
|
|
|
|
return Math.abs(hash).toString(36)
|
|
}
|
|
|
|
export function getDeviceName(): string {
|
|
const ua = navigator.userAgent
|
|
let browser = 'Onbekend'
|
|
let os = 'Onbekend'
|
|
|
|
if (ua.includes('Chrome') && !ua.includes('Edg'))
|
|
browser = 'Chrome'
|
|
else if (ua.includes('Firefox'))
|
|
browser = 'Firefox'
|
|
else if (ua.includes('Safari') && !ua.includes('Chrome'))
|
|
browser = 'Safari'
|
|
else if (ua.includes('Edg'))
|
|
browser = 'Edge'
|
|
|
|
if (ua.includes('Mac OS'))
|
|
os = 'macOS'
|
|
else if (ua.includes('Windows'))
|
|
os = 'Windows'
|
|
else if (ua.includes('Linux'))
|
|
os = 'Linux'
|
|
else if (ua.includes('Android'))
|
|
os = 'Android'
|
|
else if (ua.includes('iPhone') || ua.includes('iPad'))
|
|
os = 'iOS'
|
|
|
|
return `${browser} op ${os}`
|
|
}
|