OAUTH PREVIEW — Create and manage OAuth clients in the Developer PortalStable portal →

OAuth for native iOS and Android apps

Native apps are public clients: they cannot keep a client secret. Use authorization code with PKCE and launch Lunch Money sign-in and consent in a trusted platform authentication browser—not an embedded WebView.

Use Lunch Money's authorization-server discovery document and configure token_endpoint_auth_method=none. Send the client ID at the token and revocation endpoints, but never invent or embed a client secret. You do not need to configure authorization-request scopes because the registered client receives its complete fixed set. If your OAuth library sends scope, Lunch Money ignores that value and uses the registered set.

Redirects

Use a redirect mechanism that returns control to your app and that your platform can bind to it. Claimed Universal Links or Android App Links provide stronger app ownership than a custom URI scheme when correctly configured. A private-use scheme must contain a dot, such as app.example.demo:/oauth/callback, and must be protected against interception.

Native clients may use an HTTP loopback redirect with localhost, 127.0.0.1, or [::1]. The scheme, host, path, and query string must exactly match the registered URI, but the port may differ so the application can bind an ephemeral port at runtime. The exception applies only to loopback redirects for native clients; native HTTPS and private-use scheme redirects must match every component of the registered URI exactly. The loopback hosts are not interchangeable: a client registered with http://127.0.0.1/cb cannot authorize with http://localhost:4000/cb or http://[::1]:4000/cb, so register the host your app actually binds to.

Store and renew tokens

Store tokens in Keychain on Apple platforms. On Android, use current Keystore-backed storage guidance from your maintained library; do not start new work with deprecated EncryptedSharedPreferences APIs. Coordinate refresh-token use, persist replacements, and fall back to interactive authorization after terminal failure.

After every code exchange, call and strictly validate GET /v2/me before adding the credentials to the app's normal connection store. For an app without its own user accounts, identify each local connection by the returned Lunch Money user id and budget-account account_id pair. Atomically create a record for a new pair or replace the credential set for an existing pair. Two Lunch Money users may authorize the same shared account_id; their credentials must remain in separate records. If identification or persistence fails, discard the temporary credentials instead of saving a partial or unidentified connection. Retain budget_name for display, but never use it as a key because different budgets can share a name.

The platform authentication browser may reuse an existing Lunch Money login, and Lunch Money may let the person use a different user from the authorization screen. An ephemeral-session request is only an intent to avoid shared browser state; it does not prove which user completed authorization. Do not infer identity from browser cookies, callback fields, budget names, or token contents. The validated /v2/me response is authoritative.

Show the active Lunch Money identity when the app may retain more than one, show which authorized budget is active, and provide one separate Authorize another budget action that starts OAuth. Do not require another client-side action for switching Lunch Money users. Selecting an existing connection must not start authorization again, and the app must allow selection only among saved budgets for the active Lunch Money user.

All API calls, refreshes, revocations, local deletions, and cached data must apply to the active user/account connection. Clear or partition profile, configuration, and cached financial data when the identity or budget changes. When the active connection is removed, choose a deterministic remaining connection for that Lunch Money user or enter a disconnected state; never expose another user's hidden connection as a fallback. Use a secondary label such as the account ID only when duplicate visible budget names need disambiguation.

The app may retain credentials for inactive Lunch Money users in secure storage. Switching users hides the previous identity's connections without revoking or deleting them, and authorizing as that identity later may restore its saved budgets. Alternatively, an app may delete credentials during its own sign-out or user-switch lifecycle. Make the chosen behavior understandable without prescribing a particular native control.

Present remote disconnect or revocation separately from forgetting a credential only on the device. Confirm local-only deletion and explain that it leaves the remote authorization active.

Keep application users' connections separate

A native app can support multiple application users on the same device. If your app has its own sign-in system, keep each user's saved Lunch Money connections in a separate storage namespace. Bind the authorization attempt to the application user who started it, and retain the Lunch Money id and account_id returned by GET /v2/me with each credential set. Only load a connection after verifying that it belongs to the currently authenticated application user. Secure device storage does not perform that application-level ownership check for you.

On application sign-out or application-user switching, stop work using the previous application user's credentials and clear in-memory tokens, the active connection selection, and cached financial data. Remove saved credentials if your app does not retain connections across sign-ins; otherwise, make them available only after their application owner signs in again. Do not attach an OAuth callback or refresh result from the previous session to the newly signed-in application user.

If your app has no separate user accounts, the device's OS account and app storage protections provide the local access boundary. Anyone allowed to use the unlocked app may be able to use its saved connections; separate budget-account records alone do not authenticate different people.

Sending tokens to an application backend introduces server-side credential storage. If your architecture does that, apply the credential ownership rules on the backend as well as protecting credentials on the device.

Platform code should delegate protocol validation to the maintained library while your application supplies safe configuration, lifecycle storage, API calls, revocation, and user-facing recovery.

Lunch Money does not currently provide complete Swift or Kotlin samples; the upstream AppAuth projects provide maintained platform examples. Expo developers can use the native Expo sample for a complete Lunch Money walkthrough and reference implementation.

Next: Review OAuth security guidance.