Commit b27b2b1d by CaIon

fix(web): detect iPad login sessions correctly

parent a043eef5
......@@ -21,7 +21,7 @@ import { describe, test } from 'node:test'
import type { TFunction } from 'i18next'
import { loginMethodLabel, sessionDevice } from './login-session-utils'
import { loginMethodLabel, sessionDevice } from '../login-session-utils'
const translate = ((key: string) => key) as TFunction
......@@ -39,15 +39,57 @@ describe('login session presentation', () => {
)
})
test('derives a stable browser and operating-system label', () => {
test('labels iPad Safari as iOS when its user agent also mentions Mac OS X', () => {
const userAgent =
'Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1'
assert.equal(
sessionDevice(userAgent, 'Unknown device', 'Browser'),
'Safari · iOS'
)
})
test('labels a touch-capable current iPad session as iOS when its desktop user agent says Macintosh', () => {
const userAgent =
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15'
assert.equal(
sessionDevice(
'Mozilla/5.0 (Macintosh; Intel Mac OS X) AppleWebKit Safari/605.1.15',
'Unknown device',
'Browser'
),
sessionDevice(userAgent, 'Unknown device', 'Browser', 5),
'Safari · iOS'
)
})
test('keeps touch-capable Windows Chrome sessions identifiable', () => {
const userAgent =
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
assert.equal(
sessionDevice(userAgent, 'Unknown device', 'Browser', 10),
'Chrome · Windows'
)
})
test('keeps Android Chrome sessions identifiable when their user agent mentions Linux', () => {
const userAgent =
'Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36'
assert.equal(
sessionDevice(userAgent, 'Unknown device', 'Browser', 5),
'Chrome · Android'
)
})
test('keeps genuine macOS Safari sessions identifiable', () => {
const userAgent =
'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15'
assert.equal(
sessionDevice(userAgent, 'Unknown device', 'Browser'),
'Safari · macOS'
)
})
test('falls back to the unknown-device label for an empty user agent', () => {
assert.equal(
sessionDevice('', 'Unknown device', 'Browser'),
'Unknown device'
......
......@@ -34,6 +34,10 @@ interface LoginSessionItemProps {
export function LoginSessionItem({ session, onRevoke }: LoginSessionItemProps) {
const { t } = useTranslation()
const maxTouchPoints =
session.current && typeof navigator !== 'undefined'
? navigator.maxTouchPoints
: 0
return (
<div className='flex flex-col gap-3 py-4 sm:flex-row sm:items-center'>
......@@ -46,7 +50,8 @@ export function LoginSessionItem({ session, onRevoke }: LoginSessionItemProps) {
{sessionDevice(
session.user_agent,
t('Unknown device'),
t('Browser')
t('Browser'),
maxTouchPoints
)}
</p>
{session.current && <Badge variant='secondary'>{t('Current')}</Badge>}
......
......@@ -21,7 +21,8 @@ import type { TFunction } from 'i18next'
export function sessionDevice(
userAgent: string,
unknownDevice: string,
browserLabel: string
browserLabel: string,
maxTouchPoints = 0
): string {
if (!userAgent) return unknownDevice
let browser = browserLabel
......@@ -31,12 +32,15 @@ export function sessionDevice(
else if (userAgent.includes('Safari/')) browser = 'Safari'
let system = ''
if (userAgent.includes('Windows')) system = 'Windows'
else if (userAgent.includes('Mac OS')) system = 'macOS'
else if (userAgent.includes('Android')) system = 'Android'
else if (userAgent.includes('iPhone') || userAgent.includes('iPad')) {
const isIPad =
userAgent.includes('iPad') ||
(userAgent.includes('Macintosh') && maxTouchPoints > 1)
if (userAgent.includes('iPhone') || isIPad) {
system = 'iOS'
} else if (userAgent.includes('Linux')) system = 'Linux'
} else if (userAgent.includes('Android')) system = 'Android'
else if (userAgent.includes('Windows')) system = 'Windows'
else if (userAgent.includes('Mac OS')) system = 'macOS'
else if (userAgent.includes('Linux')) system = 'Linux'
return system ? `${browser} · ${system}` : browser
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment