Why do we need Refresh Tokens? Do you know how to securely store Refresh Tokens and Access Tokens in the browser?

Why do we need Refresh Tokens? Do you know how to securely store Refresh Tokens and Access Tokens in the browser?

Articles in series:
  1. Why do we need Refresh Tokens? Do you know how to securely store Refresh Tokens and Access Tokens in the browser?
  2. Why Do We Need Refresh Token? Do You Know How to Securely Store Refresh Token and Access Token in the Browser? Part 2
Daily short news for you
  • For over a week now, I haven't posted anything, not because I have nothing to write about, but because I'm looking for ways to distribute more valuable content in this rapidly exploding AI era.

    As I shared earlier this year, the number of visitors to my blog is gradually declining. When I looked at the statistics, the number of users in the first six months of 2025 has dropped by 30% compared to the same period last year, and by 15% compared to the last six months of 2024. This indicates a reality that users are gradually leaving. What is the reason for this?

    I think the biggest reason is that user habits have changed. They primarily discover the blog through search engines, with Google being the largest. Almost half of the users return to the blog without going through the search step. This is a positive signal, but it's still not enough to increase the number of new users. Not to mention that now, Google has launched the AI Search Labs feature, which means AI displays summarized content when users search, further reducing the likelihood of users accessing the website. Interestingly, when Search Labs was introduced, English articles have taken over the rankings for the most accessed content.

    My articles are usually very long, sometimes reaching up to 2000 words. Writing such an article takes a lot of time. It's normal for many articles to go unread. I know and accept this because not everyone encounters the issues being discussed. For me, writing is a way to cultivate patience and thoughtfulness. Being able to help someone through my writing is a wonderful thing.

    Therefore, I am thinking of focusing on shorter and medium-length content to be able to write more. Long content will only be used when I want to write in detail or delve deeply into a particular topic. So, I am looking for ways to redesign the blog. Everyone, please stay tuned! 😄

    » Read more
  • CloudFlare has introduced the pay per crawl feature to charge for each time AI "crawls" data from your website. What does that mean 🤔?

    The purpose of SEO is to help search engines see the website. When users search for relevant content, your website appears in the search results. This is almost a win-win situation where Google helps more people discover your site, and in return, Google gets more users.

    Now, the game with AI Agents is different. AI Agents have to actively seek out information sources and conveniently "crawl" your data, then mix it up or do something with it that we can't even know. So this is almost a game that benefits only one side 🤔!?

    CloudFlare's move is to make AI Agents pay for each time they retrieve data from your website. If they don’t pay, then I won’t let them read my data. Something like that. Let’s wait a bit longer and see 🤓.

    » Read more
  • Continuing to update on the lawsuit between the Deno group and Oracle over the name JavaScript: It seems that Deno is at a disadvantage as the court has dismissed the Deno group's complaint. However, in August, they (Oracle) must be held accountable for each reason, acknowledging or denying the allegations presented by the Deno group in the lawsuit.

    JavaScript™ Trademark Update

    » Read more

The Issue

Note: This article discusses within the framework of OAuth 2.0 as described in rfc6749.

Hello, the story is that I have been following many people's discussions online these past few days about how to use access tokens and refresh tokens reasonably. After reading for a while, I realized there are two distinct schools of thought: some say that implementing only access tokens is sufficient, while others say that it is not secure without also having refresh tokens. However, the most prominent question is: assuming we have both access tokens and refresh tokens, where should we store them in the browser to ensure absolute safety?

So after many hours of reading through everyone's comments, I have gathered quite a bit of knowledge, including some things I used to do that were not very optimal. Therefore, let's sit down and find the answers together!

What are Access Tokens and Refresh Tokens?

Access tokens do not need much introduction; they are strings used to verify or represent a user in the system. A common example is when logging into a website, the system sends back a piece of code, which needs to be stored for subsequent API calls.

In the past, when PHP was prevalent, websites built with PHP were primarily rendered on the server side. What we often heard about back then was Session & Cookie. The server would return a value of the Cookie to determine a login session for the browser. When a query was sent, the Cookie accompanied it to help the server identify the user's session again. Nowadays, thanks to the development of various client-side rendering libraries like React, Vue, Svelte, etc., we are gradually shifting to a new session confirmation mechanism, which is the use of access tokens.

