Logując się na nowej maszynie otrzymujesz token oraz refresh token (o tym za chwilę). Zatem każde nowe urządzenie powinno mieć taką parę kluczy, którą będzie przechowywać w pamięci. Token przechowujemy w pamięci tymczasowej (zmienna), natomiast refresh token w pamięci stałej (cookie lub local storage).
Token powinien mieć jak najkrótszy czas ważności. Jego czas zależy od częstotliwości żądań do serwera. Zazwyczaj czas waha się w okolicach 5-15 minut. Jest to średni czas korzystania z appki (w sensie częste requesty).
Refresh token służy nam do odświeżenia tokena głównego po jego wygaśnięciu (taka a'la sesja). Dzięki temu zabiegowi użytkownik nie musi się logować co 15 minut. Przed wysłaniem żądania sprawadzamy ważność tokena głównego. Jeśli jego czas ważności wygasł, prosimy serwer o przyznanie nowego tokena używając refresh token. Następnie wykorzystując świeży token wykonujemy żądanie.
Refresh token powinien być przechowywany w bazie danych w postaci refresh-token (+ np. jakaś nazwa urządzenia do wyświetlenia aktywnych sesji) + user id. Dzięki temu możemy generować nowe tokeny dla danego użytkownika na danej maszynie bez konieczności ponownego logowania. Usuwając refresh token z bazy, usuwamy aktywną sesję na danym urządzeniu i wymagane będzie ponowne logowanie. Skoro jesteśmy przy tym temacie, to przy ponownym uruchomieniu aplikacji (za dzień, dwa itp.) wysyłamy żądanie o token wykorzysując refresh-token. Jeśli widnieje on w bazie, to zostaniemy zalogowani. Kiedy z innego urządzenia usuniemy sesję, to wymagana będzie ponowna autentykacja.
Autoryzacja działa na podstawie nagłówków HTTP. JWT Token przechowuje informacje o użytkowniku, więc w momencie wysłania żądania, odczytujemy tę wartość z nagłówka HTTP. Czyli nie dołączasz w body informacji o tokenie. Jest ona w nagłówku. Jest to istotne, ponieważ tylko POST i PUT posiadają body. GET oraz DELETE już nie.
W części webowej aplikacji dołączasz nagłówek:
fetch('api_url', {
method: 'get',
headers: new Headers({
'Authorization': `Bearer ${JWTToken}`
})
});
W części serwerowej odbierasz go:
if(req.headers.authorization) {
const tokenType = req.headers.authorization.split(' ')[0];
const token = req.headers.authorization.split(' ')[1];
// Sprawdzanie czy token jest ważny
let user = undefined;
if(tokenType === 'Bearer') {
// Odczytujemy dane z Bearer token i przypisujemy je do user
} else {
// Odczytujemy dane z innego tokena i przypisujemy je do user
}
if(user) {
// Użytkownik jest zautoryzowany, mamy dostęp np. przez
const userId = user.id;
}
}
Dzięki temu wyślesz nagłówek wraz z żądaniem, a serwer będzie w stanie je autoryzować. Jest to zachowanie opcjonalne, możesz autoryzować tylko określone endpoints, lecz zawsze wysyłaj token (nie wiesz kiedy się przyda ;)).
Okej, a co w przypadku zamknięcia sesji w czasie kiedy token jest jeszcze aktywny? Otóż można wrzucić dany token do czarnej listy w bazie danych aplikacji, aby nikt więcej nie mógł go użyć. To dodaje nam kolejną walidację (chociaż uważam, że równie dobrym rozwiązaniem jest trzymanie czarnej listy w pamięci aplikacji i czyszczenie jej co pewien interwał czasu).
Ufff, sporo tego wyszło, ale mam nadzieję, że to rozwieje trochę Twoje wątpliwości ;)