iOS and Android
Native apps sign people in through their environment’s Lobby in the system browser, never a web view: people use their passkeys, saved passwords and Usher session, and the app never sees a password. The SDKs do the rest of OpenID Connect for you, and keep the tokens on the device.
Register the app
In Usher Console, open the environment, then Applications → New application → Mobile or desktop. Give it a private-use redirect made from a domain you own, such as com.example.notes:/oauth/callback. It gets a client ID and no secret: an app on a phone can’t keep one, so it uses PKCE instead. Console shows the set-up for both platforms.
iOS and macOS
import UsherKit
let usher = UsherClient(configuration: .init(
issuer: URL(string: "https://login.example.com")!,
clientID: "…",
redirectURI: URL(string: "com.example.notes:/oauth/callback")!))
let session = try await usher.signIn()
var request = URLRequest(url: URL(string: "https://api.example.com/notes")!)
try await usher.authorize(&request)Tokens are kept in the Keychain, on this device only.
Android
// build.gradle.kts: manifestPlaceholders["usherRedirectScheme"] = "com.example.notes"
val usher = Usher.client(context, UsherConfiguration(
issuer = "https://login.example.com",
clientId = "…",
redirectUri = "com.example.notes:/oauth/callback"))
val session = usher.signIn(Usher.customTabs(activity))The sign-in opens in a Custom Tab. The session is encrypted with a key in the Android Keystore.
What the SDKs check
- PKCE, state and nonce on every sign-in, and the issuer named in the answer (RFC 9207).
- The ID token’s signature against your environment’s keys, and its issuer, audience and expiry.
- Refresh tokens rotate: each is used once. Signing out revokes it at Usher.
- DPoP, if you turn it on: tokens bound to a key on the device, useless if copied elsewhere.
To call your own API with the access token, name the API and ask for it with resources: see Your own APIs.