Therefore, fundamentally, an access token is just a piece of code returned by the server to identify a session for the client. Access tokens can be stored anywhere that can hold data, such as Cookies, LocalStorage, SessionStorage, or even in a file. But what about refresh tokens? What role do they play in the system? Simply put, refresh tokens are used to request a new access token.

Wait a minute, why do we need to issue a new access token? So it has an expiration time, right? Congratulations, you understand the issue correctly. But why complicate things like this? Isn't it enough just to use Sessions & Cookies as mentioned above? If you want to know the reason, let's first delve into the authorization problem.

The Authorization Problem

For many websites, the registration/login feature is indispensable as it is the best way to identify a user in the system. To log in, information such as email, phone number, along with a password is required. However, every day we use not just one website but possibly many, each requiring different login information, to the point where it becomes impossible to remember everything. It would be wonderful if you could log in everywhere with just one account. That’s when Google, Facebook, etc., come into play. Have you ever noticed the buttons with the label "Log in with Google"? You click it, the website redirects to Google, confirms the permission to access, and the login process is complete, so quick and risky, isn't it? Most of these login methods rely on the specifications of OAuth 2.

With OAuth 2, you can easily use another platform to log into a supported website, which is called authorization. It authorizes a website to read account information such as name, email address, phone number, etc., thereby identifying who you are and setting up a new usage session. Technically, after verifying identity, the server returns to the processing flow as if logging in with email and password normally, except that no information needs to be entered anymore, just a confirmation with the authorization provider.

Access tokens appear in the specifications of OAuth 2. An access token is returned immediately after authorization. That is, as soon as you click the button to allow access, the access token is returned to the website logging in. Based on that, the website verifies that you are a user in their system. Similarly, refresh tokens also appear in the specifications of OAuth 2. Access tokens usually have a very short lifespan (TTL), while refresh tokens are typically longer. When the access token expires, the refresh token is needed to request a new access token. This continues until the refresh token is invalidated, at which point no more can be requested.

Today, many websites build their login systems according to the specifications of OAuth 2 or at least something similar. Immediately after successful login, the system returns both refresh tokens and access tokens. The developers' job is to store these two important pieces of information in the browser for use in each subsequent call. They also have to handle expiration, renewal, invalidation, etc., of the access token.

Thus, having access tokens is almost tantamount to possessing an account in any system. The leakage of the access token is very dangerous because an attacker can impersonate a user and interact with the system without being detected. For this reason, access tokens must be stored or protected as securely as possible. So how can we best protect it? First, let's analyze what an access token consists of.

Access Tokens and JSON Web Tokens

Access tokens can be any string. However, most of them are usually JWT (Json Web Token). It is a standard for transmitting safe information between parties with data being a JSON object. This information is highly reliable because it has been signed using algorithms such as HMAC, RSA, or ECDSA.

jwt

A JWT object consists of three parts: header, payload, and signature. The header contains basic information about the JWT string, such as the algorithm used for signing, expiration, etc. The payload is a JSON object that has been base64 encoded. Finally, the signature is the encoded part of the header and payload combined with a secret key and the algorithm specified in the header.

To learn more about JWT, you can refer to Introduction to JSON Web Tokens.

The content in the payload may look like this:

{
  "id" : 1,
  "username": "hoaitx"
}

When the access token is sent, the server verifies its validity and begins reading the information in the payload. This way, it knows who the user currently using the system is.

One important note is that the information in the payload is usually only encoded with base64, which means that the information in the payload can be extracted from the JWT string. The payload should only contain information sufficient to identify the user; it should not contain sensitive information such as passwords or account numbers.

Why is a Refresh Token Necessary?

As mentioned, access tokens usually have a short lifespan (to reduce security risks if exposed). When the access token expires, instead of requiring the user to log in again, the application can use the refresh token to request a new access token automatically and securely, providing a smoother user experience.

Access tokens carry all the data within; the server does not need to maintain session state. When receiving the access token, the server only needs to check the signature and decode it to retrieve the information in the payload, without involving any database in the process. This is the stateless characteristic, which helps reduce memory load and server queries, making it easier to scale the application.

However, the strength is also the weakness of a stateless design system: it cannot be revoked or changed once issued. If an access token is leaked, an attacker can use it to access resources until it expires, and the server cannot intervene at that time. To mitigate this risk, access tokens are usually designed to exist only for a short time, ranging from a few minutes to a few hours.

