OAuth2/PKCE on Android: Authorization Flow & Token Management
OAuth2 with PKCE (Proof Key for Code Exchange) is the secure standard for mobile authentication. It protects against authorization code interception attacks.
PKCE Code Challenge Generation
class OAuth2Manager(private val clientId: String) {
fun generateCodeChallenge(): Pair<String, String> {
val codeVerifier = generateRandomString(128)
val bytes = codeVerifier.toByteArray()
val md = MessageDigest.getInstance("SHA-256")
val digest = md.digest(bytes)
val codeChallenge = Base64.getUrlEncoder().withoutPadding().encodeToString(digest)
return codeVerifier to codeChallenge
}
private fun generateRandomString(length: Int): String {
val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
return (1..length).map { chars.random() }.joinToString("")
}
}
Authorization Request
fun requestAuthorizationCode(codeChallenge: String) {
val authUrl = Uri.Builder()
.scheme("https")
.authority("oauth.example.com")
.appendPath("authorize")
.appendQueryParameter("client_id", clientId)
.appendQueryParameter("redirect_uri", redirectUri)
.appendQueryParameter("response_type", "code")
.appendQueryParameter("code_challenge", codeChallenge)
.appendQueryParameter("code_challenge_method", "S256")
.build()
val intent = Intent(Intent.ACTION_VIEW, authUrl)
startActivity(intent)
}
Token Exchange with Code Verifier
suspend fun exchangeCodeForToken(
authCode: String,
codeVerifier: String
): TokenResponse = withContext(Dispatchers.IO) {
val body = FormBody.Builder()
.add("grant_type", "authorization_code")
.add("code", authCode)
.add("client_id", clientId)
.add("code_verifier", codeVerifier)
.add("redirect_uri", redirectUri)
.build()
val request = Request.Builder()
.url("https://oauth.example.com/token")
.post(body)
.build()
httpClient.newCall(request).execute().use { response ->
Json.decodeFromString(response.body!!.string())
}
}
Secure Token Storage
class SecureTokenStorage(private val context: Context) {
private val sharedPrefs = EncryptedSharedPreferences.create(
context, "oauth_tokens", context.mainEncryptedSharedPrefs(),
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
fun saveToken(token: TokenResponse) {
sharedPrefs.edit().putString("access_token", token.accessToken).apply()
}
fun getAccessToken(): String? = sharedPrefs.getString("access_token", null)
}
PKCE provides protection against interception attacks on mobile platforms. Always use encrypted storage for tokens and implement token refresh flows.
8 Android app templates available on Gumroad
Top comments (0)