JWT & react & node

0

Witam,

w zasadzie nie mam pytania konkretnego, w sensie jak coś zrobić, natomiast mam pytanie ogólne,

po tym jak przejrzałem mase tutoriali i innych artykułów, najczęściej podejście do JWT w świecie React/Nextjs/Node/etc prezentuje się następująco

  • wysyłane jest zapytanie np /login z danymi z formularza
  • jesli jest ok, to server tworzy refresh tokena w cookie ważnego np 7 dni i zwraca json'a z access tokenem, ważnego załóżmy 5 minut, generalnie dużo krócej niż refresh token
  • dla uproszczenia powiedzmy, że access token ze zapisywany w localStorage
  • po zalogowaniu access token będzie wrzucany w kolejne requesty jako header w wiadomym celu
  • teraz, mija te 5 minut i access token przestaje być ważny, potrzebny jest "silent refresh" który bazuje na refresh tokenie w cookie
  • więc załóżmy po 5 minutach leci kolejny request do np /getSecretData, zanim dotrze do serwera, po stronie klienta trzeba należy sprawdzić, czy JWT nie jest expired, jeśli jest - to najpierw request do /refresh_token, który zwróci nowy access token i zrobi nowy refresh token w cookie i dopiero wtedy wysyłam request /getSecretData.

No i dobrze, teraz pytanie, jak po stronie klienta sprawdzić czy JWT nie jest expired? należy porównać timestamp JWT.payload.exp z obecną datą i tu moim zdaniem jest poważny problem, bo to porównanie jest z datą po stronie klienta. Klient weźmie sobie po prostu date z systemu operacyjnego, która może być np 2 godziny inna względem serwera. Nie mówię tu o strefach czasowych, po prostu można być w UTC i przestawić sobie zegarek, a Date.now() i tak zwróci faktyczną godzinę z systemu operacyjnego, nadal "myśląc", że jest w UTC.

Naturalnie rodzi to poważne problemy, jeśli token tak naprawdę nie będzie już ważny, a client po sprawdzeniu tokena powie, że jego data nie jest "większa" od daty JWT.payload.exp (bo jest np 2 godziny do tyłu), to po prostu puści request do serwera, a serwer po weryfikacji tokena zwróci przysłowiowe false. Co będzie oznaczało, że "silent refresh" nie przejdzie i jest problem.

dla ułatwienia zupełnie pomijam sprawdzanie "secret" dla JWT, tak samo pomijam czy access tokena trzymac w localStorage, czy nie, czy stateless, albo refresh tokena w bazie

Pozdrawiam i liczę jakieś komentarze ;)

0

Nie mówię tu o strefach czasowych, po prostu można być w UTC i przestawić sobie zegarek, a Date.now() i tak zwróci faktyczną godzinę z systemu operacyjnego, nadal "myśląc", że jest w UTC.

Nie rozumiem co masz przez to na myśli. Serwer powinien ustawić expiry tokena w UTC, i później po stronie klienta robisz to samo- bierzesz obecną date i konwertujesz ją na UTC. Wtedy porownujesz obie daty w UTC. Rzecz w tym aby po stronie klienta dokonać tej konwersji na UTC a nie używać tego co domyślnie zwraca Ci Date.now().

Jeśli to problem to są od tego biblioteki, np. luxon.

0

no dobrze, w Polsce mamy strefe CET na moment pisania posta jest 12:45
jak przekonwertuje date na UTC to dostanę 11:45

jeśli zmienie sobie godzine ręcznie w systemie na 10:00
to czy po konwersji na UTC nie dostanę 9:00?

2

Niezależnie od czasu użytkownika, możesz po prostu zawsze wysyłać obecny token i resetować go dopiero wtedy gdy serwer Ci odpowie ejjj ale ten token nie jest już ważny - wtedy w ogóle nie musisz martwić się czasem na czyimś komputerze.

0

@Patryk27: no w takiej sytuacji to by wyglądało chyba następująco, server odpowiada "ejj ten token nie jest ważny" i wtedy z automatu by trzeba wysłac /refresh_token i potem jeszcze raz ten pierwszy request który sprawił, że serwer odpowiedział "ejj ten token nie jest ważny", dobrze rozumiem?

0