But if access tokens are so short-lived, when they expire, users will have to log in repeatedly, leading to a very poor experience. This is exactly why refresh tokens are needed. The refresh token serves as a "renewal ticket" — allowing the application to request a new access token automatically, without requiring the user to log in again. Unlike access tokens, refresh tokens are often managed by the server (for example, stored in a database), so the server can proactively revoke or disable refresh tokens whenever necessary (for instance, when a user logs out or shows signs of being compromised).

After understanding the specifications of OAuth 2, the structure and function of access tokens, as well as the necessity of refresh tokens, we will continue to explore how to store them safely and securely!

Why do we need a Refresh Token? Do you know how to securely store Refresh Tokens and Access Tokens in the browser? Part 2.

Premium
Hello

Me & the desire to "play with words"

Have you tried writing? And then failed or not satisfied? At 2coffee.dev we have had a hard time with writing. Don't be discouraged, because now we have a way to help you. Click to become a member now!

Have you tried writing? And then failed or not satisfied? At 2coffee.dev we have had a hard time with writing. Don't be discouraged, because now we have a way to help you. Click to become a member now!

View all

Subscribe to receive new article notifications

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.

Comments (4)

Leave a comment...
Avatar
Tuan Nguyen2 years ago

Giả sử rằng: Nếu bị lộ Refresh Token thì sao ? Hacker có thể dùng Refresh Token để "xin" Access Token mới và từ đó sử dụng Access Token để truy cập vào những nội dung không mong muốn. Trong mỗi request được gửi lên (giả sử token được lưu trong cookie hoặc một nơi nào đó) thì request đó luôn bao gồm một cặp Refresh Token + Access Token. Vậy nếu bị "nhảy mất" cái request đó thì có đảm bảo rằng Access Token đó sẽ an toàn (Expired ư ? => Đã có Refresh Token lo hết ). Từ đó khẳng định rằng "nên thời gian càng ngắn thì nguy cơ access token khi bị lộ càng ít rủi ro." khá là thiếu căn cứ.

Reply
Avatar
Xuân Hoài Tống2 years ago

Trường hợp bạn nêu trên thì đúng là "thiếu căn cứ" theo như bạn nói. Nhưng đó là trong trường hợp RT + AT được gửi lên trong "mỗi" request. Thực tế thì bạn không nhất thiết phải gửi lên RT trong các yêu cầu đến máy chủ tài nguyên. RT chỉ nên được gửi đến máy chủ ủy quyền (nhiều người hay bị nhầm giữa máy chủ tài nguyên & máy chủ ủy quyền, cũng có thể do máy chủ tài nguyên + ủy quyền là một) để xin AT mới. Thế nên có một vài lý do để bảo mật RT hơn AT để hạn chế RT bị rò rỉ. Nhưng phải nói khi AT bị đánh cắp thì RT bị "bay" theo cũng rất lớn. Chính vì thế chúng ta phải có biện pháp mạnh trên máy chủ ủy quyền để phát hiện RT bị đánh cắp. Vấn đề này cũng có nhiều giải pháp rồi bạn có thể tham khảo các nguồn trên GG, tôi đang tiếp tục series bài viết về AT & RT chỉ là chưa sắp xếp được thời gian, khi nào có mong bạn tiếp tục xem có vấn đề gì cần làm sáng tỏ hơn nữa không.

Avatar
Xuân Hoài Tống2 years ago

Bonus: AT sinh ra để giảm thiểu gánh nặng trên máy chủ tài nguyên, việc còn lại là gánh nặng của máy chủ ủy quyền nên RT phải làm chặt chẽ hơn. Cụ thể phải từ RT để lấy AT nên AT tồn tại càng ngắn thì càng tốt, bắt buộc phải qua máy chủ ủy quyền để kiểm tra gian lận. Dĩ nhiên là khoảng thời gian "ngắn" đó chỉ mang tính tương tối cho các dự án.

Avatar
Đình Trung2 years ago

@gif [XejFQNGS90SvaITLRZ] Mấy tháng rồi nhỉ

Reply
Avatar
Long Domi2 years ago

@gif [10JhviFuU2gWD6] Sắp có từ một tháng trước

Reply
Avatar
Linh Trịnh Mạnh3 years ago

Ủa phần tiếp theo đâu anh ơi đang cuốn mà hóng mãi không thấy

Reply
Avatar
Xuân Hoài Tống3 years ago

Sắp có rồi bạn 😅