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