Native & Mobile Apps
Signing users in from an installed iOS or Android app, where there is no cookie jar to keep the refresh token in.
What changes on a phone
In a browser, Canopy returns the refresh token as an httpOnly cookie. The browser stores it, sends it automatically, and forbids page scripts from reading it, so a script injected into your site cannot steal it.
An installed app has none of that machinery. There is no cookie jar working in the background, so the token comes back in the response body and your app stores it.
That makes storage your responsibility. Put it in the iOS Keychain or the Android Keystore, the OS-provided secure storage, not in UserDefaults, SharedPreferences, or a file you manage yourself.
Register a native key
Delivery follows the key, not the request. Create a key for the app and mark it native:
iOS app), and set Used by to Native.pk_… value in your app and send it as publishable_key on POST /v1/identity/auth/login, exactly as a browser app would.Sessions created with that key return refresh_token in the JSON body and set no cookie. Nothing else about the call changes.
Delivery is fixed when the session is created
A session records how it delivers its refresh token when it is created, and keeps that for its whole life. A session started by a web key stays on cookies; one started by a native key stays on body delivery, through every rotation.
This is worth understanding rather than working around, because it is what stops a real attack. Publishable keys are public by design: a native key can be extracted from any app binary. If /refresh chose delivery from the key on the request, a script injected into your website could present your native key and be handed the refresh token in the response body. The browser attaches the httpOnly cookie automatically, so the script would never need to read the cookie to spend it.
So there is no parameter for this. /refresh accepts no publishable key at all: it reads the mode off the session. If you need to move a user from one mode to the other, sign them in again.
Rotation on unreliable networks
Refresh tokens are single-use. Every refresh returns a new one and retires the old, and presenting a retired token normally revokes the whole session family: that is the response to theft.
Mobile networks make that awkward. If the response is lost after Canopy has already rotated, your app still holds the old token and will retry with it. Canopy recognises that: a replay is treated as a harmless retry when the token was rotated moments ago and its successor was never used.
An already-used successor means two parties hold tokens from the same chain, which is theft rather than a dropped response, and that does end every session for the identity. Your app does not need to do anything special; just retry the refresh with the token you have.
One environment, one key per app
One environment can hold several keys, and normally does. A web key for your site, a native key for the phone app: same identities, same roles, same hierarchy, just different front doors.
This is the expected arrangement, not a workaround. Keep one key per app so you can retire one without touching the others.
Revoking a native key
Revoking a key stops new sign-ins with it. Sessions already signed in keep working: revoking does not sign anyone out.
Tell us how we can improve this guide.