Beta

Create a tenant

A new tenant starts with its own users, OAuth settings, audit history and logs. You are its first Tenant Admin.

BTL Admin

Native applications

The printer's phone app lets you order prints from your phone. It is installed from an app store, runs on your device, and is registered with the photo service as its own public client, photo-printer-app. When you tap Connect photo account, the app needs what the website needed, an authorization code from the photo service, but it has no server-side callback page and no secret it can keep.

It also has an option the website never had. An app controls its own screens, so it could show the photo service's sign-in page inside one of them. That option is the first thing to rule out.

Signing in through the browser

Mobile and desktop platforms let an app display web content in a web view, a browser component embedded in the app and controlled by it. A web view is convenient, and for OAuth it is the wrong tool. The app that hosts it can read and change the page. It can record what you type into the sign-in form, including your photo account password, copy the photo service's session cookies, and even approve the consent screen on your behalf. The whole point of the exchange is that the printer never handles your photo account credentials, and a web view hands them straight back.

It is worse for you in quieter ways too. A web view keeps its own cookies, separate from your browser, so you sign in from scratch even when you are already signed in to the photo service in your browser. And the app decides what to draw around the page. There is no address bar you can trust to show which site you are typing into, so a careful person has no way to check, and everyone else learns that typing a password into an unidentified page is normal.

Current guidance for native apps therefore requires the authorization request to run in an external user agent, normally the system browser, which the app can neither inspect nor alter. Switching to the browser app works. Most platforms also offer something smoother, an in-app browser tab, which appears over the app but is still the browser, with the browser's cookies and security checks and outside the app's control. ASWebAuthenticationSession on Apple platforms and Custom Tabs on Android are examples, and the guidance recommends them where they are available. Authorization servers may go further and refuse sign-ins they detect coming from a web view.

A public client on every phone

Every copy of the app is identical, so anything built into it, including a client secret, can be extracted from any one copy. The photo service registers photo-printer-app as a public client with no secret, and requires PKCE from it. The authorization request has the same shape as the website's, with the app's client ID, its own redirect URI, and a challenge made from a fresh verifier.

The response then has to find its way from the browser back into the app. The Redirect URIs and client metadata lesson described the three ways installed applications receive it: a claimed HTTPS address that the operating system has verified belongs to the app, a private-use URI scheme, and a loopback address on desktop systems. The phone app uses its claimed address, https://app.printer.example/oauth/callback. When the browser reaches it, the system hands the response to the app, which checks state and the issuer against its pending attempt exactly as the website does.

The app then redeems the code itself. All domains, tokens, and keys in these examples are fictional.

POST /token HTTP/1.1
Host: auth.photos.example
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=demo-code-12
&redirect_uri=https%3A%2F%2Fapp.printer.example%2Foauth%2Fcallback
&code_verifier=btl_training_verifier_for_the_phone_app_only_2026
&client_id=photo-printer-app

Compare it with the website's request. There is no Authorization header, because the app has no credential to authenticate with. It names itself with client_id, and the verifier is the only proof that this copy of the app started the attempt. A code intercepted on its way back, for example by another app that registered the same private-use scheme, is useless without it.

The missing credential has one more consequence. Any software can send a request that claims to be photo-printer-app, so the photo service cannot be sure which app it is dealing with. Current guidance says it should not approve such a request silently just because you approved the app before, and should treat the request as new unless it can confirm the app's identity. A claimed HTTPS redirect can serve as that confirmation, since only the genuine app can receive responses at that address.

Keeping tokens on the device

The token response gives the app an access token and, when the photo service allows continued access, a refresh token. The app needs that refresh token next week, so it has to store it somewhere that survives the app being closed.

Apps have private files that other apps cannot read, but ordinary files are copied into backups and can be read by anything that gains access to the app's storage. The platforms offer better places. On Apple platforms that is the Keychain, which holds small secrets such as tokens, encrypted by the system, and can restrict an item to this device so that it is not restored onto a different phone from a backup. On Android, the app usually encrypts the token with a key held in the Android Keystore, which keeps the key itself out of the app's files, and stores only the encrypted value.

Because the app is a public client, the photo service also rotates its refresh tokens, the protection the lesson on the refresh token grant described for clients that cannot authenticate. The app saves each replacement in secure storage as soon as it arrives.

Signing out of the app works like Disconnect on the website: revoke the refresh token at the photo service, then delete both tokens from storage. If you delete the app without signing out, the refresh token remains valid at the photo service until it expires or you remove the app from your list of connected applications there.

One registration still covers every installation of the app. Some services go further, registering each installation as a separate client with credentials of its own, or asking the platform to attest that a request comes from a genuine, unmodified copy of the app. Advanced OAuth covers per-installation registration in Registering a client dynamically.

Try it in the Lab

PUT IT INTO PRACTICE

Check your understanding

Try these questions before moving on. If an answer isn't right, use the feedback and try again.

0 of 2 answered correctly

Enable JavaScript to answer these questions and save progress in this browser.

QUESTION 1 OF 2A developer wants to show the photo service's sign-in page in a web view inside the printer app. What is the main problem?

QUESTION 2 OF 2Why does the app's token request include a PKCE verifier but no client authentication?

We value your privacy

We use cookies and similar technologies to enhance your browsing experience, and analytics to understand our traffic. By clicking "Allow All", you consent to optional analytics. Cookie Policy

Learn identity