Skip to content

[ATL-763] [LUM-1951] fix(ios): set max-age on session cookie so it survives cold reopen#32529

Merged
clopen-set merged 1 commit into
mainfrom
fix/ios-cookie-max-age
May 29, 2026
Merged

[ATL-763] [LUM-1951] fix(ios): set max-age on session cookie so it survives cold reopen#32529
clopen-set merged 1 commit into
mainfrom
fix/ios-cookie-max-age

Conversation

@clopen-set

@clopen-set clopen-set commented May 29, 2026

Copy link
Copy Markdown
Collaborator

After completing auth exchange, the Capacitor shell installs a session cookie into WKWebView's cookie jar via document.cookie. We're manually setting cookie attributes in an attempt to mirror the actual cookie generated by the server.

export function installSessionCookies(sessionToken: string): void {
const cookieAttrs = "path=/; domain=.vellum.ai; secure; samesite=lax";
document.cookie = `sessionid=${sessionToken}; ${cookieAttrs}`;
document.cookie = `__Secure-sessionid=${sessionToken}; ${cookieAttrs}`;
}

This cookie doesn't specify max-age or expires, so its lifetime is scoped to the client session. The cookie gets wiped if you quit the app, so users would have to log in again. Biometric recovery flow was a sort of bandaid over this.

As another bandaid, let's give the cookie a 2 week expiry so that it actually persists. I don't love the cookie dance we're doing here.

Testing

Verified this on iphone with biometric recovery disabled:

  • on simulator with dev SPA
  • on production build of app

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7171c28f56

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// `max-age` makes the cookie persistent. Without it WKWebView drops
// it on app kill and the user gets bounced back to login every cold
// reopen.
const cookieAttrs = "path=/; domain=.vellum.ai; secure; samesite=lax; max-age=1209600";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear the persistent session cookie on logout failures

This makes the JS-installed native session cookie survive cold restarts, but the web logout path only calls allauthLogout() best-effort and then clears local state in finally (apps/web/src/stores/auth-store.ts:243-251), even if the DELETE fails/returns non-ok and therefore no server Set-Cookie expiry is applied. In that offline/server-error scenario an iOS user appears signed out and is redirected to login, but after reopening the app the now-persistent valid cookie logs them back in; either explicitly expire these cookie names during logout cleanup or avoid persisting them without a matching client-side clear path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor edge case that I'm not gonna address here

Without max-age the cookie is session-scoped and WKWebView drops it
when the app process is killed, falling through to biometric recovery
on every cold reopen. 1209600 = 2 weeks, matches Django's
SESSION_COOKIE_AGE default.
@clopen-set clopen-set force-pushed the fix/ios-cookie-max-age branch from 7171c28 to 9d4ec51 Compare May 29, 2026 05:52
Comment on lines 187 to 188
document.cookie = `sessionid=${sessionToken}; ${cookieAttrs}`;
document.cookie = `__Secure-sessionid=${sessionToken}; ${cookieAttrs}`;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only set one of these cookies, not both. Public SPA is served over HTTPS and should use the __Secure prefix

// `max-age` makes the cookie persistent. If unspecified, the cookie
// expires at the end of the session, and users will be required to
// login again.
const cookieAttrs = "path=/; domain=.vellum.ai; secure; samesite=lax; max-age=1209600";

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like that we're manually reinstalling cookies. Why not just use X-Session-Token for auth, like with macOS?

As is, this requires us to keep all of these attributes in sync with the actual cookie, but we're not doing properly right now:

  • domain is wrong. This should be same-host and not be made available on all subdomains
  • max-age also needs to be kept in sync with backend in case we tweak default expiry

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @noanflaherty in case this has implications for electron

@clopen-set clopen-set merged commit 1b9253f into main May 29, 2026
7 checks passed
@clopen-set clopen-set deleted the fix/ios-cookie-max-age branch May 29, 2026 06:17
@clopen-set clopen-set changed the title fix(ios): set max-age on session cookie so it survives cold reopen [ATL-763] fix(ios): set max-age on session cookie so it survives cold reopen Jun 3, 2026
@linear

linear Bot commented Jun 3, 2026

Copy link
Copy Markdown

ATL-763

LUM-1951

@clopen-set clopen-set changed the title [ATL-763] fix(ios): set max-age on session cookie so it survives cold reopen [ATL-763] [LUM-1951] fix(ios): set max-age on session cookie so it survives cold reopen Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant