История изменений
Исправление theNamelessOne, (текущая версия) :
Но возникает одно НО. На многих сайтах есть функция «выйти со всех устройств» или даже список сессий и возможность кикнуть любую сессию. Это полезно в случае, если нечаянно залогинился на чужом компьютере или если тебя поломали, но ты успел сменить пароль.
Не только, есть ещё как минимум один момент — если ты обновил какие-то важные данные юзера (например, удалил у него роль admin
), в уже существующих токенах данные останутся протухшими.
В голову пришёл ещё компромиссный вариант, что можно гонять короткоживущие токены второго типа, но с расчётом, что они будут постоянно протухать и будет происходить переавторизация по токену первого типа. В этом случае получается, что функция «выйти со всех устройств» работать будет, но с лагом несколько минут/часов.
(Лаг «в несколько часов» — это security nightmare в данном случае. Stateless JWT должны быть короткоживущими).
В данном контексте в терминах JWT «токен второго типа» обычно называют access token
, а «токен первого типа» — refresh token
. Чтобы сделать это без лага, тебе всё равно понадобится какое-то хранилище (например, in-memory или Redis), в которое тебе придётся лезть на каждый запрос аутентификации.
Например, юзер нажал «выйти со всех устройств»/«выйти со всех устройств, кроме этого». На сервере:
- Инвалидируешь в БД все активные refresh-токены (кроме текущего в случае «выйти со всех устройств, кроме этого»).
- Пишешь в Redis ID юзера и текущее время, ставишь TTL этой записи равным времени жизни access-токена.
- При запросах аутентификации проверяешь для access-токена наличие записи юзер/таймпстамп в Редисе из прошлого пункта. Если эта запись есть, и таймпстамп больше времени создания токена, отклоняешь токен.
Если же ты обновил данные юзера, и тебе нужно просто инвалидировать access-токены (чтобы получить новые/актуальные), то алгоритм тот же, просто пропускаешь первый пункт.
Также ещё в том же Redis хорошо бы хранить blacklist протухших access-токенов, с тем же TTL, равным времени access-токена, это позволит реализовать функцию «выйти с текущего устройства». Да, тебе тоже его надо будет проверять на каждом запросе аутентификации.
Всё это несколько нивелирует преимущества по производительности и I/O, которые ты описал, но: 1) Redis обычно быстрее и легче, чем традиционная RDBMS; 2) данных там будет гораздо меньше, чем в таблице sessions
(т.к. мы храним там только протухшие access-токены и записи user-id,current-timestamp, плюс данные там регулярно очищаются за счёт того, что у каждой записи TTL равен времени жизни access-токена); 3) эти данные не так страшно потерять; 4) Redis достаточно просто масштабировать. Так что это всё равно должно быть быстрее, чем хранить сессии в традиционной RDBMS.
Из минусов такого — тебе это надо запилить, придусмотреть все граничные случаи (а они наверняка ещё есть, я написал только про то, что пришло в голову сразу), покрыть тестами. Реализация получается более сложная в итоге. Я бы тебе рекомендовал сначала убедиться, что один запрос в БД для получения сессии на каждый запрос к твоему сервису действительно является узким местом в твоём приложении.
И вот, почитай ещё:
Исправление theNamelessOne, :
Но возникает одно НО. На многих сайтах есть функция «выйти со всех устройств» или даже список сессий и возможность кикнуть любую сессию. Это полезно в случае, если нечаянно залогинился на чужом компьютере или если тебя поломали, но ты успел сменить пароль.
Не только, есть ещё как минимум один момент — если ты обновил какие-то важные данные юзера (например, удалил у него роль admin
), то эти обновлённые данные появятся в новом токене для юзера.
В голову пришёл ещё компромиссный вариант, что можно гонять короткоживущие токены второго типа, но с расчётом, что они будут постоянно протухать и будет происходить переавторизация по токену первого типа. В этом случае получается, что функция «выйти со всех устройств» работать будет, но с лагом несколько минут/часов.
(Лаг «в несколько часов» — это security nightmare в данном случае. Stateless JWT должны быть короткоживущими).
В данном контексте в терминах JWT «токен второго типа» обычно называют access token
, а «токен первого типа» — refresh token
. Чтобы сделать это без лага, тебе всё равно понадобится какое-то хранилище (например, in-memory или Redis), в которое тебе придётся лезть на каждый запрос аутентификации.
Например, юзер нажал «выйти со всех устройств»/«выйти со всех устройств, кроме этого». На сервере:
- Инвалидируешь в БД все активные refresh-токены (кроме текущего в случае «выйти со всех устройств, кроме этого»).
- Пишешь в Redis ID юзера и текущее время, ставишь TTL этой записи равным времени жизни access-токена.
- При запросах аутентификации проверяешь для access-токена наличие записи юзер/таймпстамп в Редисе из прошлого пункта. Если эта запись есть, и таймпстамп больше времени создания токена, отклоняешь токен.
Если же ты обновил данные юзера, и тебе нужно просто инвалидировать access-токены (чтобы получить новые/актуальные), то алгоритм тот же, просто пропускаешь первый пункт.
Также ещё в том же Redis хорошо бы хранить blacklist протухших access-токенов, с тем же TTL, равным времени access-токена, это позволит реализовать функцию «выйти с текущего устройства». Да, тебе тоже его надо будет проверять на каждом запросе аутентификации.
Всё это несколько нивелирует преимущества по производительности и I/O, которые ты описал, но: 1) Redis обычно быстрее и легче, чем традиционная RDBMS; 2) данных там будет гораздо меньше, чем в таблице sessions
(т.к. мы храним там только протухшие access-токены и записи user-id,current-timestamp, плюс данные там регулярно очищаются за счёт того, что у каждой записи TTL равен времени жизни access-токена); 3) эти данные не так страшно потерять; 4) Redis достаточно просто масштабировать. Так что это всё равно должно быть быстрее, чем хранить сессии в традиционной RDBMS.
Из минусов такого — тебе это надо запилить, придусмотреть все граничные случаи (а они наверняка ещё есть, я написал только про то, что пришло в голову сразу), покрыть тестами. Реализация получается более сложная в итоге. Я бы тебе рекомендовал сначала убедиться, что один запрос в БД для получения сессии на каждый запрос к твоему сервису действительно является узким местом в твоём приложении.
И вот, почитай ещё:
Исправление theNamelessOne, :
Но возникает одно НО. На многих сайтах есть функция «выйти со всех устройств» или даже список сессий и возможность кикнуть любую сессию. Это полезно в случае, если нечаянно залогинился на чужом компьютере или если тебя поломали, но ты успел сменить пароль.
Не только, есть ещё как минимум один момент — если ты обновил какие-то важные данные юзера (например, удалил у него роль admin
), то клиент об этом никак не узнает, пока не получит новый токен.
В голову пришёл ещё компромиссный вариант, что можно гонять короткоживущие токены второго типа, но с расчётом, что они будут постоянно протухать и будет происходить переавторизация по токену первого типа. В этом случае получается, что функция «выйти со всех устройств» работать будет, но с лагом несколько минут/часов.
(Лаг «в несколько часов» — это security nightmare в данном случае. Stateless JWT должны быть короткоживущими).
В данном контексте в терминах JWT «токен второго типа» обычно называют access token
, а «токен первого типа» — refresh token
. Чтобы сделать это без лага, тебе всё равно понадобится какое-то хранилище (например, in-memory или Redis), в которое тебе придётся лезть на каждый запрос аутентификации.
Например, юзер нажал «выйти со всех устройств»/«выйти со всех устройств, кроме этого». На сервере:
- Инвалидируешь в БД все активные refresh-токены (кроме текущего в случае «выйти со всех устройств, кроме этого»).
- Пишешь в Redis ID юзера и текущее время, ставишь TTL этой записи равным времени жизни access-токена.
- При запросах аутентификации проверяешь для access-токена наличие записи юзер/таймпстамп в Редисе из прошлого пункта. Если эта запись есть, и таймпстамп больше времени создания токена, отклоняешь токен.
Если же ты обновил данные юзера, и тебе нужно просто инвалидировать access-токены (чтобы получить новые/актуальные), то алгоритм тот же, просто пропускаешь первый пункт.
Также ещё в том же Redis хорошо бы хранить blacklist протухших access-токенов, с тем же TTL, равным времени access-токена, это позволит реализовать функцию «выйти с текущего устройства». Да, тебе тоже его надо будет проверять на каждом запросе аутентификации.
Всё это несколько нивелирует преимущества по производительности и I/O, которые ты описал, но: 1) Redis обычно быстрее и легче, чем традиционная RDBMS; 2) данных там будет гораздо меньше, чем в таблице sessions
(т.к. мы храним там только протухшие access-токены и записи user-id,current-timestamp, плюс данные там регулярно очищаются за счёт того, что у каждой записи TTL равен времени жизни access-токена); 3) эти данные не так страшно потерять; 4) Redis достаточно просто масштабировать. Так что это всё равно должно быть быстрее, чем хранить сессии в традиционной RDBMS.
Из минусов такого — тебе это надо запилить, придусмотреть все граничные случаи (а они наверняка ещё есть, я написал только про то, что пришло в голову сразу), покрыть тестами. Реализация получается более сложная в итоге. Я бы тебе рекомендовал сначала убедиться, что один запрос в БД для получения сессии на каждый запрос к твоему сервису действительно является узким местом в твоём приложении.
И вот, почитай ещё:
Исходная версия theNamelessOne, :
Но возникает одно НО. На многих сайтах есть функция «выйти со всех устройств» или даже список сессий и возможность кикнуть любую сессию. Это полезно в случае, если нечаянно залогинился на чужом компьютере или если тебя поломали, но ты успел сменить пароль.
Не только, есть ещё как минимум один момент — если ты обновил какие-то важные данные юзера (например, удалил у него роль admin
), то клиент об этом никак не узнает, пока не получит новый токен.
В голову пришёл ещё компромиссный вариант, что можно гонять короткоживущие токены второго типа, но с расчётом, что они будут постоянно протухать и будет происходить переавторизация по токену первого типа. В этом случае получается, что функция «выйти со всех устройств» работать будет, но с лагом несколько минут/часов.
В данном контексте в терминах JWT «токен второго типа» обычно называют access token
, а «токен первого типа» — refresh token
. Чтобы сделать это без лага, тебе всё равно понадобится какое-то хранилище (например, in-memory или Redis), в которое тебе придётся лезть на каждый запрос аутентификации.
Например, юзер нажал «выйти со всех устройств»/«выйти со всех устройств, кроме этого». На сервере:
- Инвалидируешь в БД все активные refresh-токены (кроме текущего в случае «выйти со всех устройств, кроме этого»).
- Пишешь в Redis ID юзера и текущее время, ставишь TTL этой записи равным времени жизни access-токена.
- При запросах аутентификации проверяешь для access-токена наличие записи юзер/таймпстамп в Редисе из прошлого пункта. Если эта запись есть, и таймпстамп больше времени создания токена, отклоняешь токен.
Если же ты обновил данные юзера, и тебе нужно просто инвалидировать access-токены (чтобы получить новые/актуальные), то алгоритм тот же, просто пропускаешь первый пункт.
Также ещё в том же Redis хорошо бы хранить blacklist протухших access-токенов, с тем же TTL, равным времени access-токена, это позволит реализовать функцию «выйти с текущего устройства». Да, тебе тоже его надо будет проверять на каждом запросе аутентификации.
Всё это несколько нивелирует преимущества по производительности и I/O, которые ты описал, но: 1) Redis обычно быстрее и легче, чем традиционная RDBMS; 2) данных там будет гораздо меньше, чем в таблице sessions
(т.к. мы храним там только протухшие access-токены и записи user-id,current-timestamp, плюс данные там регулярно очищаются за счёт того, что у каждой записи TTL равен времени жизни access-токена); 3) эти данные не так страшно потерять; 4) Redis достаточно просто масштабировать. Так что это всё равно должно быть быстрее, чем хранить сессии в традиционной RDBMS.
Из минусов такого — тебе это надо запилить, придусмотреть все граничные случаи (а они наверняка ещё есть, я написал только про то, что пришло в голову сразу). Реализация получается более сложная в итоге. Я бы тебе рекомендовал сначала убедиться, что один запрос в БД для получения сессии на каждый запрос к твоему сервису действительно является узким местом в твоём приложении.
И вот, почитай ещё: