iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🔑

[OAuth 2.0] The Dangers of Careless Implementation: Proper Authorization Code Flow and Security Measures

に公開

"Do you want to implement a login feature with OAuth 2.0, but find the flow so complex that you just end up copy-pasting code?" 😰

In reality, if you implement OAuth incorrectly, there is a risk of access tokens leaking or accounts being hijacked. In particular, quite a few older tutorial articles introduce methods that are now deprecated.

In this article, I will explain the mechanism of the "Authorization Code Flow," which is currently the most secure and recommended method, along with implementation patterns you should absolutely avoid.

What is OAuth 2.0?

OAuth 2.0 is a framework for Authorization. It is a mechanism for a "user (resource owner) to grant an application (client) permission to access their data (resource) stored in another service."

Its key feature is the ability to link services safely without entrusting your ID/password to the application.

Roles and Flow

There are four main roles involved in OAuth 2.0.

Abstract Flow of OAuth 2.0

  1. Resource Owner: The owner of the data (you)
  2. Client: The app you want to integrate
  3. Authorization Server: The server that manages authorization (Google, Twitter, etc.)
  4. Resource Server: The server that holds the data (API)

Summary: Is That Implementation Okay?

Choosing the wrong flow can lead to vulnerabilities.

OAuth 2.0 Flow Comparison

Flow Name Characteristics Current Recommendation
Authorization Code Flow Performs token exchange on the backend. Tokens are not exposed to the browser. ⭐️ Recommended (Standard)
Implicit Flow Receives tokens via URL fragments. Risk of remaining in browser history. Deprecated
Client Credentials No user involved. Used for server-to-server communication. Limited Use Cases

Specific Implementation Points

To enhance security, the following measures are essential.

1. CSRF Protection with the state Parameter

When sending an authorization request, generate a random string (state), store it in the session, and include it in the parameters. When the callback returns, verify that it matches the state you sent. Without this, an attacker could potentially force a user to link their account to one the attacker has prepared.

// 1. At the time of authorization request
const state = generateRandomString();
session.state = state;
res.redirect(`https://auth-server.com/authorize?response_type=code&client_id=MY_ID&state=${state}&...`);

// 2. Upon receiving the callback
if (req.query.state !== session.state) {
  throw new Error("State mismatch! CSRF attack detected.");
}

2. Never Leak the Client Secret

You use the client_secret when exchanging an "authorization code" for an "access token." This process must always be performed on the backend server.

If you embed the client_secret in the frontend (such as a SPA or mobile app), it can be easily extracted through reverse engineering.

3. Handling Refresh Tokens

Access tokens have short expiration periods (e.g., 1 hour) and are updated using refresh tokens. If a refresh token is leaked, it allows for persistent unauthorized access, so it requires even stricter management (such as storing it in an HttpOnly Cookie).

Implementation Example: Authorization Code Exchange with Node.js Express

// Image of callback processing
app.get('/callback', async (req, res) => {
  const { code, state } = req.query;

  // 1. Validate State
  if (state !== req.session.state) return res.status(403).send('Invalid state');

  // 2. Exchange authorization code for access token (backend communication)
  const tokenResponse = await axios.post('https://auth-server.com/token', {
    grant_type: 'authorization_code',
    code,
    redirect_uri: REDIRECT_URI,
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET // Keep only on the server-side
  });

  const { access_token } = tokenResponse.data;

  // 3. Access API using access token
  // ...
});

Conclusion

OAuth 2.0 is powerful, but it becomes a security hole if not implemented correctly. Instead of just "making it work," choose a recommended flow (especially Authorization Code Flow + PKCE) and implement it after understanding the meaning of each parameter.

To prevent security incidents, using libraries (like NextAuth.js) is one option, but understanding the underlying mechanism is important.

Discussion