Skip to content

iOS CookieManager uses inconsistent expiresDate units and weak URL/domain matching #46

@kjsolo

Description

@kjsolo

Hi, thanks for maintaining ComposeNativeWebView.

While integrating the library's CookieManager across Android, Desktop/Wry, and iOS, I noticed a few iOS-specific cookie handling issues that may cause inconsistent behavior across platforms.

The public/common Cookie.expiresDate appears to be treated as a millisecond timestamp on Android/JVM/Desktop/Wry, but the iOS implementation currently passes it directly to NSDate.dateWithTimeIntervalSince1970, whose input is seconds.

This can make iOS cookie expiration dates incorrect by a factor of 1000.

Affected area

webview-compose/src/iosMain/kotlin/.../cookie/IOSCookieManager.kt

Relevant behavior:

expiresDate = cookie.expiresDate?.timeIntervalSince1970?.toLong()

and:

NSDate.dateWithTimeIntervalSince1970(expires.toDouble())

Issues found

1. expiresDate unit mismatch on iOS

Other platforms appear to use milliseconds:

  • Android/JVM use Date(expiresDate)
  • Wry-side field is named/handled as expires_date_ms
  • Ktor-style timestamps are usually milliseconds

But iOS APIs use seconds:

NSDate.dateWithTimeIntervalSince1970(...)
NSHTTPCookie.expiresDate?.timeIntervalSince1970

So iOS should likely convert:

// writing to iOS
NSDate.dateWithTimeIntervalSince1970(expiresMs / 1000.0)

// reading from iOS
(cookie.expiresDate?.timeIntervalSince1970?.times(1000))?.toLong()

2. domain = null becomes an empty iOS cookie domain

Current behavior appears to use:

NSHTTPCookieDomain to (cookie.domain ?: "")

For host-only cookies, it may be better to derive the host from the request URL when cookie.domain is null or blank.

Suggested behavior:

NSHTTPCookieDomain to (cookie.domain?.takeIf { it.isNotBlank() } ?: urlHost)

3. URL/domain matching uses string containment

The iOS implementation appears to match cookies with logic similar to:

url.contains(cookie.domain.removePrefix("."))

This may produce false positives or false negatives. A safer check would parse the URL host and compare:

host == cookieDomain || host.endsWith(".$cookieDomain")

Path matching could also follow normal cookie path rules.

Expected behavior

Cookie behavior should be consistent across Android, Desktop/Wry, and iOS:

  • Cookie.expiresDate has one documented unit across all platforms, preferably milliseconds.
  • Host-only cookies can be set from a request URL without requiring an explicit domain.
  • getCookies(url) and removeCookies(url) match cookies by parsed host/path rather than substring matching.

Suggested fix

  • Document Cookie.expiresDate as milliseconds.
  • Convert milliseconds ↔ seconds inside the iOS adapter.
  • Derive domain from request URL when cookie.domain is null/blank.
  • Replace url.contains(...) matching with parsed URL host/path matching.

Notes

This issue was found during cross-platform cookie synchronization testing. No app-specific behavior is required to reproduce the underlying problem; it appears to be in the iOS platform adapter itself.

Thanks again for the library.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions