JWT & react & node

KU
  • Rejestracja:około 3 lata
  • Ostatnio:około 3 lata
  • Postów:7
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 ;)

Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
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.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
edytowany 1x, ostatnio: Aventus
KU
  • Rejestracja:około 3 lata
  • Ostatnio:około 3 lata
  • Postów:7
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?

Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
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.


edytowany 2x, ostatnio: Patryk27
KU
  • Rejestracja:około 3 lata
  • Ostatnio:około 3 lata
  • Postów:7
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?

Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
0

Tak, dokładnie :-)


KU
  • Rejestracja:około 3 lata
  • Ostatnio:około 3 lata
  • Postów:7
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ć

FR
  • Rejestracja:około 11 lat
  • Ostatnio:około 20 godzin
  • Postów:928
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

edytowany 1x, ostatnio: froziu
KU
  • Rejestracja:około 3 lata
  • Ostatnio:około 3 lata
  • Postów:7
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

edytowany 1x, ostatnio: kubor
Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
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.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
Patryk27
Moderator
  • Rejestracja:ponad 17 lat
  • Ostatnio:ponad rok
  • Lokalizacja:Wrocław
  • Postów:13042
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?


Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
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.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
edytowany 1x, ostatnio: Aventus
KU
  • Rejestracja:około 3 lata
  • Ostatnio:około 3 lata
  • Postów:7
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ą

Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
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 ;)


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)