Makrosites

Suas ideias em realidade digital!

Login JWT no Android com Refresh Token automático (Interceptor avançado)

Login JWT no Android com Refresh Token automático (Interceptor avançado)

Por que implementar Refresh Token no Android?

Quando usamos JWT, o token normalmente expira em poucos minutos. Sem refresh automático, o usuário precisaria fazer login novamente.

A solução é implementar um Interceptor que detecta erro 401 e automaticamente solicita um novo token.


Fluxo correto de autenticação


1️⃣ Modelo de resposta no Android

data class AuthResponse(
    val token: String,
    val refresh_token: String,
    val expires_in: Int
)

2️⃣ Salvando Tokens

object TokenManager {

    private const val PREF_NAME = "auth_prefs"
    private const val KEY_TOKEN = "token"
    private const val KEY_REFRESH = "refresh"

    fun save(context: Context, token: String, refresh: String) {
        val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
        prefs.edit().apply {
            putString(KEY_TOKEN, token)
            putString(KEY_REFRESH, refresh)
            apply()
        }
    }

    fun getToken(context: Context): String? {
        return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
            .getString(KEY_TOKEN, null)
    }

    fun getRefresh(context: Context): String? {
        return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
            .getString(KEY_REFRESH, null)
    }
}

3️⃣ Interceptor para adicionar Authorization

class AuthInterceptor(
    private val context: Context
) : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {

        val token = TokenManager.getToken(context)

        val request = chain.request().newBuilder()
            .addHeader("Authorization", "Bearer $token")
            .build()

        return chain.proceed(request)
    }
}

4️⃣ Interceptor avançado com Refresh automático

class RefreshInterceptor(
    private val context: Context,
    private val apiService: ApiService
) : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {

        var request = chain.request()
        val response = chain.proceed(request)

        if (response.code == 401) {

            synchronized(this) {

                val refreshToken = TokenManager.getRefresh(context)

                val refreshResponse = apiService.refreshToken(refreshToken!!)
                    .execute()

                if (refreshResponse.isSuccessful) {

                    val newToken = refreshResponse.body()?.token
                    val newRefresh = refreshResponse.body()?.refresh_token

                    TokenManager.save(context, newToken!!, newRefresh!!)

                    request = request.newBuilder()
                        .header("Authorization", "Bearer $newToken")
                        .build()

                    return chain.proceed(request)
                }
            }
        }

        return response
    }
}

5️⃣ Endpoint Refresh na API PHP

Seu backend deve ter algo como:

POST /auth/refresh

Veja implementação completa aqui:


Boas práticas importantes