OAuth2orize - OAuth 2.0 Authorization Server Toolkit
OAuth2orize is a Node.js toolkit designed for implementing OAuth 2.0 authorization servers. It provides a suite of modular middleware functions that allow developers to construct a server supporting various OAuth 2.0 grant types, such as authorization code, implicit, password, and client credentials, along with refresh token functionality. The library, currently at stable version 1.12.0, integrates seamlessly with Passport.js for user authentication, acting primarily as the authorization layer. Its architecture requires application-specific route handlers and persistent storage for clients, authorization codes, and access tokens, which are not provided out-of-the-box. Due to its long-standing stability and minimal recent updates (last published 2 years ago), it operates under a maintenance release cadence, indicating it's a mature project rather than one undergoing active feature development. A key differentiator is its highly pluggable middleware design, allowing granular control over the OAuth flow, though this also means more boilerplate compared to opinionated, full-stack solutions.
Common errors
-
TypeError: oauth2orize.createServer is not a function
cause Attempting to use ES Module `import` syntax (`import { createServer } from 'oauth2orize'`) with a CommonJS-only library.fixUse CommonJS `require` syntax: `const oauth2orize = require('oauth2orize'); const server = oauth2orize.createServer();` -
Error: Invalid redirect URI
cause The `redirectURI` provided by the client in the authorization request does not exactly match the `redirectUri` registered for that client on your OAuth2orize server.fixEnsure the `redirectURI` in your client registration (`db.clients` in the example) precisely matches the `redirect_uri` parameter sent by the client in the authorization request. -
ReferenceError: XXX is not defined (e.g., AuthorizationCode, AccessToken, utils)
cause The example code in the documentation often uses placeholder models or utility functions (`AuthorizationCode`, `AccessToken`, `utils.uid`) that are not part of `oauth2orize` itself and must be provided by the implementer.fixDefine these models/utilities (e.g., `AuthorizationCode` and `AccessToken` classes with persistence logic, or a `utils` object with a `uid` function) in your application code.
Warnings
- breaking OAuth2orize is built upon older specifications (OAuth 2.0 RFC 6749) and does not inherently conform to the latest OAuth 2.1 best practices. Key security enhancements like PKCE enforcement for all clients, refresh token rotation, and strict redirect URI matching (now mandatory in OAuth 2.1) are not automatically handled and require manual implementation or external modules.
- gotcha The library is CommonJS-only (`require`) and does not support ES Modules (`import`). Attempting to use `import` statements will result in runtime errors like 'oauth2orize is not a function' or 'Cannot find module'.
- gotcha OAuth2orize provides the framework for an OAuth 2.0 server but does not include any persistence layer for clients, users, authorization codes, or access tokens. Developers must implement their own storage mechanisms (e.g., database integrations).
- gotcha The library relies heavily on Passport.js for user authentication before authorization. Without a correctly configured Passport setup, the `server.authorize()` middleware will not function as expected for authenticating the end-user.
- deprecated OAuth 2.1 has officially deprecated and removed the Implicit Grant Flow and the Resource Owner Password Credentials (ROPC) Grant Flow due to security vulnerabilities. While OAuth2orize supports these flows, their use in new applications is strongly discouraged.
Install
-
npm install oauth2orize -
yarn add oauth2orize -
pnpm add oauth2orize
Imports
- createServer
import { createServer } from 'oauth2orize';const oauth2orize = require('oauth2orize'); const server = oauth2orize.createServer(); - grant.code
import { grant } from 'oauth2orize'; server.grant(grant.code(...));const oauth2orize = require('oauth2orize'); server.grant(oauth2orize.grant.code(...)); - exchange.code
import { exchange } from 'oauth2orize'; server.exchange(exchange.code(...));const oauth2orize = require('oauth2orize'); server.exchange(oauth2orize.exchange.code(...));
Quickstart
const express = require('express');
const oauth2orize = require('oauth2orize');
const passport = require('passport');
const BasicStrategy = require('passport-http').BasicStrategy;
// Mock database/storage for demonstration
const db = {
clients: [{ id: 'client1', secret: 'secret1', redirectUri: 'http://localhost:3000/auth/example/callback' }],
users: [{ id: 'user1', username: 'testuser', password: 'password' }],
authorizationCodes: [],
accessTokens: []
};
// Mock utility for UID generation
const utils = {
uid: (len) => Math.random().toString(36).substring(2, 2 + len)
};
const app = express();
app.use(express.urlencoded({ extended: true })); // For parsing x-www-form-urlencoded
app.use(express.json()); // For parsing application/json
app.use(require('express-session')({ secret: 'keyboard cat', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());
// Passport setup (simplified for example)
passport.use(new BasicStrategy(function(username, password, done) {
const user = db.users.find(u => u.username === username && u.password === password);
if (!user) { return done(null, false); }
return done(null, user);
}));
passport.serializeUser(function(user, done) { done(null, user.id); });
passport.deserializeUser(function(id, done) {
const user = db.users.find(u => u.id === id);
done(null, user);
});
// Create OAuth 2.0 server
const server = oauth2orize.createServer();
// Register authorization code grant type
server.grant(oauth2orize.grant.code(function(client, redirectURI, user, ares, done) {
const code = utils.uid(16);
db.authorizationCodes.push({ code, clientId: client.id, redirectUri, userId: user.id, scope: ares.scope });
done(null, code);
}));
// Register authorization code exchange type
server.exchange(oauth2orize.exchange.code(function(client, code, redirectURI, done) {
const authCode = db.authorizationCodes.find(ac => ac.code === code && ac.clientId === client.id && ac.redirectUri === redirectURI);
if (!authCode) { return done(null, false); }
// Remove code after use (one-time use)
db.authorizationCodes = db.authorizationCodes.filter(ac => ac.code !== code);
const token = utils.uid(256);
db.accessTokens.push({ token, userId: authCode.userId, clientId: authCode.clientId, scope: authCode.scope });
done(null, token);
}));
// Authorization endpoint
app.get('/dialog/authorize',
passport.authenticate('session'), // Ensure user is logged in via Passport session
server.authorize(function(clientId, redirectURI, done) {
const client = db.clients.find(c => c.id === clientId);
if (!client) { return done(null, false); }
if (client.redirectUri !== redirectURI) { return done(new Error('Invalid redirect URI'), false); }
done(null, client, client.redirectUri);
}),
function(req, res) {
// Render a consent dialog
res.send(`
<h1>Authorize ${req.oauth2.client.id} to access your account?</h1>
<form action="/dialog/authorize/decision" method="POST">
<input type="hidden" name="transaction_id" value="${req.oauth2.transactionID}">
<input type="submit" value="Allow" name="allow">
<input type="submit" value="Deny" name="deny">
</form>
`);
}
);
// Decision endpoint
app.post('/dialog/authorize/decision',
passport.authenticate('session'),
server.decision()
);
// Token endpoint
app.post('/oauth/token',
passport.authenticate(['basic', 'oauth2-client-password'], { session: false }),
server.token(),
server.errorHandler()
);
app.listen(3000, () => console.log('OAuth2orize server listening on port 3000'));