Obsługa odnowienia tokena / wygaśnięcia sesji w interfejsie API RESTful

17

Buduję interfejs API RESTful, który korzysta z tokenów JWT do uwierzytelniania użytkowników (wydawanych przez loginpunkt końcowy i wysyłanych we wszystkich nagłówkach później), a tokeny należy odświeżyć po ustalonym czasie (wywoływanie renewpunktu końcowego, który zwraca odnowiony token ).

Możliwe jest, że sesja API użytkownika utraci ważność przed wygaśnięciem tokena, dlatego wszystkie moje punkty końcowe zaczynają się od sprawdzenia, czy: 1) token jest nadal ważny i 2) sesja użytkownika jest nadal ważna. Nie ma możliwości bezpośredniego unieważnienia tokena, ponieważ klienci przechowują go lokalnie.

Dlatego wszystkie moje punkty końcowe muszą sygnalizować moim klientom dwa możliwe warunki: 1) że nadszedł czas na odnowienie tokena lub 2) że sesja stała się nieważna i nie mają już dostępu do systemu. Mogę wymyślić dwie alternatywy dla moich punktów końcowych, aby zasygnalizować swoim klientom, gdy wystąpi jeden z dwóch warunków (zakładamy, że klienci mogą być dostosowani do dowolnej opcji):

  1. Zwróć kod http 401 (nieautoryzowany), jeśli sesja stała się nieprawidłowa, lub zwróć kod 412 (warunek wstępny nie powiódł się) po wygaśnięciu tokena i czas zadzwonić do renewpunktu końcowego, który zwróci kod 200 (ok).
  2. Zwraca 401 za zasygnalizowanie, że sesja jest nieprawidłowa lub token wygasł. W takim przypadku klient natychmiast wywoła renewpunkt końcowy, jeśli zwróci 200, token zostanie odświeżony, ale jeśli renewrównież zwróci 401, oznacza to, że klient jest poza systemem.

Którą z dwóch powyższych alternatyw poleciłbyś? Który z nich byłby bardziej standardowy, prostszy do zrozumienia i / lub bardziej RESTful? A może poleciłbyś w ogóle inne podejście? Czy widzisz jakieś oczywiste problemy lub zagrożenia bezpieczeństwa związane z którąkolwiek z tych opcji? Dodatkowe punkty, jeśli Twoja odpowiedź zawiera odniesienia zewnętrzne, które potwierdzają Twoją opinię.

AKTUALIZACJA

Chłopaki, proszę skoncentrować się na prawdziwym pytaniu - która z dwóch alternatywnych kodów HTTP sygnalizujących przedłużenie / unieważnienie sesji jest najlepsza? Nie przejmuj się faktem, że mój system korzysta z JWT i sesji po stronie serwera, to jest swoiste dla mojego API dla bardzo specyficznych reguł biznesowych, a nie tej części, której szukam pomocy;)

Óscar López
źródło
W jaki sposób sesja użytkownika stałaby się nieważna przed wygaśnięciem tokena, przy założeniu krótkiego (około 5 minut) okresu ważności?
Jack,
Z powodu reguły biznesowej inna część systemu może unieważnić sesję.
Óscar López,
1
Dokumenty JWT służą do potwierdzenia tożsamości, ponieważ w „potwierdzono, że żądanie to pochodzi od użytkownika X”. Jeśli twoja reguła biznesowa brzmi „użytkownik X nie może już wchodzić w interakcje z zasobem Y”, należy to sprawdzić osobno od JWT.
Jack
@Jack dokładnie! właśnie o to mi chodzi i powód, dla którego muszę używać dodatkowej warstwy stanowej do zapisywania informacji o sesji. Zwykły JWT, choć może być miły, po prostu nie jest przeznaczony do pracy.
Óscar López,
1
Być może zainteresuje Cię moja odpowiedź :)
Jack

Odpowiedzi:

22

To brzmi jak przypadek uwierzytelnienia kontra autoryzacja .

JWT są kryptograficznie podpisanymi oświadczeniami o inicjatorze żądania. JWT może zawierać oświadczenia takie jak „To żądanie dotyczy użytkownika X”, a „Użytkownik X ma role administratora”. Uzyskanie i dostarczenie tego dowodu za pomocą haseł, podpisów i TLS jest domeną uwierzytelniania - udowadniając, że jesteś tym, za kogo się podajesz.

Jakie roszczenia te myśli z serwerem - jakie konkretne role użytkowników i są dopuszczone do zrobienia - jest problem autoryzacji . Różnicę między tymi dwoma można opisać za pomocą dwóch scenariuszy. Załóżmy, że Bob chce wejść do sekcji magazynu ograniczonego w magazynie swojej firmy, ale najpierw musi poradzić sobie ze strażnikiem o imieniu Jim.

