OAuth 2.0 is the foundation for modern identity and access management, enabling applications to delegate user authentication securely. In this guide, you’ll learn how to implement the Authorization Code Flow—the most secure OAuth flow for web apps—using Node.js and Express. This is ideal for server-rendered apps or Backend-for-Frontend (BFF) patterns where you control the server exchanging the code for tokens.
We’ll walk through everything from route setup to token exchange using only open-source libraries and built-in Express functionality.
Why Authorization Code Flow in Node.js?
Node.js is widely used for building scalable web applications. The Authorization Code Flow ensures:
- Secure handling of access and refresh tokens.
- Server-side exchange of credentials.
- Improved user experience with redirect-based login.
- Compatibility with providers like ForgeRock, Auth0, Okta, or Google.
How the Flow Works
Here’s a visual of the standard Authorization Code Flow in a Node.js web app:
+--------+ (1) Redirect to AuthZ URL +---------------+
| |----------------------------------------->| |
| | | Authorization |
| User | (2) Login & Grant Access | Server |
| |<-----------------------------------------| |
| | (3) Redirect with Code +---------------+
| |----------------------------------------->| Express App |
| | (4) Token Exchange (code) | |
| |<-----------------------------------------| |
+--------+ (5) Session Starts +---------------+
Step 1: Set Up the Express Server
// Node.js (Express) starter app with OAuth 2.0 support
const express = require('express');
const session = require('express-session');
const axios = require('axios');
const querystring = require('querystring');
const app = express();
const port = 3000;
app.use(session({
secret: 'your-session-secret',
resave: false,
saveUninitialized: true
}));
Step 2: Redirect to Authorization Server
// Endpoint to start OAuth 2.0 login
app.get('/login', (req, res) => {
const params = {
response_type: 'code',
client_id: 'your-client-id',
redirect_uri: 'http://localhost:3000/callback',
scope: 'openid profile email',
state: 'random_state_string' // CSRF protection
};
const authUrl = `https://your-oauth-server.com/oauth2/authorize?${querystring.stringify(params)}`;
res.redirect(authUrl);
});
Step 3: Handle Callback and Exchange Code for Tokens
// OAuth 2.0 callback route
app.get('/callback', async (req, res) => {
const { code } = req.query;
try {
const tokenResponse = await axios.post('https://your-oauth-server.com/oauth2/token',
querystring.stringify({
grant_type: 'authorization_code',
code,
redirect_uri: 'http://localhost:3000/callback',
client_id: 'your-client-id',
client_secret: 'your-client-secret'
}),
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
);
req.session.tokens = tokenResponse.data;
res.redirect('/profile');
} catch (err) {
res.status(500).send('Token exchange failed');
}
});
Step 4: Access Protected Resources
// Protected route using access token
app.get('/profile', async (req, res) => {
if (!req.session.tokens) return res.redirect('/login');
try {
const userInfo = await axios.get('https://your-oauth-server.com/userinfo', {
headers: {
Authorization: `Bearer ${req.session.tokens.access_token}`
}
});
res.send(`<pre>${JSON.stringify(userInfo.data, null, 2)}</pre>`);
} catch (err) {
res.status(401).send('Access denied');
}
});
Step 5: Logout and Clean Session
// End session
app.get('/logout', (req, res) => {
req.session.destroy();
res.send('Logged out');
});
Security Notes
- Always validate the
state
parameter for CSRF protection. - Use HTTPS in production environments.
- Use secure cookies and proper session handling in production.
Real Use Cases and Extensions
Node.js is often used in:
- Building OAuth 2.0 gateways
- Integrating with ForgeRock or Auth0 BFF flows
- Custom login experiences with Express and React hybrid apps
👉 Related:
Understanding the Authorization Code Flow in OAuth 2.0
Understanding the Authorization Code Flow with PKCE in OAuth 2.0
Conclusion
OAuth 2.0 with Node.js and Express provides a flexible, secure, and standards-compliant foundation for managing user identity and sessions. With minimal dependencies, you can build your own OAuth login layer or integrate with existing providers.
💡 What’s Next?
- How do you refresh tokens automatically using
express-session
? - Should you implement PKCE for public-facing SPAs in Node.js?
- What role does OAuth 2.1 play in evolving this pattern?
Stay tuned as we cover these questions and more in the upcoming posts.