Security: OAuth, Tokens & Encryption

📖 Concept

Mobile security is a critical topic for senior Android developers. Google expects you to understand authentication flows, secure key storage, and network security at a deep level.

OAuth 2.0 + PKCE flow for mobile:

1. App generates code_verifier (random) + code_challenge (SHA256 hash)
2. App opens browser with auth URL + code_challenge
3. User authenticates in browser
4. Server redirects back to app with authorization code
5. App exchanges code + code_verifier for access_token + refresh_token
6. App uses access_token for API calls
7. When token expires, use refresh_token to get new access_token

PKCE (Proof Key for Code Exchange) prevents authorization code interception — essential for mobile apps where you can't safely store a client secret.

Token storage:

  • Use EncryptedSharedPreferences or Android Keystore for tokens
  • NEVER store tokens in plain SharedPreferences or logs
  • Clear tokens on logout

Certificate pinning: Prevents man-in-the-middle attacks by verifying the server's certificate matches a known hash. OkHttp supports this natively.

Network security config (Android 9+):

  • Cleartext traffic blocked by default
  • Custom trust anchors per domain
  • Debug-only CA certificates for testing with proxies

💻 Code Example

codeTap to expand ⛶
1// OAuth 2.0 token management
2class AuthManager @Inject constructor(
3 private val tokenStorage: TokenStorage,
4 private val authApi: AuthApi
5) {
6 suspend fun getValidAccessToken(): String {
7 val currentToken = tokenStorage.getAccessToken()
8 if (currentToken != null && !isExpired(currentToken)) {
9 return currentToken.value
10 }
11 return refreshToken()
12 }
13
14 private suspend fun refreshToken(): String {
15 val refreshToken = tokenStorage.getRefreshToken()
16 ?: throw AuthException("No refresh token — re-login required")
17 val response = authApi.refreshToken(
18 RefreshTokenRequest(refreshToken = refreshToken)
19 )
20 tokenStorage.saveTokens(response.accessToken, response.refreshToken)
21 return response.accessToken
22 }
23}
24
25// OkHttp interceptor for automatic token injection + refresh
26class AuthInterceptor @Inject constructor(
27 private val authManager: AuthManager
28) : Interceptor {
29 override fun intercept(chain: Interceptor.Chain): Response {
30 val token = runBlocking { authManager.getValidAccessToken() }
31 val request = chain.request().newBuilder()
32 .header("Authorization", "Bearer $token")
33 .build()
34 val response = chain.proceed(request)
35
36 if (response.code == 401) {
37 response.close()
38 // Token expired mid-flight — refresh and retry
39 val newToken = runBlocking { authManager.refreshToken() }
40 val newRequest = chain.request().newBuilder()
41 .header("Authorization", "Bearer $newToken")
42 .build()
43 return chain.proceed(newRequest)
44 }
45 return response
46 }
47}
48
49// Certificate pinning with OkHttp
50val client = OkHttpClient.Builder()
51 .certificatePinner(
52 CertificatePinner.Builder()
53 .add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
54 .add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") // Backup pin
55 .build()
56 )
57 .build()
58
59// Encrypted token storage
60class TokenStorage @Inject constructor(
61 @ApplicationContext context: Context
62) {
63 private val prefs = EncryptedSharedPreferences.create(
64 "secure_prefs",
65 MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(),
66 context,
67 EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
68 EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
69 )
70
71 fun saveTokens(access: String, refresh: String) {
72 prefs.edit()
73 .putString("access_token", access)
74 .putString("refresh_token", refresh)
75 .apply()
76 }
77
78 fun getAccessToken(): String? = prefs.getString("access_token", null)
79 fun getRefreshToken(): String? = prefs.getString("refresh_token", null)
80 fun clearTokens() { prefs.edit().clear().apply() }
81}

🏋️ Practice Exercise

Practice:

  1. Implement OAuth 2.0 + PKCE login flow using AppAuth library
  2. Create an OkHttp Authenticator that handles token refresh without racing
  3. Set up certificate pinning with a backup pin
  4. Implement EncryptedSharedPreferences for secure token storage
  5. Configure network_security_config.xml for your production and debug environments

⚠️ Common Mistakes

  • Storing tokens in plain SharedPreferences — easily extractable on rooted devices

  • Not implementing token refresh — users get logged out when access token expires

  • Using a single certificate pin without backup — certificate rotation will break your app

  • Running OAuth flow in a WebView instead of Custom Tabs — WebView can intercept credentials

  • Logging access tokens — accidental token exposure in logcat/crash reports

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for Security: OAuth, Tokens & Encryption. Login to unlock this feature.