Scenariusz A - Uwierzytelnianie

  • Bob: „Witaj Jim, chciałbym wprowadzić ograniczoną przestrzeń dyskową.”
  • Jim: „Masz swoją odznakę?”
  • Bob: „Nie, zapomniałem”.
  • Jim: „Przepraszam kolego, brak wpisu bez odznaki”.

Scenariusz B - Autoryzacja

  • Bob: „Witaj Jim, chciałbym wprowadzić ograniczoną przestrzeń dyskową. Oto moja odznaka.”
  • Jim: „Hej Bob, potrzebujesz pozwolenia na poziomie 2, żeby tu wejść. Przepraszam”.

Czasy wygaśnięcia JWT to urządzenie uwierzytelniające, które zapobiega kradzieży ich przez innych. Jeśli wszystkie twoje JWT mają pięciominutowy okres ważności, nie będzie to aż tak wielka sprawa, jeśli zostaną skradzione, ponieważ szybko staną się bezużyteczne. Jednak omawiana zasada „wygasania sesji” brzmi jak problem z autoryzacją. Pewna zmiana stanu oznacza, że ​​użytkownik X nie może już robić czegoś, co kiedyś był w stanie zrobić. Na przykład użytkownik Bob mógł zostać zwolniony - nie ma znaczenia, że ​​jego odznaka mówi, że jest Bobem, ponieważ samo bycie Bobem nie daje mu już żadnej władzy w firmie.

Te dwa przypadki mają różne kody odpowiedzi HTTP: 401 Unauthorizedi 403 Forbidden. Niestety kod o nazwie 401 dotyczy problemów z uwierzytelnianiem, takich jak brakujące, wygasłe lub odwołane poświadczenia. 403 służy do autoryzacji, gdzie serwer dokładnie wie, kim jesteś, ale po prostu nie możesz robić tego, co próbujesz zrobić. W przypadku usunięcia konta użytkownika próba zrobienia czegoś za pomocą JWT w punkcie końcowym spowoduje 403 zabronioną odpowiedź. Jeśli jednak JWT utraci ważność, poprawny wynik to 401 Nieautoryzowany.

Częstym wzorem JWT jest posiadanie tokenów „długowiecznych” i „krótkotrwałych”. Tokeny długożyjące są przechowywane na kliencie jak tokeny krótkotrwałe, ale mają ograniczony zakres i są używane tylko z systemem autoryzacji do uzyskiwania tokonów krótkotrwałych. Długowieczne tokeny, jak sama nazwa wskazuje, mają bardzo długie okresy ważności - możesz ich użyć, aby poprosić o nowe tokeny przez kilka dni lub tygodni. Tokeny krótkotrwałe to tokeny, które opisujesz, używane z bardzo krótkim czasem przydatności do użycia w systemie. Tokeny długowieczne są przydatne do wdrożenia funkcji Remember Me, więc nie trzeba podawać hasła co pięć minut, aby uzyskać nowy token krótkotrwały.

Problem „unieważnienia sesji”, który opisujesz, brzmi podobnie do próby unieważnienia długowiecznego JWT, ponieważ te krótkotrwałe rzadko są przechowywane po stronie serwera, a te długofalowe są śledzone na wypadek konieczności ich odwołania. W takim systemie próba uzyskania poświadczeń za pomocą odwołanego długowiecznego tokena skutkowałaby 401 nieautoryzowaniem, ponieważ użytkownik może technicznie być w stanie uzyskać poświadczenia, ale token, którego używają, nie jest odpowiedni do tego zadania. Następnie, gdy użytkownik próbuje uzyskać nowy długowieczny token przy użyciu swojej nazwy użytkownika i hasła, system może odpowiedzieć 403 Forbidden, jeśli zostanie wyrzucony z systemu.

Jacek
źródło
3
Oto wskazówki, których szukałem, a wniosłeś bardzo istotny wgląd w dyskusję - że jest to przypadek uwierzytelnienia vs. autoryzacji i każdy z nich powinien być traktowany inaczej. Dzięki!
Óscar López,
16

Sesja interfejsu API jest rzeczą, która w ogóle nie powinna istnieć w świecie RESTful. Operacje RESTful powinny być bezstanowe, sesja zawiera stan i dlatego nie ma miejsca w świecie RESTful.

JWT powinien być Twoim jedynym sposobem ustalenia, czy użytkownik nadal kwalifikuje się do uzyskania dostępu do punktu końcowego, czy nie. Sesja nie powinna odgrywać absolutnie żadnej roli. Jeśli tak, nie masz interfejsu API RESTful.

Po całkowitym wyeliminowaniu sesji, co należy zrobić, jeśli dąży się do interfejsu API RESTful, i należy użyć JWT tylko jako czynnika uwierzytelniającego, użytkownik jest uprawniony do korzystania z punktu końcowego lub nie - w takim przypadku 401 Unauthorizedkod odpowiedzi jest odpowiedni - i powinien wywołać renewpunkt końcowy z grant_type=refresh_tokenjakąkolwiek identyfikacją odnowienia, której używasz.

