The Token Tango: Seamless Authentication in Android Apps 🎺🔐

Swatiomar
5 min readFeb 2, 2025

--

Learn from yesterday, live for today, hope for tomorrow. The important thing is not to stop questioning.
– Albert Einstein

Imagine your app is a nightclub 🎉, and the access token is the VIP pass that lets users in. But here’s the catch: This pass expires every few hours for security reasons. When it does, users get kicked out of the club, and nobody likes that! 🚪😠

How do we keep users in the party without constant re-entry hassles? The answer lies in refresh tokens — the backstage pass that quietly gets you a new VIP pass without making a scene.

Let me tell you how to keep the party going with a simple solution: The Token Tango

The VIP Pass (Access Token) 🎟️

When users log in, your app gets an access token from the server. This token is like a golden ticket that lets users access their data and perform actions.

val accessToken = "eysdsdhsadusdgjbcjshciugdfiasghsai.."

But this token has a short lifespan — maybe 1 hour. After that, it expires, and the server starts rejecting requests with a 401 Unauthorized error.

The Backup Pass (Refresh Token) 🔄

To avoid kicking users out every hour, the server also gives you a refresh token. This is a special key that can generate a new access token without asking the user to log in again.

val refreshToken = "xhdhasgdhxbzkxnakdhakzanxkzbxhckzc…"

Think of it as a backstage pass that lets you get a new VIP pass whenever the old one expires.

🎤 Act 1: Understanding Access and Refresh Tokens

Most secure APIs require an access token to authenticate requests. However, these tokens are short-lived for security reasons. Instead of asking users to log in repeatedly, we use a refresh token to get a new access token when the current one expires.

  • Access Token: Short-lived, used to authenticate API calls.
  • Refresh Token: Longer-lived, used to obtain a new access token when the old one expires.

How It Works:

  1. User logs in and receives an access token + refresh token.
  2. App uses the access token for API calls.
  3. When the access token expires, the app makes a request using the refresh token to get a new one.
  4. The new access token is used for subsequent requests, and the cycle continues.
Image from goolg eimage search

🎉 Act 2: Implementing Token Refresh in Android

Step 1: Store Tokens Securely

Tokens should be stored securely to prevent unauthorized access. Use EncryptedSharedPreferences for storing refresh tokens:

val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()

val sharedPreferences = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

Step 2: Handle Unauthorized Requests

We need an OkHttp Interceptor to check for 401 Unauthorized responses and refresh the token automatically.

class AuthInterceptor(private val tokenManager: TokenManager) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer ${tokenManager.getAccessToken()}")
.build()

val response = chain.proceed(request)

if (response.code == 401) { // Token expired, attempt refresh
synchronized(this) {
val newToken = tokenManager.refreshToken()
return if (newToken != null) {
chain.proceed(
request.newBuilder()
.addHeader("Authorization", "Bearer $newToken")
.build()
)
} else {
response
}
}
}
return response
}
}

Step 3: Refresh the Token with Request Queueing

The TokenManager handles refreshing the token while ensuring that multiple API requests don’t trigger concurrent refresh calls. Token refresh should be handled asynchronously using coroutines instead of execute(), which is blocking.

class TokenManager(private val apiService: ApiService, private val prefs: SharedPreferences) {
private var isRefreshing = false
private val failedRequestQueue = mutableListOf<() -> Unit>()

fun getAccessToken(): String? {
return prefs.getString("access_token", null)
}

fun setRefreshing(value: Boolean) {
isRefreshing = value
}

suspend fun refreshToken(): String? {
val refreshToken = prefs.getString("refresh_token", null) ?: return null
return try {
val response = apiService.refreshToken(RefreshTokenRequest(refreshToken)).await()
if (response.isSuccessful) {
val newToken = response.body()?.accessToken
prefs.edit().putString("access_token", newToken).apply()
retryFailedRequests()
newToken
} else {
null
}
} catch (e: Exception) {
null
}
}

private fun retryFailedRequests() {
failedRequestQueue.forEach { it.invoke() }
failedRequestQueue.clear()
}
}

Step 4: Add Interceptor to Retrofit

val okHttpClient = OkHttpClient.Builder()
.addInterceptor(AuthInterceptor(tokenManager))
.build()

val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()

🔥 Act 3: Handling Expired Tokens During Button Clicks & Multiple Requests

What Happens If a User Clicks a Button and the Token Expires?

  1. The API request is sent with an expired token.
  2. The server responds with a 401 Unauthorized error.
  3. The AuthInterceptor detects the expired token and triggers a refresh.
  4. The TokenManager ensures that only one refresh call is made at a time.
  5. Any other API requests that failed due to token expiration are queued.
  6. Once the new token is obtained, all queued requests are retried with the new token.

🎯 Real-World Scenarios

Music Streaming App (Spotify-like) 🎵
Every API (fetch playlists, stream songs, like a song) requires an access token.If the token expires while streaming, the app refreshes it without interrupting playback.

E-commerce App (Amazon-like) 🛒
Every request (fetch cart, add items, checkout) needs authentication.If a token expires mid-session, the cart remains intact while the token refreshes in the background.

🔥 Keep the Party Going

With this setup, users won’t even notice when their token expires — they’ll just keep enjoying the party! 🕺. The app automatically refreshes it in the background, ensuring a smooth and secure experience.

Why This Matters

a. Security: Short-lived tokens reduce the risk of misuse.

b. User Experience: No annoying logouts or interruptions.

c. Simplicity: A few lines of code can make a huge difference.

Key Takeaways:

✅ Implement refresh tokens for seamless authentication.
✅ Securely store tokens using EncryptedSharedPreferences.
✅ Implement an OkHttp Interceptor to refresh tokens automatically.
Queue failed requests and retry them once a new token is available.
✅ Ensure thread safety when refreshing tokens to prevent multiple requests.

And that’s how you master the Token Tango! Keep your app secure, keep your users happy, and keep the dance floor alive! 🕺✨

Happy coding! :)

I hope you enjoyed this exploration. I’d love to hear your thoughts and feedback in the comments. Stay tuned for more deep dives into Android development!

Don’t forget to follow me on Medium for more content like this. If you found this helpful, Please share and give it a clap below so others can discover it too!

Thanks for reading!

--

--

Swatiomar
Swatiomar

Written by Swatiomar

As a programmer, to keep learning and help ones who needs the skills to be a master, I am available to be a part of an adventurous journey….

No responses yet