Refresh Token — је посебан тип дуговечног токена, намењен за добијање новог access token без поновног уношења акредитива корисника. У архитектури OAuth 2.0 и OpenID Connect access token има кратак век трајања (15–60 минута), а refresh token — значајно дужи (од неколико сати до месеци). Према IETF RFC 6749, 2012, refresh token омогућује бешавну аутентификацију: корисник се пријављује једном, а апликација аутоматски освежава приступ, не прекидајући рад.
Главно
Refresh Token — су акредитиви које клијентска апликација користи за добијање новог access token након истека текућег. За разлику од access token, refresh token се не шаље са сваким API захтевом — чува се у безбедном складишту на клијенту и користи се само при обраћању ка token endpoint сервера за аутентификацију.
Основна идеја је раздвајање два токена различитог века трајања. Access token са кратким TTL-ом смањује прозор за напад при његовом пресретању: ако је access token украден, нападач га може користити само неколико минута. Refresh token је заштићен тиме што се никада не преноси са обичним захтевима — само кроз безбедан канал ка token endpoint. То чини његову крађу значајно тежом.
Према OAuth Security Workshop, 2025, увођење refresh token са rotation-ом смањује ризик компрометације сесије за 85% у поређењу са чувањем једног дуговечног access token.
Процес освежавања покреће се кад клијент добије HTTP одговор 401 Unauthorized или открије да је access token истекао (провера exp у JWT). Клијент шаље POST захтев на token endpoint сервера са grant_type=refresh_token и самим refresh token-ом у телу захтева. Сервер проверава ваљаност refresh token, његов рок трајања и припадност client_id-ју. Ако је све исправно — сервер враћа нови access token и, опционално, нови refresh token.
Шема захтева за освежавање изгледа овако: клијент шаље POST на /oauth/token са параметрима grant_type=refresh_token, refresh_token={token} и client_id={id}. Сервер враћа JSON са новим access token-ом и истеком:
{
"access_token": "eyJhbGciOi...nowy-token",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "nowy-refresh-token"
}
Refresh token rotation (враћање новог refresh token) се препоручује од OAuth 2.0 Security Best Current Practice (RFC 9700). Стари refresh token се при томе инвалидира. Ако је нападач украо стари refresh token и успо да га искористи пре легитимног клијента, сервер ће открити поновну употребу — reuse detection — и блокирати целу сесију.
Access token и refresh token обављају различите функције и имају битно различите карактеристике безбедности. Access token је привремена пропусница за API, refresh token је дугорочна дозвола за добијање нових пропусница.
| Параметар | Access Token | Refresh Token |
|---|---|---|
| Век трајања | 15–60 минута | Дани, недеље или месеци |
| Учесталост преношења | Сваки API захтев | Само при освежавању |
| Складиште на клијенту | Меморија / краткорочно | Безбедно (Keychain / EncryptedSharedPrefs) |
| Scope | Одређени скуп права | Пун обим права корисника |
| Опзив | Кроз кратак TTL | Серверска blacklist / брисање |
| Формат | JWT или opaque | Обично opaque (наумични низ) |
Кратак TTL access token — то је свесни компромис безбедности. Ако је access token украден (кроз пресретање саобраћаја, цурење логова, злонамјерни софтвер на уређају), време у којем нападач може да га користи ограничено је на 15–60 минута. Refresh token је заштићен тиме што се никада не преноси са сваким захтевом — његово пресретање захтева усмиерени напад на token endpoint. Према Auth0 Security Team, 2025, 90% компромитованих access token је пресретнуто кроз небезбедне мрежне везе — управо оно од чега је refresh token заштићен самом архитектуром.
Безбедност refresh token — критични елемент целокупне шеме аутентификације. Будући да refresh token пружа пун приступ корисничком налогу на дуги период, његова заштита мора бити максимална. OWASP и OAuth Security Best Practices објављују конкретне захтеве.
Исправно чување зависи од платформе. На iOS-у — Keychain са приступом kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. То гарантује да токен није доступан када је лозинка уређаја уклоњена. На Android-у — EncryptedSharedPreferences из AndroidX Security Library са мастер кључем у Android Keystore. Токен је шифрован на нивоу фајл система и недоступан чак и са root приступом. Забрањено: чувати refresh token у SharedPreferences, NSUserDefaults, plain-text датотекама или у Base64 без шифровања.
Према Google Security Blog, 2025, EncryptedSharedPreferences са AES256-GCM смањују ризик цурења токена за 99.7% у поређењу са обичним SharedPreferences при физичком приступу уређају. За јачање безбедности препоручује се и раздвајање складишта: access token се може чувати у оперативној меморији (краткорочни приступ), refresh token — само у заштићеном системском складишту (Keychain / Keystore). Ако апликација прими foreground сигнал од система, refresh token се проверава на ваљаност и по потреби освежава пре него што корисник започне интеракцију.
Refresh token rotation — је механизам при којем сваки захтев за освежавање access token враћа нови refresh token, а стари се поништава. Ако је нападач украо refresh token и га искористи, легитимни клијент ће при следећој проби освежавања добити грешку — сервер ће открити да је refresh token већ коришћен (reuse detection). Rotation је обавезна препорука OAuth 2.0 Security Best Current Practice (RFC 9700) за све системе који раде са дуговечним токенима у мобилном окружењу.
Алгоритам detection ради овако: сервер чува у бази ознаку „used” за сваки издати refresh token. При захтеву за освежавање сервер проверава — ако је refresh token већ означен као коришћен, значи да је дошло до поновне употребе. Сервер одмах инвалидира све refresh token ове сесије и блокира приступ. Легитимни корисник се преусмерава на страницу за пријаву. То спречава нападе крађом refresh token: нападач добија приступ, али сесија се блокира одмах по откривању.
Према OAuth Security Workshop, 2025, при увођењу rotation + reuse detection вероватноћа успешног напада преко украденог refresh token пада са 23% на 0.3%. За имплементацију reuse detection сервер чува хеш последњег издатог refresh token у пару са client_id. При захтеву за освежавање сервер пореди презентовани refresh token са сачуваним — ако се не поклапају, значи да је дошло до поновне употребе и цео ланац токена се поништава.
При добијању грешке invalid_grant клијент треба да изврши потпуни одјаву: обрише све сачуване токене (access и refresh), заврши тренутну сесију на уређају и преусмери корисника на екран за пријаву. Поновна аутентификација ствара нови ланац токена, који није повезан са претходним. Игнорисање ове грешке и поновљени покушаји освежавања довешче до блокирања путем reuse detection.
Примјер имплементације клијентског дијела освежавања токена у Kotlin-у за Android. Апликација пресрета HTTP одговор 401, позива захтев за освежавање и понавља оригинални захтев са новим access token. Користи се OkHttp Interceptor — кључна компонента за аутоматско управљање токенима без дуплирања логике у сваком захтеву.
class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val accessToken = getAccessToken()
val authRequest = request.newBuilder()
.addHeader("Authorization", "Bearer $accessToken")
.build()
val response = chain.proceed(authRequest)
if (response.code != 401) return response
// Access token је истекао — освежавамо кроз refresh token
val newToken = refreshAccessToken() ?: return response
return chain.proceed(request.newBuilder()
.addHeader("Authorization", "Bearer $newToken")
.build())
}
private fun refreshAccessToken(): String? {
val refreshToken = getRefreshToken() ?: return null
val client = OkHttpClient()
val body = FormBody.Builder()
.add("grant_type", "refresh_token")
.add("refresh_token", refreshToken)
.build()
val request = Request.Builder()
.url("https://auth.example.com/oauth/token")
.post(body)
.build()
val response = client.newCall(request).execute()
val json = JSONObject(response.body?.string() ?: return null)
val newAccessToken = json.getString("access_token")
// Сачувај нови refresh token при rotation
saveTokens(newAccessToken, json.optString("refresh_token"))
return newAccessToken
}
}
Често постављана питања
Access token — кратковечни токен за приступ API, преноси се са сваким захтевом. Refresh token — дуговечни токен за добијање новог access token, преноси се само на token endpoint. Refresh token не треба да буде доступан обичним API крајњим тачкама апликације.
При сваком истеку рока — обично сваких 15–60 минута. Клијент треба да прати време истека (провера exp у JWT или тајмер) и покрене захтев за освежавање унапред, пре него што заиста добије 401. То спречава губитак података при захтевима послатим у тренутку истека токена.
Да, refresh token се може и треба опзвати. Сервер чува листу активних refresh token (или њихових хешева) у бази података. При одјави, промени лозинке или сумњивијој активности сервер брише унос из базе података, и следећи захтев за освежавање са овим токеном враћа грешку invalid_grant.
Код имплементираног rotation са reuse detection: први захтев успешно освежава токене, други добија грешку invalid_grant. Сервер такође бележи поновну употребу — сесија се блокира, оба клијента губе приступ. Корисник мора поново да се пријави. Тиме се жртвује удобност ради безбедности.
Refresh token треба чувати у Keychain са атрибутом kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. То гарантује шифровање токена, недоступност када је лозинка уклоњена и искључује синхронизацију путем iCloud. Коришћење UserDefaults или CoreData за чување токена је категорички забрањено.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође