A logout button that only hides the account screen leaves a surprising amount behind: tokens in secure storage, cached API responses, a local database full of the previous user's data, images in the cache folder, a push token still registered to the old account, and a session the server still considers valid. On a personal phone that is untidy. On a shared tablet, a phone handed to a family member, or a device being sold, it is a privacy failure. Getting logout right is mostly a checklist, and the items on it are easy to miss because nobody tests logout as carefully as login.
Short answer
A complete logout does two things: it ends the session on the server, and it removes the account's data from the device. On the server, revoke the refresh token and any active sessions. On the device, delete tokens from the Keychain or Keystore, clear the local database and caches that belong to the account, remove downloaded files, unregister or reassign the push token, reset analytics identifiers tied to the user, and clear in-memory state. Remember that iOS Keychain items can survive an app uninstall, so a reinstall should not quietly restore the previous session.
Why logout gets less attention than login
Login is tested on every build because nothing works without it. Logout is tested once, when the button is added, by checking that the login screen appears afterwards. That check passes whether or not anything was actually removed.
Meanwhile, apps accumulate state over time. A networking library caches responses, an image loader keeps a disk cache, a database grows new tables for new features, an analytics SDK assigns a user ID, a push service registers a token. Each was added by someone thinking about their feature, not about the account ending. The OWASP Session Management Cheat Sheet treats logout as a server-side invalidation requirement first, and the device-side clean-up is the mobile-specific half.
End the session on the server first
Deleting a token from the device does not make it invalid. If the token was copied, through a backup, a compromised device, a log or a proxy, it keeps working until it expires. Logout should call the backend to revoke the refresh token and, for sensitive apps, end the session everywhere the user chooses. This is the weakness described in CWE-613, insufficient session expiration.
If the network call fails, still clear the device and retry the revocation later, or let short access token lifetimes limit the window. The post on mobile session management covers token lifetimes and revocation in more depth.
What to clear on the device
| Item | Where it lives | What happens if left | How to clear |
|---|---|---|---|
| Access and refresh tokens | Keychain, Keystore, secure storage | Next user or attacker resumes the session | Delete items explicitly |
| Local database | SQLite, Room, Core Data, Realm | Previous user's data visible or synced | Delete or wipe account tables |
| HTTP response cache | URL cache, OkHttp cache | Old responses served to the next user | Clear the cache |
| Image and file caches | Cache and documents folders | Photos and documents remain readable | Delete account files |
| Push token registration | Your backend and push provider | Notifications go to the wrong person | Unregister or reassign server-side |
| Analytics and crash user IDs | SDK storage | New activity attributed to the old user | Call the SDK reset method |
| In-memory state | View models, stores, singletons | Old data flashes on the next screens | Reset app state or restart the root |
| Preferences tied to the account | UserDefaults, SharedPreferences | Settings and drafts leak across accounts | Remove account-scoped keys |
Each row is a separate piece of code, which is why a single central logout function that calls every clean-up step is worth having. New features then add their clean-up to that function rather than relying on someone remembering.
Tokens and the Keychain uninstall surprise
On iOS, items stored in the Keychain can persist after the app is deleted and be readable again if the app is reinstalled. This behaviour has existed for years and catches teams in two ways. A user who deletes the app to sign out, a common habit, finds themselves signed straight back in after reinstalling. And a device passed to someone else can carry the previous user's tokens into a fresh install.
The fix is to detect a fresh install, for example with a flag in regular app storage, which is removed on uninstall, and to clear Keychain items when that flag is missing. Libraries such as Expo SecureStore use the Keychain on iOS, so the same consideration applies to React Native apps.
On Android, app data including Keystore-protected keys is removed on uninstall, but Auto Backup can restore files on reinstall or on a new device unless sensitive files are excluded. Tokens and account databases should be excluded from backup.
Local data and caches
The local database is usually the largest store of personal data on the device. On logout, either delete the database file entirely or clear every table that holds account data, and do the same for any encrypted store and its key. The post on encrypting local databases covers key handling, and the key should be deleted along with the data.
HTTP caches are easy to forget because they are managed by the networking layer. On iOS, URLCache can hold full responses on disk; on Android, OkHttp and similar clients keep their own caches. Clear them on logout, and consider not caching authenticated responses at all, as discussed in the post on NSURLCache and sensitive responses.
Downloaded files, generated PDFs, cached images and voice notes live in the app's cache and documents directories. Delete the account's files explicitly rather than trusting the system to clear caches eventually.
Push notifications
If the push token stays registered to the old account, the next person to sign in on that device may receive the previous user's notifications, and a device that was sold keeps receiving them. On logout, tell your backend to remove the association between the device token and the account. Firebase's guidance on managing registration tokens covers keeping that mapping accurate, and on iOS the app can also unregister for remote notifications if it should stop receiving them entirely.
Analytics, crash reporting and other SDKs
Most analytics and crash reporting SDKs let you set a user identifier, and most have a reset or logout call. Without it, the next user's activity is attributed to the previous account, which is a data accuracy problem and, under privacy law, can be a data protection problem. Add each SDK's reset call to the central logout function, and review the list whenever a new SDK is added.
Account switching and shared devices
Apps used on shared devices, such as staff tablets, kiosks and family phones, need logout to be complete every time, and they often need a fast account switch too. The safest pattern is to treat each sign-in as a fresh start: no data from the previous account is reused, even if the same person signs back in a minute later. Where speed matters, separate account-scoped data clearly from device-level settings so the clean-up can remove one without touching the other.
Logout on account deletion and password change
Account deletion is logout with stricter requirements. Apple requires apps that support account creation to offer account deletion in the app, and when an account is deleted, every device-side item in the list above should be removed along with the server-side data, not just the session. A deleted account that still shows cached messages on the device is a visible failure in exactly the moment the user is paying attention.
A password change or a reported compromise should end other sessions as well. Offer the user a choice to sign out everywhere, revoke all refresh tokens server-side when they accept it, and make sure each device clears its local state the next time it receives a rejected token rather than continuing to show cached data. The same central logout function can run in that case, triggered by the server's response instead of the button.
Background and offline cases
Logout often happens at awkward moments: while an upload is running, while offline, or while a background sync is mid-way. Cancel queued requests that carry the old token, stop background tasks tied to the account, and if the device is offline, clear the local state immediately and queue the server revocation for when connectivity returns. Showing the login screen while a background job continues to write the previous user's data into the database is a common bug that inspection after logout reveals quickly.
Web views and cookies
Apps that show account pages in a web view, for example a help centre, a checkout or a settings page served by the website, also hold a cookie store. Clearing native tokens does nothing to the web view's session cookies, so the next user who opens that screen may find the previous user signed in on the web side. Clear the web view's cookies and website data on logout, and if the app uses an external browser session for sign-in, end that session on the server as well, since the app cannot clear the system browser's cookies itself.
Widgets, extensions and shared containers
On iOS, app extensions and widgets often read data from an App Group container shared with the main app. If logout clears only the main app's storage, a home screen widget can keep showing the previous user's balance, messages or tasks. Clear the shared container and reload widget timelines as part of logout. Android home screen widgets and notification channels deserve the same check.
A single function, tested in CI
Put every clean-up step behind one function and cover it with an automated test that signs in, populates each store, logs out and asserts each store is empty. That test fails the day someone adds a new cache or SDK without adding its clean-up, which is exactly the regression manual testing misses. Pair it with a periodic manual inspection on a real device, since some stores, such as the Keychain and widget timelines, behave differently outside a test environment.
Review the test whenever a dependency is added, because new SDKs are the most common source of state nobody thought to clear. A logout that was complete last quarter can be incomplete today without any change to the logout code itself.
Testing logout properly
A logout test should prove absence, not just show the login screen. Sign in, use every major feature so data is cached, then log out and inspect the app container on a test device: secure storage items, database files, caches and preferences. Sign in as a different user and check that nothing from the first account appears, including in notifications. Then delete and reinstall the app and confirm it does not resume the old session.
An automated pass over the built artifact, such as PTKD.com, flags the build-level settings around this, such as backup configuration and insecure storage APIs used for tokens; the runtime behaviour of logout itself needs the device inspection described above, since it depends on code paths rather than build settings. The OWASP MASVS authentication requirements and privacy requirements set the standard reviewers will use.
What to take away
Logout is two jobs: revoke the session on the server and remove the account from the device. On the device that means tokens, the local database and its key, HTTP and file caches, account preferences, push token registration, analytics identifiers and in-memory state, all cleared from one central function that new features extend. Handle the iOS Keychain surviving uninstall and Android backups restoring files, and test logout by inspecting what remains rather than by watching the login screen appear.




