Skip to main content
Migrate Already selling? Move your customers to Keylight without re-issuing a single key.
Keylight
Blog
macos swift security

Where to Store License Data on macOS

3 min read Nicolas Demanez — Founder

Once your Mac app has a license, it has to put it somewhere. The obvious candidates are the Keychain, UserDefaults, and a file on disk. They are not equivalent, and the wrong choice leaks license data or loses it on reinstall. Here is how to pick.

First, what you are storing

The decision is easier once you are precise about what a Mac app stores for licensing. It is usually two things:

  1. The license itself — a signed lease, or the key that resolves to one. With modern licensing this is a signed document: tamper-evident, so editing it breaks the signature.
  2. Cached validation state — the last-known-good result and when the app should next revalidate online, so the app can launch offline.

Hold on to one fact throughout: because the license is signed, no storage location can make it forgeable. A signed lease in a plain text file is still cryptographically tamper-proof. So storage is not about forgery — it is about how easily the data can be read, copied, or wiped, and how well it survives.

The three options

UserDefaults is the path of least resistance — defaults is one line of code. But UserDefaults is a plain plist file in the user’s Library. Any process running as that user can read it and edit it. Storing a license there means the license is trivially copyable from one Mac to another by hand. The signature still stops forgery, but it does nothing to stop a customer copying their signed license to three friends. UserDefaults is genuinely fine for non-sensitive cached state — “next revalidation due on this date” — and a poor choice for the license itself.

A file you manage — JSON in Application Support, say — gives you control over format and location, and it is reasonable for the cached validation state. But a plain file has the same exposure as UserDefaults: readable and copyable by the user and any process they run. If you go this route for anything sensitive you are signing up to encrypt it yourself, and rolling your own encryption is rarely the best use of your time.

The Keychain is purpose-built for exactly this. It is encrypted at rest, access-controlled by the system, and — importantly — it survives app reinstalls and updates, because it is not inside your app’s container. A customer who reinstalls your app does not lose their license. The cost is a slightly more verbose API than UserDefaults, which is a small price.

The sensible default

For the license itself, use the Keychain. It is encrypted, it is access-controlled, it persists across reinstalls, and it does not leave your customer’s license sitting in a world-readable plist. This is the single most defensible default for Mac licensing storage.

For non-sensitive cached state — a “revalidate after” date, a UI flag — UserDefaults is fine and convenient. There is no need to put a timestamp in the Keychain.

Storing the license in the Keychain is also kinder to your customers, not just more secure. Keychain persistence means the “I reinstalled and lost my license” support ticket largely disappears, because the license is still there after the reinstall.

You probably should not hand-roll this

Here is the practical reality: getting Keychain storage right — the correct accessibility attributes, behaviour across reinstalls, migration when your storage format changes — is fiddly, and it is the kind of code that fails silently and quietly until a customer hits the edge case.

A licensing SDK handles storage for you. The KeylightSDK persists the signed lease and the validation state itself; your code never touches the Keychain directly. You create the manager and ask for the state:

import KeylightSDK

// The SDK reads the persisted, cached lease and resolves the state.
await licensing.checkOnLaunch()

switch licensing.state {
case .licensed:     enablePaidFeatures()
case .trial(let d): showTrialBanner(daysLeft: d)
case .expired:      showRenewalPrompt()
case .invalid:      showActivationSheet()
}

If you do store licensing data yourself, the rule is simple: the license goes in the Keychain, non-sensitive cached state can go in UserDefaults, and you do not invent your own encryption. If you have a storage edge case worth a deeper post, send us your feedback.

Frequently asked

Where should a Mac app store its license key?+

The Keychain is the best default. It is encrypted, access-controlled, and survives app reinstalls. UserDefaults and plain files are readable and editable by anyone, which is fine only for non-sensitive data.

Is it safe to store a license key in UserDefaults?+

UserDefaults is a plain plist any user or process can read and edit. A signed license stored there cannot be forged, but it can be trivially copied — the Keychain is the better choice.

Does it matter where I store a signed license?+

Storage location does not affect forgery resistance — a signed license is tamper-evident anywhere. It affects how easily the license can be read, copied, or wiped, which is why the Keychain is preferred.

Ready to ship?

Create your account and start licensing your apps in under a minute. Free forever tier included.

Start Free