Вопрос о том, где хранить JWT (и маркеры доступа в целом), является распространенным, и, похоже, среди разработчиков нет единого мнения.1
Некоторые считают, что JWT никогда не должны храниться в localStorage
, в то время как другие считают, что хранить их там можно. У каждой стороны есть свои доводы и объяснения для своих убеждений. Этот вопрос обсуждался долго и, на мой взгляд, непродуктивно.2
Вместо того чтобы продолжать эти бесконечные дебаты, давайте рассмотрим популярные SDK и посмотрим, как они помогают одностраничным приложениям (SPA) хранить свои JWT. Поехали!
Auth0
-
Для SPA, Auth0 вообще не хранит JWT в хранилище на стороне клиента.
-
Вместо этого Auth0 SDK встраивает скрытый
<iframe>
, который выполняет поток кода авторизации с доказательством ключа для обмена кодами (PKCE) в сочетании с молчаливой аутентификацией. Этот поток выполняется каждый раз при загрузке SPA, поэтому JWT не сохраняется в каком-либо месте хранения. Кроме того, запросы токенов выполняются в Web Workers, что обеспечивает дополнительный уровень безопасности.
-
Состояние входа поддерживается внутри
<iframe>
с помощью кукиHttpOnly
. Ни один из файлов cookie не является JWT. -
После завершения процесса аутентификации
<iframe>
удаляется из DOM. -
Safari с интеллектуальной защитой от слежения блокирует сторонние файлы cookie, что может нарушить поток аутентификации Silent. Чтобы обойти это, вы можете настроить пользовательские домены для Auth0 (требуется платный тарифный план), чтобы сессионные куки Auth0 стали куками первой стороны. В качестве альтернативы можно использовать функцию Refresh Token Rotation, но тогда состояние входа будет потеряно при обновлении страницы, если только вы не решите хранить токены в
localStorage
.
AWS Amplify
-
ID-токены, токены доступа и токены обновления хранятся в
localStorage
. -
Все они являются JWT. Вы знаете, потому что они начинаются с
eyJ
. Они имеют подпись RS256.
Firebase Auth
-
Firebase Auth по умолчанию хранит токен доступа и токены обновления в IndexedDB.
-
Токен доступа
accessToken
представляет собой JWT с подписью RS256. Он также функционирует как ID-токен (и упоминается как таковой в документации). Использование RS256 JWT удобно тем, что сторонние приложения могут проверять ID-токен с помощью открытых ключей Firebase без необходимости делиться секретными ключами. -
Между тем, маркер обновления представляет собой непрозрачную строку.
Supabase
-
Supabase хранит маркер доступа и маркер обновления в
localStorage
. -
Маркер доступа — это JWT с подписью HS256, а маркер обновления — непрозрачная строка.
Выводы
В заключение отметим, что различные SDK для аутентификации реализовали различное поведение по умолчанию для хранения маркеров.
Важно учитывать контекст, связанный с выбором, сделанным этими SDK. То, что несколько популярных SDK хранят токены в localStorage
, не означает, что то же самое в наших пользовательских реализациях обеспечит нам безопасную схему аутентификации; мы также должны рассмотреть, как эти SDK помогают разработчикам защитить токены от неправильного использования.
Если у вас есть другие примеры, не стесняйтесь комментировать!
-
- https://twitter.com/swyx/status/1133780714988736512
- https://www.reddit.com/r/webdev/comments/bpcleu/so_whats_the_issue_with_jwts_in_localstorage/
- https://twitter.com/wesbos/status/1310637597480411138
- https://stackoverflow.com/questions/44133536/is-it-safe-to-store-a-jwt-in-localstorage-with-reactjs
-
Пожалуйста, не обращайте внимания на мои бредни.
Всякий раз, когда по касательной затрагивается тема хранения токенов, кажется, что кто-то должен провозгласить «никогда не храните JWT в localStorage» и сорвать разговор. Это происходит так часто, и очень интересно наблюдать, на что иногда идут люди, чтобы доказать свою точку зрения. Когда я указал на наши средства защиты от XSS и ненадежных активов, я получил ответ: «Но пользователь может установить вредоносную программу, которая может читать хранилище. Вот я создал расширение, которое читает localStorage сайта. Видите? localStorage — это плохо».