Skip to main content
All posts
Recovery LayerLaunchiOSOkta

Butterfly Security for iPhone is on the App Store

A companion to the Okta Admin Console for the minutes when you're on call and nowhere near a laptop. Suspend a user, clear sessions, activate a network zone, and read your recovery posture from your phone, with two independent gates on every change that the server enforces rather than the app. Running a full restore stays on the desktop on purpose, and here is the reasoning behind that line.

Mick Johnson
Founder, Butterfly Security
6 min read

Butterfly Security for iPhone and iPad is on the App Store today. It's a companion to the Okta Admin Console, running on the Okta integration you install from the Okta Integration Network, and it puts the on-call subset of that work in your pocket.

The reason we built it is fairly boring and specific. Identity incidents don't wait until you're at your desk, and the opening minutes of one are mostly small decisive actions, which is to say finding the person, cutting their sessions, suspending the account, tightening a network zone, and getting a read on whether you could actually recover if the next hour goes badly. None of that needs a full console, but it does need a laptop, so you either drove home or talked a colleague through the screens over the phone.

What you can do from the phone

You sign in with the one-time code we email you (SSO sign-in on the phone isn't supported yet, so plan around that if your team normally logs in through an IdP), pick the Okta connection you're working in, and land on a readout with a resilience score out of 100 and the factors behind it, the age and status of your last backup, and the configuration drift from the past week.

From there the app covers the on-call subset of admin work. You can search users and open a person, then suspend or unsuspend them, clear every active session, send a password reset, expire a password, unlock an account, or deactivate, with deactivation requiring the email typed back exactly. Groups and applications are browsable with membership and assignment changes on each. Network zones list out with blocklist zones marked, and you can activate one during an incident. The Okta system log is there with severity, event type, actor and timing, backup history carries status, age, resource counts and size, and restore preview shows you what a recovery would touch. The DR assistant answers from indexed runbooks and your own backed up configuration and cites what it used, and there's an executive brief you can forward when someone senior asks what happened.

Every change clears two gates, and the server enforces both

A phone in a pocket is a different threat model from a laptop at a desk, so we didn't want the safety of any mutation to depend on the app behaving correctly.

The first gate is biometric. Before submitting a change the client calls LAContext.evaluatePolicy(.deviceOwnerAuthentication), and only once that succeeds does it attach the X-Biometric-Confirmed: 1 header. The server checks for that header before it parses the request body and before it loads your session, so a session cookie lifted off the device and replayed from somewhere else can't even probe the mutation surface, never mind reach Okta. The value has to be exactly 1. We ran that against production this morning, and a request with no header comes back 412 Precondition Failed, the same request with the header advances to the session check and returns 401, and a header carrying the string "true" instead of 1 is still a 412.

The second gate is capability. Every mutation declares the Okta scope it needs, and the server compares that against the scopes your connection was actually granted before it does anything else, so a missing scope comes back as a structured 403 naming the scope and the remediation with no round trip to Okta at all. That means an action your integration was never granted can't be exercised even if something goes wrong on our side, and in the app you see it as a lock badge on a disabled button, which is how you find out during a quiet Tuesday rather than during an incident.

Both gate decisions are written to the audit log, refusals included, so the record shows the changes that happened alongside the ones that were turned away and why. Reads are logged too but aren't biometric gated, which is deliberate, because a connection holding only read scopes should still give you the full readout, the score, the log and the backup history, while being structurally unable to change anything. Rate limits sit on top of all of it at thirty mutations a minute and sixty reads a minute.

Running a full restore stays on the desktop, on purpose

A full restore can reach thousands of objects across users, groups, applications, policies and zones, and the thing that makes it safe is reading the dry run diff carefully before you commit to it. That review doesn't survive a five inch screen in the back of a taxi, so the app shows you restore preview counts and then hands you to the console for the execution itself.

We think of that boundary as blast radius control that we chose rather than a corner we haven't gotten to. The phone is where you contain an incident and where you check that recovery is available to you, and the desktop is where you put the org back with the diff in front of you at a size where you'll notice the line you weren't expecting. Holding that line also keeps the highest consequence operation we ship behind the surface where we can show you the most context, which matters more to us than being able to say the whole product runs on a phone.

What the app keeps on the device

The session cookie lives in the Keychain, along with a few preferences such as the connection you last selected. There's no local copy of your directory, no cached user list, and no configuration data at rest on the phone, because everything the app displays it fetches for that screen and then lets go. If the device is lost, the exposure is a session you can revoke, and the mutation surface behind that session is still gated by a biometric check an attacker has to pass on the device itself.

Getting it

Butterfly Security needs iOS 17.0 or later and runs on iPhone and iPad. It's a free download and it works with the 30 day trial, so you can connect an Okta org and have the readout on your phone the same afternoon. The setup walkthrough, the full table of which action needs which Okta scope, and the exact behaviour of both gates are in the iOS documentation.

Download Butterfly Security on the App Store