JWT (JSON Web Token) automatyczne przedłużenie wygaśnięcia
Chciałbym zaimplementować uwierzytelnianie oparte na JWT do naszego nowego REST API. Ale ponieważ wygaśnięcie jest ustawione w tokenie, czy można go automatycznie przedłużyć? Nie chcę, aby użytkownicy musieli logować się po każdym x minutach, jeśli aktywnie korzystali z aplikacji w tym okresie. To byłaby wielka porażka UX.
Ale przedłużenie ważności tworzy nowy token (a stary jest nadal ważny, dopóki nie wygaśnie). I generowanie nowego tokena po każdym żądaniu brzmi głupio, aby ja. Brzmi jak problem z zabezpieczeniami, gdy więcej niż jeden token jest ważny w tym samym czasie. Oczywiście mógłbym unieważnić Stary używany za pomocą czarnej listy, ale musiałbym przechowywać tokeny. Jedną z zalet JWT jest brak pamięci masowej.
Znalazłem sposób, w jaki Auth0 to rozwiązał. Używają nie tylko tokena JWT, ale także tokena odświeżającego: https://docs.auth0.com/refresh-token
Ale znowu, aby to zaimplementować (bez Auth0) musiałbym przechowywać tokeny odświeżania i utrzymywać ich ważność. Co? czy jest to zatem prawdziwa korzyść? Dlaczego nie mieć tylko jednego tokena (nie JWT) i zachować ważność na serwerze?
Są inne opcje? Czy użycie JWT nie nadaje się do tego scenariusza?
9 answers
Pracuję w Auth0 i brałem udział w projektowaniu funkcji odświeżania tokena.
Wszystko zależy od rodzaju aplikacji i oto nasze zalecane podejście.
Aplikacje internetowe
Dobrym wzorcem jest odświeżenie tokena przed jego wygaśnięciem.
Ustaw datę wygaśnięcia tokena na jeden tydzień i odśwież token za każdym razem, gdy użytkownik otwiera aplikację internetową i co godzinę. Jeśli użytkownik nie otworzy aplikacji dłużej niż tydzień, będzie musiał się zalogować ponownie i jest to dopuszczalne UX aplikacji webowej.
Aby odświeżyć token, API potrzebuje nowego punktu końcowego, który otrzyma poprawny, nie wygasły JWT i zwróci ten sam podpisany JWT z nowym polem wygaśnięcia. Następnie aplikacja internetowa zapisze gdzieś token.
Aplikacje mobilne/natywne
Większość aplikacji natywnych loguje się tylko raz.
Chodzi o to, że token odświeżania nigdy nie wygasa i można go zawsze wymienić na poprawny JWT.
The problem z tokenem, który nigdy nie wygasa, polega na tym, że nigdy oznacza nigdy. Co zrobić, jeśli zgubisz telefon? Musi więc być w jakiś sposób identyfikowalny przez użytkownika, a aplikacja musi zapewnić sposób odwołania dostępu. Postanowiliśmy użyć nazwy urządzenia, np. "maryo' s iPad". Następnie użytkownik może przejść do aplikacji i odwołać dostęp do "iPad maryo".
Innym podejściem jest cofnięcie tokena odświeżania w określonych zdarzeniach. Ciekawym wydarzeniem jest zmiana hasło.
Uważamy, że JWT nie jest użyteczny w tych przypadkach użycia, więc używamy losowego wygenerowanego ciągu i przechowujemy go po naszej stronie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-04 16:22:55
W przypadku, gdy sam obsługujesz auth (tzn. nie używasz dostawcy takiego jak Auth0), mogą działać następujące rzeczy:
- Wydaj token JWT ze stosunkowo krótkim terminem wygaśnięcia, powiedzmy 15min.
- Aplikacja sprawdza datę wygaśnięcia tokenu przed jakąkolwiek transakcją wymagającą tokenu (token zawiera datę wygaśnięcia). Jeśli token wygasł, to najpierw prosi API o "odświeżenie" tokena(odbywa się to przejrzyście w UX).
- API pobiera żądanie odświeżenia tokena, ale najpierw sprawdza bazę danych użytkownika, aby zobaczyć jeśli na tym profilu użytkownika ustawiono flagę "reauth" (token może zawierać identyfikator użytkownika). Jeśli znacznik jest obecny, to odmowa odświeżenia tokena, w przeciwnym razie zostanie wydany nowy token.
- Powtórz.
Flaga 'reauth' w zapleczu bazy danych zostanie ustawiona, gdy na przykład użytkownik zresetuje swoje hasło. Flaga zostanie usunięta, gdy użytkownik zaloguje się następnym razem.
Ponadto Załóżmy, że masz politykę, zgodnie z którą użytkownik musi zalogować się co najmniej raz na 72 godziny. W takim razie, logika odświeżania tokenów API sprawdza również datę ostatniego logowania użytkownika z bazy danych użytkownika i odmawia / zezwala na odświeżanie tokenów na tej podstawie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-19 00:12:15
Majstrowałem przy przenoszeniu naszych aplikacji do HTML5 z RESTful API w backendzie. Rozwiązanie, które wymyśliłem było:
- Klient otrzymuje token z czasem sesji 30 minut (lub innym zwykłym czasem sesji po stronie serwera) po pomyślnym zalogowaniu.
- zegar po stronie klienta jest tworzony w celu wywołania usługi w celu odnowienia tokenu przed jego upływem. Nowy token zastąpi istniejące w przyszłości połączenia.
Jak widać, zmniejsza to częste odświeżanie tokenów żądań. Jeśli użytkownik zamknie przeglądarkę/aplikację przed wywołaniem Odnów token, poprzedni token wygaśnie w czasie i użytkownik będzie musiał ponownie zalogować się.
Można wdrożyć bardziej skomplikowaną strategię, aby zaspokoić nieaktywność użytkownika (np. zaniedbanie otwartej karty przeglądarki). W takim przypadku wywołanie Odnów token powinno zawierać oczekiwany czas wygaśnięcia, który nie powinien przekraczać zdefiniowanego czasu sesji. Aplikacja będzie musiała śledzić ostatniego użytkownika interakcji.
Nie podoba mi się pomysł ustawiania długiego wygaśnięcia, dlatego takie podejście może nie działać dobrze z natywnymi aplikacjami wymagającymi mniej częstego uwierzytelniania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-21 03:00:16
Alternatywnym rozwiązaniem dla unieważnienia JWTs, bez dodatkowego bezpiecznego przechowywania w backendzie, jest zaimplementowanie nowej kolumny jwt_version
integer w tabeli users. Jeśli użytkownik chce wylogować się lub wygasnąć istniejące tokeny, po prostu zwiększa pole jwt_version
.
Podczas generowania nowego JWT, Zakoduj jwt_version
do ładunku JWT, opcjonalnie zwiększając wartość wcześniej, jeśli nowy JWT powinien zastąpić wszystkie inne.
Podczas walidacji JWT, pole jwt_version
jest porównywane wraz z user_id
i autoryzacja jest udzielana tylko wtedy, gdy pasuje.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-31 09:19:31
Dobre pytanie - a w samym pytaniu jest mnóstwo informacji.
Artykuł odśwież tokeny: kiedy ich używać i jak oddziałują z JWTs daje dobry pomysł na ten scenariusz. Niektóre punkty to: -
- tokeny odświeżające zawierają informacje niezbędne do uzyskania nowego dostępu token.
- odświeżanie tokenów może również wygasnąć, ale jest raczej długotrwałe.
- tokeny odświeżania podlegają zazwyczaj ścisłym wymogom przechowywania upewnij się, że nie są wyciekł.
- mogą być również umieszczone na czarnej liście przez serwer autoryzacji.
Zobacz też auth0 / angular-jwt angularjs
Dla Web API. read Enable OAuth Refresh Tokens in AngularJS app using ASP. net Web API 2, and Owin
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-26 17:50:23
Zaimplementowałem to w PHP przy użyciu klienta Guzzle, aby stworzyć bibliotekę klienta dla api, ale koncepcja powinna działać na innych platformach.
Zasadniczo wydaję dwa żetony, krótki (5 minutowy) i długi, który wygasa po tygodniu. Biblioteka klienta używa oprogramowania pośredniczącego, aby spróbować odświeżyć krótki token, Jeśli otrzyma odpowiedź 401 na pewne żądanie. Następnie spróbuje ponownie oryginalnego żądania i jeśli udało mu się odświeżyć, otrzyma poprawną odpowiedź, w sposób przejrzysty dla użytkownika. Jeśli się nie powiedzie, po prostu wyśle 401 do użytkownika.
Jeśli krótki token wygasł, ale nadal jest autentyczny, a długi Token jest ważny i autentyczny, odświeży krótki token za pomocą specjalnego punktu końcowego w usłudze, do której uwierzytelnia się długi token (jest to jedyna rzecz, do której można go użyć). Następnie użyje krótkiego tokenu, aby uzyskać nowy długi token, przedłużając go o kolejny tydzień za każdym razem, gdy odświeży krótki token.
This approach pozwala nam również odwołać dostęp w ciągu maksymalnie 5 minut, co jest dopuszczalne dla naszego użytku bez konieczności przechowywania czarnej listy tokenów.
Late edit: ponowne czytanie w tym miesiącu po tym, jak był świeży w mojej głowie, powinienem zwrócić uwagę, że możesz odwołać dostęp podczas odświeżania krótkiego tokena, ponieważ daje to możliwość droższych połączeń (np. połączenia do bazy danych, aby sprawdzić, czy użytkownik został zbanowany) bez płacenia za to przy każdym połączeniu do twojego serwisu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-22 14:54:58
Jwt-autorefresh
Jeśli używasz node (React / Redux / Universal JS) możesz zainstalować npm i -S jwt-autorefresh
.
Ta biblioteka planuje odświeżanie tokenów JWT na liczoną przez użytkownika liczbę sekund przed wygaśnięciem tokena dostępu (w oparciu o roszczenie exp zakodowane w tokenie). Posiada obszerny zestaw testów i sprawdza kilka warunków, aby upewnić się, że każdej dziwnej aktywności towarzyszy opisowy komunikat dotyczący błędnych konfiguracji z twojego środowisko.
Pełna przykładowa implementacja
import autorefresh from 'jwt-autorefresh'
/** Events in your app that are triggered when your user becomes authorized or deauthorized. */
import { onAuthorize, onDeauthorize } from './events'
/** Your refresh token mechanism, returning a promise that resolves to the new access tokenFunction (library does not care about your method of persisting tokens) */
const refresh = () => {
const init = { method: 'POST'
, headers: { 'Content-Type': `application/x-www-form-urlencoded` }
, body: `refresh_token=${localStorage.refresh_token}&grant_type=refresh_token`
}
return fetch('/oauth/token', init)
.then(res => res.json())
.then(({ token_type, access_token, expires_in, refresh_token }) => {
localStorage.access_token = access_token
localStorage.refresh_token = refresh_token
return access_token
})
}
/** You supply a leadSeconds number or function that generates a number of seconds that the refresh should occur prior to the access token expiring */
const leadSeconds = () => {
/** Generate random additional seconds (up to 30 in this case) to append to the lead time to ensure multiple clients dont schedule simultaneous refresh */
const jitter = Math.floor(Math.random() * 30)
/** Schedule autorefresh to occur 60 to 90 seconds prior to token expiration */
return 60 + jitter
}
let start = autorefresh({ refresh, leadSeconds })
let cancel = () => {}
onAuthorize(access_token => {
cancel()
cancel = start(access_token)
})
onDeauthorize(() => cancel())
Uwaga: jestem opiekunem
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-05 13:42:09
Co powiesz na takie podejście:
- W przypadku każdego żądania klienta serwer porównuje czas wygaśnięcia tokena z (currentTime - lastAccessTime)
- If expirationTime , zmienia ostatni lastAccessedTime na currentTime.
- w przypadku braku aktywności w przeglądarce przez czas przekraczający expirationTime lub w przypadku, gdy okno przeglądarki zostało zamknięte i expirationtime > (currentTime - lastAccessedTime), oraz następnie serwer może wygasnąć token i poprosić Użytkownika o ponowne zalogowanie.
Nie wymagamy dodatkowego punktu końcowego do odświeżania tokena w tym przypadku. Byłabym wdzięczna za każdy feedack.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-10 21:31:08
Rozwiązałem ten problem dodając zmienną w danych tokena:
softexp - I set this to 5 mins (300 seconds)
Ustawiam opcję expiresIn
na żądany czas, zanim użytkownik będzie zmuszony ponownie się zalogować. Mój jest ustawiony na 30 minut. Wartość ta musi być większa niż wartość softexp
.
Gdy aplikacja po stronie klienta wysyła żądanie do API serwera (gdzie wymagany jest token, np. strona listy klientów), serwer sprawdza, czy przesłany token jest nadal ważny, czy nie na podstawie jego pierwotnej wartości expiration (expiresIn
). Jeśli nie valid, serwer odpowie statusem określonym dla tego błędu, np. INVALID_TOKEN
.
Jeśli token jest nadal ważny na podstawie wartości expiredIn
, ale już przekroczył wartość softexp
, Serwer odpowie oddzielnym statusem dla tego błędu, np. EXPIRED_TOKEN
:
(Math.floor(Date.now() / 1000) > decoded.softexp)
Po stronie klienta, jeśli otrzymał odpowiedź EXPIRED_TOKEN
, powinien automatycznie odnowić token, wysyłając żądanie odnowienia do serwera. Jest to przejrzyste dla użytkownika i automatycznie pod opieką klienta app.
Metoda odnowienia na serwerze musi sprawdzić, czy token jest nadal ważny:
jwt.verify(token, secret, (err, decoded) => {})
Serwer odmówi odnowienia tokenów, jeśli nie powiodła się powyższa metoda.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-04 21:29:46