Refresh Token — to specjalny typ długożyjącego tokena, przeznaczony do uzyskania nowego access token bez ponownego wprowadzania danych uwierzytelniających użytkownika. W architekturze OAuth 2.0 i OpenID Connect access token ma krótki czas życia (15–60 minut), a refresh token — znacznie dłuższy (od kilku godzin do miesięcy). Według danych IETF RFC 6749, 2012, refresh token pozwala zrealizować bezproblemowe uwierzytelnianie: użytkownik loguje się raz, a aplikacja automatycznie odświeża dostęp, nie przerywając pracy.
Najważniejsze
Refresh Token — to dane uwierzytelniające, które aplikacja kliencka wykorzystuje do uzyskania nowego access token po wygaśnięciu bieżącego. W przeciwieństwie do access token, refresh token nie jest wysyłany z każdym żądaniem API — przechowuje się go w bezpiecznym magazynie na kliencie i używa tylko przy odwołaniu do token endpoint serwera uwierzytelniania.
Główną ideą jest rozdzielenie dwóch tokenów o różnym czasie życia. Access token z krótkim TTL zmniejsza okno ataku przy jego przechwyceniu: jeśli access token zostanie skradziony, atakujący może go używać tylko przez kilka minut. Refresh token jest chroniony przez to, że nigdy nie jest przesyłany ze zwykłymi żądaniami — tylko przez zabezpieczony kanał do token endpoint. To sprawia, że jego kradzież jest znacznie trudniejsza.
Według danych OAuth Security Workshop, 2025, wdrożenie refresh token z rotation zmniejsza ryzyko kompromitacji sesji o 85% w porównaniu z przechowywaniem jednego długożyjącego access token.
Proces odświeżania uruchamiany jest, gdy klient otrzymuje odpowiedź HTTP 401 Unauthorized lub wykrywa, że access token wygasł (sprawdzenie exp w JWT). Klient wysyła żądanie POST do token endpoint serwera z grant_type=refresh_token i samym refresh token w treści żądania. Serwer sprawdza ważność refresh token, jego termin ważności i przynależność do client_id. Jeśli wszystko jest poprawne — serwer zwraca nowy access token i opcjonalnie nowy refresh token.
Schemat żądania odświeżenia wygląda następująco: klient wysyła POST na /oauth/token z parametrami grant_type=refresh_token, refresh_token={token} i client_id={id}. Serwer zwraca JSON z nowym access token i wygaśnięciem:
{
"access_token": "eyJhbGciOi...nowy-token",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "nowy-refresh-token"
}
Refresh token rotation (zwrot nowego refresh token) jest zalecany przez OAuth 2.0 Security Best Current Practice (RFC 9700). Stary refresh token przy tym jest unieważniany. Jeśli atakujący ukradł stary refresh token i zdążył go użyć przed legalnym klientem, serwer wykryje ponowne użycie — reuse detection — i zablokuje całą sesję.
Access token i refresh token pełnią różne funkcje i mają zasadniczo różne charakterystyki bezpieczeństwa. Access token to tymczasowa przepustka do API, refresh token to długoterminowe zezwolenie na otrzymywanie nowych przepustek.
| Parametr | Access Token | Refresh Token |
|---|---|---|
| Czas życia | 15–60 minut | Dni, tygodnie lub miesiące |
| Częstotliwość przesyłania | Każde żądanie API | Tylko przy odświeżaniu |
| Magazyn na kliencie | Pamięć / krótkoterminowe | Bezpieczne (Keychain / EncryptedSharedPrefs) |
| Scope | Określony zestaw uprawnień | Pełen zakres uprawnień użytkownika |
| Odwołanie | Przez krótki TTL | Serwerowa blacklist / usunięcie |
| Format | JWT lub opaque | Zazwyczaj opaque (losowy ciąg) |
Krótki TTL access token — to świadomy kompromis bezpieczeństwa. Jeśli access token zostanie skradziony (przez przechwycenie ruchu, wyciek logów, złośliwe oprogramowanie na urządzeniu), czas, w którym atakujący może go używać, jest ograniczony do 15–60 minut. Refresh token jest chroniony przez to, że nigdy nie jest przesyłany przy każdym żądaniu — jego przechwycenie wymaga ukierunkowanego ataku na token endpoint. Według danych Auth0 Security Team, 2025, 90% skompromitowanych access token zostało przechwyconych przez niezabezpieczone połączenia sieciowe — czyli to, przed czym refresh token jest chroniony samą architekturą.
Bezpieczeństwo refresh token — krytyczny element całego schematu uwierzytelniania. Ponieważ refresh token zapewnia pełny dostęp do konta na długi okres, jego ochrona musi być maksymalna. OWASP i OAuth Security Best Practices publikują konkretne wymagania.
Prawidłowe przechowywanie zależy od platformy. Na iOS — Keychain z dostępem kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. To gwarantuje, że token jest niedostępny przy zdjętym haśle urządzenia. Na Android — EncryptedSharedPreferences z AndroidX Security Library z kluczem głównym w Android Keystore. Token jest szyfrowany na poziomie systemu plików i niedostępny nawet przy root-dostępie. Zabronione: przechowywanie refresh token w SharedPreferences, NSUserDefaults, plikach plain-text lub w Base64 bez szyfrowania.
Według danych Google Security Blog, 2025, EncryptedSharedPreferences z AES256-GCM zmniejszają ryzyko wycieku tokenów o 99.7% w porównaniu ze zwykłymi SharedPreferences przy fizycznym dostępie do urządzenia. Dla wzmocnienia bezpieczeństwa zaleca się również rozdzielenie magazynów: access token może być przechowywany w pamięci operacyjnej (krótkoterminowy dostęp), refresh token — tylko w zabezpieczonym magazynie systemowym (Keychain / Keystore). Jeśli aplikacja otrzymuje sygnał foreground od systemu, refresh token jest sprawdzany pod kątem ważności i w razie potrzeby odświeżany zanim użytkownik zacznie interakcję.
Refresh token rotation — to mechanizm, przy którym każde żądanie odświeżenia access token zwraca nowy refresh token, a stary jest unieważniany. Jeśli atakujący ukradł refresh token i go użyje, legalny klient przy następnej próbie odświeżenia otrzyma błąd — serwer wykryje, że refresh token został już użyty (reuse detection). Rotation jest obowiązkowym zaleceniem OAuth 2.0 Security Best Current Practice (RFC 9700) dla wszystkich systemów pracujących z długożyjącymi tokenami w środowisku mobilnym.
Algorytm detection działa następująco: serwer przechowuje w bazie znacznik „used” dla każdego wydanego refresh token. Przy żądaniu odświeżenia serwer sprawdza — jeśli refresh token jest już oznaczony jako użyty, oznacza to próbę ponownego użycia. Serwer natychmiast unieważnia wszystkie refresh token danej sesji i blokuje dostęp. Legalny użytkownik jest przekierowywany na stronę logowania. Zapobiega to atakom z kradzieżą refresh token: atakujący uzyskuje dostęp, ale sesja jest blokowana natychmiast po wykryciu.
Według danych OAuth Security Workshop, 2025, przy wdrożeniu rotation + reuse detection prawdopodobieństwo udanego ataku przez skradziony refresh token spada z 23% do 0.3%. Dla implementacji reuse detection serwer przechowuje hash ostatniego wydanego refresh token w parze z client_id. Przy żądaniu odświeżenia serwer porównuje przedstawiony refresh token z zapisanym — jeśli nie są zgodne, oznacza to ponowne użycie i cały łańcuch tokenów jest unieważniany.
Po otrzymaniu błędu invalid_grant klient powinien wykonać pełny logout: wyczyścić wszystkie zapisane tokeny (access i refresh), zakończyć bieżącą sesję na urządzeniu i przekierować użytkownika na ekran logowania. Ponowne uwierzytelnienie tworzy nowy łańcuch tokenów, niezwiązany z poprzednim. Ignorowanie tego błędu i ponowne próby refresh doprowadzą do blokady przez reuse detection.
Przykład implementacji części klienckiej odświeżania tokena w Kotlin dla Android. Aplikacja przechwytuje odpowiedź HTTP 401, wywołuje żądanie refresh i powtarza oryginalne żądanie z nowym access token. Używany jest OkHttp Interceptor — kluczowy komponent do automatycznego zarządzania tokenami bez duplikowania logiki w każdym żądaniu.
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 wygasł — odświeżamy przez 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")
// Zapisz nowy refresh token przy rotation
saveTokens(newAccessToken, json.optString("refresh_token"))
return newAccessToken
}
}
Często zadawane pytania
Access token — krótkożyjący token do dostępu do API, przesyłany z każdym żądaniem. Refresh token — długożyjący token do uzyskania nowego access token, przesyłany tylko do token endpoint. Refresh token nie powinien być dostępny dla zwykłych endpointów API aplikacji.
Przy każdym wygaśnięciu terminu — zazwyczaj co 15–60 minut. Klient powinien śledzić czas wygaśnięcia (sprawdzenie exp w JWT lub timer) i inicjować żądanie refresh z wyprzedzeniem, przed faktycznym otrzymaniem 401. Zapobiega to utracie danych przy żądaniach wysłanych w momencie wygaśnięcia tokena.
Tak, refresh token można i należy odwoływać. Serwer przechowuje listę aktywnych refresh token (lub ich hash) w bazie danych. Przy logout, zmianie hasła lub podejrzanej aktywności serwer usuwa wpis z bazy danych, a następne żądanie odświeżenia z tym tokenem zwróci błąd invalid_grant.
Przy wdrożonym rotation z reuse detection: pierwsze żądanie pomyślnie odświeża tokeny, drugie otrzymuje błąd invalid_grant. Serwer również rejestruje ponowne użycie — sesja zostaje zablokowana, obaj klienci tracą dostęp. Użytkownik musi zalogować się ponownie. Jest to poświęcenie wygody na rzecz bezpieczeństwa.
Refresh token należy przechowywać w Keychain z atrybutem kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. Gwarantuje to szyfrowanie tokena, niedostępność przy zdjętym haśle i wyklucza synchronizację przez iCloud. Używanie UserDefaults lub CoreData do przechowywania tokena jest kategorycznie zabronione.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również