Authenticator: an offline two-factor client that never opens a socket
Two-Factor Authentication Client for iOS
At a glance
- What is it?
- Mattrubin's Authenticator is a Swift iOS app that stores one-time password secrets encrypted on the keychain and never connects to the internet. It is a good fit for a single device, and a poor fit for anyone who wants multi-device sync or a current release trail.
- Who is it for?
- Authenticator suits an iPhone user who wants one-time passwords generated entirely on device with no account and no network traffic, and it does not suit a team that needs synced secrets across devices, an Android build, or a release process you can audit.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 89 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Secrets live on the keychain and the app never connects
The defining constraint of this app is not a feature list but an absence: the app never connects to the internet, and secret keys never leave the device. That single decision shapes everything else, from the setup screens to the absence of any sync or backup feature in the repository.
Tokens are created three ways: by scanning a QR code, by opening an otpauth:// URL following the KeyUriFormat, or by typing the secret by hand. Once created, the data is stored in encrypted form on the iOS keychain rather than in a plain file or a user defaults blob, which is what makes the offline claim hold up when the device is lost or imaged.
The one-time password algorithms follow the published standards, with full support for time-based tokens under RFC 6238 and counter-based tokens under RFC 4226. Supporting both matters more than it sounds, because services that issue counter based codes cannot be handled by an app that only implements a moving time window. The repository topics list the same concerns: 2fa, authenticator, one-time-passwords, two-factor-authentication, ios, swift.
The app is a native Swift project, not a web view. The top level of the repository carries the expected pieces of a long lived iOS codebase: an Authenticator.xcodeproj, an Authenticator.xcworkspace, the Authenticator/ source directory, AuthenticatorTests/, a fastlane/ directory with the screenshots used in the README, a Cartfile, a Cartfile.resolved and a Cartfile.private.
Open the workspace, not the project, or the build breaks
The setup instructions are short but contain one step that people skip and then lose an hour to. The sequence starts with a clone and a submodule update, both of which are required because the dependencies are checked in as submodules rather than resolved on demand:
git clone https://github.com/mattrubin/Authenticator.git cd Authenticator
git submodule update --init --recursiveThe project then tells you to open the Authenticator.xcworkspace file, with a warning attached that matters: if you open the .xcodeproj instead, the project will not be able to find its dependencies. In practice this is the difference between a workspace that aggregates the app target and its dependency targets, and a bare project file that knows nothing about them.
The final step is to build and run the Authenticator scheme. Nothing in the setup path requires a developer account, a signing certificate or a device provisioning profile, so the barrier to evaluating the app on a simulator is low.
One thing the instructions do not spell out is how to build for a physical device, since that normally means a signing step and a development team. If you need an installable build rather than a simulator run, that gap is where you will look first.
Carthage manages the dependencies but never builds them
The dependency story here is unusual enough to be worth reading twice. The project uses Carthage to manage its dependencies, but it does not currently use Carthage to build those dependencies. Instead, the dependency projects are checked out as submodules, included in Authenticator.xcworkspace, and compiled by Xcode as target dependencies of the Authenticator app.
So Carthage appears in two roles at once. The Cartfile is the declaration of what the app depends on, and Cartfile.resolved records the pinned versions, but the actual build does not run through Carthage. The submodule checkout in the getting started steps is what makes the workspace resolvable, which is why git submodule update --init --recursive is a required step rather than an optional convenience.
Upgrading a dependency follows a different path from installing one. You modify the Cartfile yourself and then run:
carthage update --no-build --use-submodulesThe two flags are the point. --no-build tells Carthage to resolve versions without compiling them, and --use-submodules tells it to check the dependencies out as submodules, which is the layout the project expects. Running this without --use-submodules would leave the workspace pointing at projects that are not there.
Cartfile.private sits alongside the others, and a Cartfile.resolved is committed, so the version set is pinned and reviewable in a pull request. The .swiftlint.yml and .hound.yml files at the top level point to a codebase with lint expectations attached to it.
Seven years between the newest tag and the newest commit
The repository is not archived, and the last push was on 2026-07-27. The release tags tell a different story. The newest published release is 2.1.2 from 2019-06-01, before it 2.1.1 from 2019-04-26 and 2.1.0 from 2018-12-22. The default branch is develop rather than main, which is common in Xcode projects where a release branch carries the shipped binary.
So the maintenance picture has two halves. Commits have continued for years after the last tag, and the CI setup with a test workflow badge, a Codecov badge and a CHANGELOG.md file indicates the project still expects changes to be tested. But no version has been published since June 2019, and nothing in the repository says when or whether another App Store release will come.
That matters for anyone evaluating the project today. If you only want to read the source, the recent push date tells you the code is not frozen. If you want to ship the app to users, you are depending on the App Store build rather than on a tag you can point at, and the version gap between 2.1.2 and the current develop branch is where any behaviour change would live.
The homepage for the project is mattrubin.me/authenticator, which is where a user would look for the shipped binary and any support notice.
Licensing is MIT for the current code, Apache 2.0 for the ancestor
The licence story has two layers and it is worth reading the LICENSE.txt file rather than assuming. The project makes itself available under the terms of the MIT License, and the README carries a badge pointing at LICENSE.txt.
The second layer is historical. The modern Authenticator grew out of the abandoned source for Google Authenticator for iOS, and that original Google code on which this project is based is licensed under the Apache License, Version 2.0. The repository also carries an AUTHORS file, which is the usual signal that individual contributors are credited separately from the project licence.
What this means in practice is not a legal question but a provenance one. Anyone auditing the codebase for its origin will find two licences in the ancestry, and the current MIT grant covers the current work. Anyone redistributing a build should read LICENSE.txt to see exactly what it grants, since the README summary is a pointer rather than the text itself.
There is no contributor licence agreement or code of conduct beyond the CONDUCT.md file, and no CLA text in the repository listing, so there is no document that assigns copyright in a way a large commercial team would normally negotiate first.
Why a single device with no sync is both the strength and the limit
The absence of network access is a feature for some readers and a hard limit for others. For a person who wants a second factor that cannot be phoned home to a vendor, cannot be silently exfiltrated, and does not need an account, the design is exactly right. Add to that the keychain storage, the two standard token algorithms and three ways to enter a secret, and the app does one job well.
But the same choice rules out several things people now expect from an authenticator. There is no multi-device sync, so a new phone means re-scanning every QR code by hand. There is no cloud backup of secrets, which is a deliberate trade-off rather than a missing feature, and it means the recovery path when a device is lost is manual on every service. There is no Android build either, since the app is native Swift for iOS.
The comparison with the abandoned project it grew from is instructive about scope. Google Authenticator for iOS was tied to the Google account system, whereas this fork is standalone and self-contained, with secrets held locally and no connectivity. That difference in approach is the whole product. It also means the app competes on trust and simplicity, not on features, so if you need backup, sync or multi-device, no configuration flag will turn this into a different product.
Editorial conclusion
Authenticator suits an iPhone user who wants one-time passwords generated entirely on device with no account and no network traffic, and it does not suit a team that needs synced secrets across devices, an Android build, or a release process you can audit. Before adopting it, confirm the current tag still matches the App Store build you rely on, because the last release was 2.1.2 on 2019-06-01 even though the last push was on 2026-07-27, and check the Cartfile before touching dependencies, since the checkout flow uses submodules rather than a Carthage build.
Frequently asked questions
How do I build Authenticator from source on a Mac?
Clone the repository, run git submodule update --init --recursive inside the Authenticator directory, then open Authenticator.xcworkspace rather than the .xcodeproj, because opening the project file leaves the dependencies unresolvable. Build and run the Authenticator scheme afterwards.
Does the Authenticator app send my one-time passwords to a server?
No. The app never connects to the internet and your secret keys never leave your device. The stored data is kept in encrypted form on the iOS keychain.
Can Authenticator handle counter-based one-time passwords?
Yes. It supports both time-based and counter-based one-time passwords as standardised in RFC 4226 and RFC 6238, in addition to QR code scanning, otpauth:// URLs and manual entry.
How do I update the dependencies in Authenticator?
Modify the Cartfile, then run carthage update --no-build --use-submodules. The project uses Carthage to manage dependencies but builds them as Xcode target dependencies through submodules, so the --use-submodules flag matters.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mattrubin-authenticator)Was this useful?