Aktualizacja:

Z komentarza wynika, że ​​przepływ sprawdzania poprawności używanego obecnie JWT jest niepoprawny. Walidacja powinna wyglądać następująco:

  Client        RESTful API      JWT Issuer
     |              |                |
     |----- 1. ---->|                | 
     |              |------ 2. ----->|-----
     |              |                | 3. |
     |              |<----- 4. ------|<----
-----|<---- 5. -----|                |
| 6. |              |                |
---->|----- 7. ---->|                |
     |              |------ 8. ----->|-----
     |              |                | 9. |
     |              |<----- 10. -----|<----
     |              |                |
     |              |------          |
     |              | 11. |          |
     |<---- 12. ----|<-----          |
     |              |                |
     .              .                .

1. Ask RESTful API for a JWT using login endpoint.
2. Ask Issuer to create a new JWT.
3. Create JWT.
4. Return JWT to the RESTful API.
5. Return JWT to Client.
6. Store JWT to append it to all future API requests.
7. Ask for data from API providing JWT as authorization.
8. Send JWT to Issuer for verification.
9. Issuer verifies JWT.
10. Issuer returns 200 OK, verification successful.
11. Retrieve and format data for Client.
12. Return data to Client.

Serwer RESTful APImusi sprawdzić poprawność tokena wysyłanego jako autoryzacja. To nie jest odpowiedzialność Client. Wygląda na to, że obecnie tego nie robisz. W ten sposób zaimplementuj weryfikację JWT, a nie potrzebujesz sesji.

Andy
źródło
Dziękuję za odpowiedź. Zgadzam się, użycie sesji nie byłoby podejściem opartym w 100% na REST, ale jak wspomniałem powyżej, muszę odmówić dostępu niektórym użytkownikom przed wygaśnięciem tokena.
Óscar López,
2
@ ÓscarLópez Następnie unieważnij tokeny, których używają użytkownicy. Nie będą już mogli uzyskać dostępu do interfejsu API za pomocą dostarczonego tokena (który teraz zostanie unieważniony) i nie potrzebujesz sesji.
Andy,
1
Tokeny są przechowywane na kliencie, jak mogę je tam unieważnić? Musiałbym śledzić, które z nich są ważne ... i to tam stan podnosi brzydką głowę. Czasami jest to nieuniknione.
Óscar López,
Złośliwy klient może wysyłać poprzednio ważny token tak długo, jak pozwala na to jego okres ważności, ale muszę go natychmiast wykopać z systemu - dlatego ustawienie krótkiego czasu odnowienia również nie jest możliwe.
Óscar López
4
@Laiv Zrobiłem schemat sekwencji w notatniku.
Andy,
1

Wyznaję więc, że nie ma dla mnie sensu martwić się, które podejście jest najbardziej RESTfulne, gdy już łamiesz konwencje REST podczas sesji, ale rozumiem, że spełniasz wymagania biznesowe.

Z punktu widzenia REST klient albo jest uwierzytelniany, albo nie. Architektura nie dba o to, dlaczego (to wstrzykuje niepotrzebny stan), więc aby odpowiedzieć na twoje główne pytanie, w ogóle nie miałbym odnawiać punktu końcowego. Zalogowany klient po prostu zawsze wyśle ​​swój JWT, a serwer zawsze go potwierdzi i albo zaakceptuje, wysyłając odpowiedni kod sukcesu na podstawie akcji 200, 201 itd.), Albo odrzuci odpowiednio 401 lub 403.

Teraz JWT zostanie powiązany z jakimś kontem. To konto może być zablokowane, zdławione lub cokolwiek innego, więc sam token może być ważny, ale akcja nadal może zostać odrzucona w innym miejscu. Jeśli przypadek jest taki, że konto użytkownika jest zablokowane z powodu reguł biznesowych, to nadal jest to 401 lub 403, w zależności od tego, ile informacji chcesz przekazać klientowi (różne firmy mają różne opinie na ten temat).

Na koniec, jeśli twierdzisz, że konto może zostać odblokowane i ważne, ale JWT musi zostać tylko odwołane, to nadal będę trzymać 401 lub 403 i utrzymywać coś w rodzaju listy unieważnień certyfikatów nieprawidłowych JWT, które możesz umieścić w , o ile czyści się sam po wygaśnięciu JWT (większość baz danych ma na to sposób lub zdarzenia w kodzie aplikacji).

Paweł
źródło
jwt mają być bezpaństwowcami. w momencie, gdy kwestionujesz jego zawartość i sprawdzasz poprawność względem db, nie jest już bezstanowy, więc staje się zbędny, ponieważ istnieją lepsze rozwiązania dla stanowych sesji
Stavm