Tak, dokładnie :-)

0

@Patryk27: no to by było idealnie rozwiązanie, natomiast musze przyznać, że zaimplementowanie tego moze mi sprawdzić problem : D
łatwo by bylo wyslać /refresh_token, natomiast wysłanie ponowne tego pierwszego requesta to już nie wiem, musze poczytać

0

Wysyłasz login
Dostajesz request token i accessToken (obsługę requestTokenu na backendzie trzeba osobno napisać i walidować czy jest on prawidłowy, moze niektóre frameworki to mają, może to być nawet jakiś zaszyfrowany ciąg znaków)
Wysyłąsz requesty z accessTokenem nieustannie jakis axios czy inny framework
Dodajesz interceptor do axiosa, ktory łapie 403 (wygaśnięty accessToken)
Jeśli przyjdzie 403 to w interceptorze wysyłasz jeden raz request z refreshTokenem (ktory tez ma lifetime i tez moze byc wygasniety), nastepnie lapiesz z niego nowy accessToken, zapisujesz i wysyłasz powyzszy nieudany request z nowym accessTokenem, w przeciwnym wypadku wywalasz ciastka/localstorage/gdzie tam trzymasz stan tokenow i najprawdopodobniej wylogowujesz usera
zapetlij

0

ok problem rozwiązany, wszystko po stronie serwera, tak jak pisał @Patryk27
nie wiem czemu sie uczepiłem tego sprawdzania daty na kliencie, jako że używam apollo clienta, to dodałem sprawdzanie na onError w ApolloLink i w zasadzie cała filozofia, dzieki za posty i pozdrawiam

0

A ja zagram adwokata diabła i powiem że sprawdzać po stronie klienta też można. Jeśli ktoś ma ustawioną złą godzinę to problem tkwi po stronie tego urządzenia. Autoryzacja po stronie serwera i tak odrzuci requesty z tokenem który wygasł.

Mały test- cofnij sobie datę w komputerze o rok, albo daj rok do przodu i zobacz jak zachowują się różne strony.

2

Też można, ale skoro i tak musisz/powinieneś mieć zaimplementowane reagowanie na wiadomość serwera oj stary token, to co daje dodatkowa walidacja po stronie frontendu?

1
Patryk27 napisał(a):

Też można, ale skoro i tak musisz/powinieneś mieć zaimplementowane reagowanie na wiadomość serwera oj stary token, to co daje dodatkowa walidacja po stronie frontendu?

Chociażby to że po stronie klienta nie musimy reagować inaczej na odpowiedź z serwera że token wygasł. Zazwyczaj serwer rzuca 401 i nie interesuje go z jakiego powodu token jest odrzucony. Jeśli nie mamy walidacji po stronie klienta, to;

  • Albo klient musi próbować odświeżyć token, zakładając że 401 w tym przypadku oznaczał że token wygasł. A wcale tak nie musiało być. Token mógł mieć niepoprawną sygnaturę, lub z jakiegoś innego powodu po stronie serwera nie został zaakceptowany.
  • Albo serwer i klient muszą uzgodnić że serwer w jakiś jawny sposób zasygnalizuje konieczność odświeżenia tokena, i klient będzie musiał odpowiednio to obsłużyć.

W przypadku walidacji po stronie klienta, klient wie czy token należy odświeżyć i nie musi czekać na kod błędu z sewera. A jeśli serwer zwraca 401 to klient po prostu nakazuje się ponownie zalogować użytkownikowi, zamiast zakładać co jest powodem odrzucenia requestu.

0

teoretycznie można zwracać coś więcej niż tylko 401 i reagować na to po stronie klienta,
no nic, zdania są podzielone najwyraźniej i słusznie zresztą

3

@kubor

teoretycznie można zwracać coś więcej niż tylko 401 i reagować na to po stronie klienta,

Tak, to jest dokładnie ten scenariusz który opisałem w tym punkcie:

Albo serwer i klient muszą uzgodnić że serwer w jakiś jawny sposób zasygnalizuje konieczność odświeżenia tokena, i klient będzie musiał odpowiednio to obsłużyć.

I żeby nie było. Ja nie twierdzę że moja propozycja to jedyne słuszne podejście. To po prostu alternatywne rozwiązanie ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1