Witam, mówiąc o temacie związanym z refresh tokenami, spotkałem się nieraz z taką opinią jak ta:
A refresh token HAS to be stored on the server side. You shouldn't leverage the "self-contained" property of JWT for a refresh token. Doing so leaves you with no way to revoke refresh tokens other than changing your private key
https://stackoverflow.com/questions/27726066/jwt-refresh-token-flow
Cała sprawa tyczy się tego, że refresh tokeny żyją dużej niż żetony dostępowe. Musimy w jakiś sposób je przechować na "czarnej lub białej liście". W przeciwnym razie mogą one być niebezpiecznie długo aktywne, a my nie będziemy mogli nic z tym zrobić.
Problem jest tylko taki, że tworząc takowe listy, niewiele zyskamy na "bezstanowym" systemie autoryzacji. Moim zdaniem, sesja była by po prostu prostsza i mniej zawiła.
Wydaje mi się, że wpadłem (pewnie nie ja pierwszy) na potencjalny pomysł, jak szybko zdezaktywować wszystkie tokeny, przechowując w modelu użytkownika jedynie jedno dodatkowe pole. Nazwałem je state, jest to nic innego jak prosty losowy żeton:
const generateUserState = () => crypto.randomBytes(32).toString('hex');
Aktualna wartość pola state wraz z ID użytkownika (i dodatkowymi informacjami jak exp i iat) składają się na payload w JWT. Przy próbie autoryzacji i odświeżania tokenu, sprawdzam zgodność pola state w JWT oraz bazie.
Teraz, by wszystkie klucze dostępowe uczynić nieaktualnymi, wystarczy zmienić w bazie wartość tego jednego pola.
Jeżeli użytkownik tego dokona, to wszystkie tokeny atakującego przestaną być aktywne. "Złodziej" sam nie będzie mógł tego zrobić, gdyż request do zmiany pola state, będzie wymagał podania hasła.
To oznacza, że posiadacz skradzionego refresh tokenu, będzie mógł oczywiście dokonywać zmiany na koncie. Nie uda mu się jednak w zbyt prosty sposób przejąć nad nim całkowitej kontroli.
Jeżeli do tego wszystkiego dodamy wszystkie inne reguły zabezpieczeń, to wydaje mi się, że dla prostych aplikacji to by wystarczyło. Podoba mi się, że nie wiele rezygnujemy z bezstanowości procesu - potrzebne jest tylko jedne zwyczajne pole tekstowe w bazie (do tego nie często zmieniane).
Oczywiście nie ma tu możliwości likwidacji poprawności tylko wybranych tokenów. To jest największa wada.
Ciekawy jestem zdania innych na temat tego rozwiązania problemu. Czy było by to wystarczająco bezpieczne do użycia w praktyce? Z góry dziękuje za wskazówki :)