Blog8 min read
#engineering#security#product

How We Redesigned Login to Get Out of Your Way

A behind-the-scenes look at how we rethought authentication in twodos — introducing one-tap OTP login, refresh token rotation, and per-device session control — and why it matters for how you use the app every day.

June 20, 2026·8 min read·twodos team

I want to tell you about a change we shipped recently that aims to simplify the login process on the app.

For the first year of twodos, authentication worked the way most apps work: you entered your email and password, received a token that was valid for a short window, and when it expired the app silently failed until you logged in again. On a well-behaved client this was invisible. On a flaky network, or after a weekend away from the app, it wasn't. Sessions would die, lists would stop syncing, and you'd need to relogin.

This was the #1 point in the feedback from various customers. This post is about what we changed, why we made the decisions we made, and what it means for you.


The problem with how login used to work

The original session model was simple: log in, get a token, use the token, token expires, log in again. Simple to implement, simple to reason about. But it had a structural problem: the expiry window was fixed, and there was no way to extend a session without going through the full login flow again.

In practice this created two bad outcomes. First, users on mobile — especially on iOS and Android where apps are frequently backgrounded and killed — would come back to twodos to find their session gone. Second, users on multiple devices had no visibility into where their account was active, and no way to sign out of a device they'd lost access to.

The fix required rethinking the whole session lifecycle, not just tweaking a timeout value.


What we decided to build

We settled on three changes that work together:

1. Refresh token rotation. When you log in, you receive two things: a short-lived access token (valid for short time) and a long-lived refresh token (valid for longer time). The app uses the access token for API calls. Before it expires, the app quietly exchanges the refresh token for a new pair — without you doing anything. Your session stays alive indefinitely, as long as you use the app at least once every few days.

2. One-tap OTP login. For users who don't want to type a password — or who sign in on a new device where they've forgotten their credentials — we added a one-time password flow. Request a code to your email, enter it in the app, you're in. The code expires in five minutes and is single-use. No password required.

3. Per-device session control. Every login now records the platform and IP address. You can see a list of every active session in the app — what device, what platform, when it last connected — and revoke any of them instantly. Sign out of the old phone you sold last month. See an unfamiliar device? Kill the session and change your password.


The decision that took longest: replay attack detection

Refresh token rotation sounds simple until you ask: what happens if a refresh token is stolen and both the attacker and the legitimate user try to use it?

The naive approach — first come, first served — lets whichever party gets there first keep the session and silently invalidates the other. The legitimate user loses access without knowing why.

We took a different approach. When a refresh token is used, it is immediately marked as rotated and a new pair is issued. If anyone ever presents an already-rotated token, we treat that as a theft signal. The response is total: all active sessions for that account are immediately terminated, and the next request returns a specific security error that tells the client to show the user an alert. The user is forced back to the login screen, but they're told why.

This is a hard trade-off. It means a bug in a client — accidentally replaying a token due to a race condition — will be treated as an attack and log the user out. We spent time making sure the client-side token handling was correct before enabling this, and we've instrumented every rotation event so we can see anomalies quickly if they appear.

The alternative — ignoring replay — is not a trade-off we were willing to make. If a refresh token is stolen, silent session continuation is worse than a forced logout.


What changed in the code

For those interested in the technical detail: the core of the new system is a refreshtokens table with six columns that matter — id (the token the client stores), userId, platform, familyId (a shared identifier for the rotation chain), accessTokenId (the access token paired to this refresh token), and revokedAt.

Every rotation: the old refresh token is marked revoked with revokedReason = 'rotated', the paired access token is deleted, and a new pair is created in the same family chain. Every request that tries to use a rotated token triggers the full-family revocation.

The session list endpoint (GET /api/account/sessions) groups tokens by familyId — one row per device session — and marks the current one with isCurrent: true. Revoking a session kills the entire family: all rotations in that chain are revoked and the paired access token is deleted, leaving that device permanently signed out until the user logs in again from that device.

We also record the client's IP address at session creation. This is used only for the session list view — it is not used to enforce or restrict anything. You can log in from anywhere.


What this means for you

If you're an existing user: nothing changes in how you log in. Email and password still works exactly the same. The improvement is invisible — your session will simply last longer and reconnect automatically after brief network gaps.

If you've had problems with the app signing you out unexpectedly, this should fix them. The access token rotation happens in the background. You'll never see a "session expired" message unless you've been away from the app for more than 30 days without using it.

If you want to use the new features, here's what's available:

OTP login is available on all platforms. On the login screen, tap "Sign in with a code instead" and enter your email. A six-character code arrives in your mailbox (check for Spam/Junk folders too). This is useful when signing in on a shared or unfamiliar device where you don't want to type your password.

Session management is in Account Settings. You'll see every active session — what platform it is, when it signed in, when it last used the app. Tap "Sign out" next to any session to terminate it immediately. The device will see a security error on its next request and return to the login screen. There's also a "Sign out all other devices" button that terminates every session except the one you're currently using.

There's nothing to set up. Open the app, update if prompted, and everything works.


What we're watching

A change this central to authentication deserves close monitoring. In the first few weeks we're watching:

  • Replay attack events. We log every rotated-token replay attempt. A small number of false positives from buggy clients is expected and manageable. A spike would indicate a real theft vector and require immediate investigation.
  • Session duration. Are users staying logged in longer? Are they seeing fewer unexpected sign-outs? We're measuring this.
  • OTP usage. How many users prefer OTP to password? If it's high, we'll invest more in the passwordless flow and eventually offer passkey support.

We'll share what we find.


How to send us feedback

If you notice anything odd after the update — unexpected sign-outs, error messages you haven't seen before, a session that should have been terminated but wasn't — we want to know.

The easiest way is through the app. In Account Settings, there's a "Send feedback" option that attaches your account ID and session metadata automatically so we can investigate without you needing to describe the technical details.

You can also reach us directly at contact@twodos.app. If you're reporting a security concern, please use security@twodos.app — these go to a separate queue and are treated with higher priority.

We read everything. Not every piece of feedback turns into a change immediately, but it all shapes what we build next. The session management panel existed because several users asked how to sign out of an old device. That's the kind of detail that doesn't show up in analytics.


Download twodos

If you're reading this and haven't tried the app yet, now is a good moment.

twodos is a shared list app built for pairs — two people who want to stay coordinated without the overhead of a project management tool, or creating numerous groups in WhatsApp. Groceries, home repairs, travel packing, weekend plans. Anything where one person's "I need to remember this" should automatically become the other person's "I can see what they're thinking."

It's free to download and use.


If you have questions about anything technical mentioned here, we're happy to go deeper — just email us.

Try twodos

Shared lists for two people.

Free on Android, iOS, Windows, and macOS. No subscription.