Implementing OAuth 2.0 with PKCE: A Guide for Mobile Developers
We often see projects where OAuth 2.0 authorization is implemented using outdated or insecure schemes. We use Authorization Code Flow with PKCE—the only correct option for native apps. Implicit Flow has been deprecated with RFC 9700, and Client Credentials does not provide user context. If you're offered Implicit Flow for a mobile app—that's a red flag.
How PKCE Works
PKCE (Proof Key for Code Exchange) solves the problem of missing client_secret in mobile code. The app generates a random code_verifier, hashes it with SHA-256, and sends the code_challenge to the authorization server. When exchanging the code for a token, the server verifies that the verifier matches the challenge. Intercepting the code and using it without a verifier is impossible.
// iOS - generating verifier var buffer = [UInt8](repeating: 0, count: 32) _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer) let verifier = Data(buffer).base64URLEncodedString() // Android val bytes = ByteArray(32) SecureRandom().nextBytes(bytes) val verifier = Base64.encodeToString(bytes, Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP) Universal Links vs Custom URL Scheme for OAuth 2.0
A scheme like myapp://callback can be intercepted by any app that registers the same identifier. Universal Links on iOS and App Links on Android verify domain digital certificates through apple-app-site-association or assetlinks.json. Only your site can be authorized to open your app.
Setting this up requires:
- AASA file at
https://yourapp.com/.well-known/apple-app-site-association - Capability
Associated Domainswithapplinks:yourapp.com - Handling URLs in
scene(_:continue:)orapplication(_:continue:restorationHandler:)
Libraries for OAuth 2.0 in Mobile Apps
Do not implement the protocol from scratch. AppAuth (iOS/Android), react-native-app-auth, and flutter_appauth handle PKCE, browser opening, and redirect processing. AppAuth is more reliable and faster than a custom implementation—reducing development time by 3x and eliminating 80% of common vulnerabilities.
| Library | Platform | PKCE | Status |
|---|---|---|---|
| AppAuth | iOS / Android | Yes | Recommended |
| react-native-app-auth | React Native | Yes | Recommended |
| flutter_appauth | Flutter | Yes | Recommended |
Token Storage
Keep the short-lived access token in memory (e.g., @State in SwiftUI). Store the refresh token in a protected storage: Keychain on iOS, EncryptedSharedPreferences on Android. Over 70% of mobile apps store tokens insecurely—don't be one of them. Intercepting such a token from SharedPreferences on a rooted device is a real attack vector.
Refresh Token Rotation and Compromise Detection
Refresh token rotation is a mandatory practice for production apps. With each successful exchange, the server issues a new access token and immediately invalidates the old refresh token. If an attacker steals a token and tries to use an already invalid one, the server detects the conflict and revokes the entire session. Keycloak enables rotation with revokeRefreshToken: true, AWS Cognito uses the Refresh Token Revocation parameter.
Recommended lifetimes: access token 15–60 minutes, refresh token 30–90 days with sliding-window extension on each use. Always implement logout with server-side token revocation—call the /revoke endpoint per RFC 7009. A long-lived refresh token without logout revocation is the most common source of account compromise in mobile apps.
State Handling and CSRF Protection
The state parameter is a random string the app sends in the authorization request and checks in the redirect. Without state checking, a CSRF attack is possible: an attacker substitutes someone else's authorization code, and the app links their account to the victim's session. AppAuth generates and validates state automatically. If you write an OAuth client manually, state is mandatory. Audit-logging of each authorization request with device fingerprint, IP, and User-Agent helps detect anomalies. Our experience with 30+ OAuth integrations shows that event logs catch unauthorized access incidents within minutes.
Comparison of OAuth Flows
| Flow | Security | Mobile Support | Status |
|---|---|---|---|
| Implicit | Low (token in URL) | No (RFC 9700 deprecated) | Deprecated |
| Authorization Code | Medium (requires client_secret) | Partial | Rare |
| Authorization Code + PKCE | High | Full | Recommended |
Step-by-Step Implementation on iOS
- Install AppAuth via CocoaPods or SPM.
- Configure
ASWebAuthenticationSessionto open the browser. - Implement generation of
code_verifierandcode_challenge. - Handle redirect via Universal Links.
- After receiving tokens, store the access token in memory and the refresh token in Keychain.
- Implement automatic token refresh on expiration.
What's Included in Our Work
We provide: integration documentation, access to test environments, team training, and post-launch support. Our service starts at $2,500 for a single provider integration, and we guarantee a 30% reduction in security audit findings. Our 5+ years of experience in mobile security ensures your solution passes App Store Review (sections 4.8, 5.1). We implement the integration turnkey in 5–10 business days for one provider.
Typical Mistakes
Common errors include: using Implicit flow, storing tokens in UserDefaults, and lack of PKCE. We are a team of OAuth-certified engineers—over 100 apps audited, 70% found insecure token storage initially. Let us review your code and provide a free project assessment. Leave a request—we'll pinpoint your case.







