Security

    OAuth 2.0 with PKCE for mobile apps

    A 2026 view of a mobile OAuth Authorization Code flow with PKCE running in the system browser, where an intercepted authorization code is useless without the code verifier

    OAuth on a mobile app is easy to get subtly wrong, and the wrong way leaks tokens. Native apps are public clients: they cannot keep a client secret, and their redirect can be intercepted, which is why the old implicit flow and embedded login web views are no longer the right approach. The recommended pattern is the Authorization Code flow with PKCE, run through the system browser. PKCE closes the gap that makes a public client safe to use OAuth at all. Here is why that flow is the standard for native apps and how to implement it securely.

    Short answer

    For mobile apps, the recommended OAuth approach is the Authorization Code flow with PKCE (Proof Key for Code Exchange), run in the system browser rather than an embedded web view. Per RFC 8252, OAuth 2.0 for Native Apps, native apps are public clients that cannot safely hold a client secret, so PKCE protects the flow by ensuring an intercepted authorization code cannot be exchanged for tokens without the matching verifier. Avoid the deprecated implicit flow and never collect credentials in an embedded web view. Use the system browser through the platform's authentication session, redirect back via a claimed link or custom scheme, and store the resulting tokens in the Keychain or Keystore.

    What you should know

    • Native apps are public clients: they cannot keep a client secret.
    • Use Authorization Code with PKCE: the recommended native flow.
    • PKCE protects the code exchange: an intercepted code is useless without the verifier.
    • Use the system browser: not an embedded web view for login.
    • Store tokens securely: in the Keychain or Keystore.

    Why Authorization Code with PKCE for native apps?

    Because a mobile app cannot protect a client secret, and its redirect can be intercepted. A confidential web server can hold a client secret to prove its identity during the token exchange, but a mobile app ships its code to users, so any embedded secret can be extracted, making the app a public client that cannot rely on a secret. The implicit flow tried to work around this by returning a token directly in the redirect, but that exposes the token to interception, which is why it is deprecated for native apps. The Authorization Code flow with PKCE solves it differently: the app generates a one-time secret, the code verifier, and sends a derived challenge with the authorization request, so when it later exchanges the authorization code for tokens, it must present the verifier. An attacker who intercepts the authorization code cannot exchange it without that verifier, which is what makes the flow safe for a public client.

    What does PKCE protect against?

    Authorization code interception, especially on the redirect. The table shows the model.

    ElementRole
    Code verifierA one-time random secret the app keeps
    Code challengeA derived value sent with the authorization request
    Authorization codeReturned to the app's redirect; useless alone
    Token exchangeRequires the verifier, which only the real app has

    The risk PKCE addresses is that the authorization code comes back to your app through a redirect, a custom scheme or a claimed link, and that channel can be intercepted, for instance by an app that hijacked a custom scheme. Without PKCE, an intercepted code could be exchanged for tokens. With PKCE, the exchange also requires the code verifier, which never left the legitimate app, so a stolen code is worthless. That is why PKCE is essential for mobile, where redirect interception is a realistic threat.

    How do you implement OAuth securely on mobile?

    Use the system browser, PKCE, and secure token storage. Run the authorization request through the platform's secure authentication session, which uses the system browser, ASWebAuthenticationSession on iOS or Chrome Custom Tabs on Android, rather than an embedded web view, because an embedded view can capture the user's credentials and is not the trusted, shared browser session users expect. Use the Authorization Code flow with PKCE, generating a fresh code verifier per request, and do not embed a client secret, since a public client should not have one. For the redirect, prefer a claimed link, a Universal Link or verified App Link, which cannot be hijacked, and if you use a custom scheme, PKCE is what keeps an intercepted code safe. Finally, store the access and refresh tokens in the Keychain or Keystore, not in plain storage, and rely on your backend to validate them. The whole flow assumes the client is untrusted, which is the correct stance.

    What to watch out for

    The first trap is collecting login credentials in an embedded web view, which both undermines trust and can expose credentials; use the system browser. The second is using the implicit flow or embedding a client secret, neither of which suits a public client; use Authorization Code with PKCE. The third is returning tokens to a hijackable custom scheme without PKCE, or storing tokens in plain storage. A pre-submission scan such as PTKD.com (https://ptkd.com) reads the compiled APK, AAB, or IPA against OWASP MASVS and surfaces embedded secrets and insecure token storage, so you can confirm no client secret ships and tokens are stored securely. The flow itself you implement with the platform's authentication session.

    What to take away

    • Native apps are public clients, so use the Authorization Code flow with PKCE, not the deprecated implicit flow or an embedded client secret.
    • PKCE ensures an intercepted authorization code cannot be exchanged for tokens without the verifier, which makes the flow safe on a redirect that can be intercepted.
    • Run login through the system browser via the platform's authentication session, prefer a claimed-link redirect, and store tokens in the Keychain or Keystore.
    • Use a pre-submission scan such as PTKD.com to confirm no client secret is embedded and tokens are stored securely.
    • #oauth
    • #pkce
    • #authentication
    • #native-apps
    • #tokens
    • #owasp-masvs
    • #app-security

    Frequently asked questions

    Why do mobile apps need PKCE for OAuth?
    Because a mobile app is a public client that cannot keep a client secret, and the authorization code comes back through a redirect that can be intercepted. PKCE has the app generate a one-time code verifier and send a derived challenge, so when it exchanges the authorization code for tokens it must present the verifier. An attacker who intercepts the code cannot exchange it without the verifier, which never left the real app. That is what makes the Authorization Code flow safe for a native app.
    Should I use the implicit flow in a mobile app?
    No. The implicit flow returns a token directly in the redirect, which exposes it to interception, and it is deprecated for native apps. Use the Authorization Code flow with PKCE instead, which returns a code that is useless without the verifier and then exchanges it for tokens. The implicit flow was an early workaround for clients that could not keep a secret, but PKCE is the modern, safer answer for public clients like mobile apps.
    Can I use an embedded web view for OAuth login?
    You should not. An embedded web view can capture the user's credentials and is not the trusted, shared system browser session users expect, which both undermines trust and weakens security. Use the platform's secure authentication session, ASWebAuthenticationSession on iOS or Chrome Custom Tabs on Android, which runs in the system browser. That keeps the user's credentials with the identity provider and out of your app, as the native OAuth guidance recommends.
    Should a mobile app have an OAuth client secret?
    No. A mobile app ships its code to users, so any embedded client secret can be extracted, which means a native app is a public client that should not rely on a secret. Instead of a client secret, use PKCE, which provides the proof that the token exchange comes from the legitimate app. If a configuration asks for a client secret in a mobile app, that is a sign the flow is set up for a confidential client rather than a public one.
    How do I confirm my OAuth setup is secure?
    Scan the build and check token storage. A pre-submission scan such as PTKD.com reads the compiled binary against OWASP MASVS and surfaces embedded secrets and insecure token storage, so you can confirm no client secret ships in the app and the access and refresh tokens are stored in the Keychain or Keystore rather than plain storage. Combined with using the system browser, PKCE, and a claimed-link redirect, that covers the main ways mobile OAuth goes wrong.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free