Registry / auth-security / oauth2-server

oauth2-server

JSON →
library3.1.1jsnpmunverified

oauth2-server is a complete, framework-agnostic, and well-tested module for implementing an OAuth2 Authorization Server in Node.js. It adheres to RFC 6749 (OAuth 2.0 Authorization Framework) and RFC 6750 (Bearer Token Usage), providing the core logic for handling various OAuth 2.0 grant types including `authorization_code`, `client_credentials`, `refresh_token`, and `password` grants, as well as support for custom extension grants and scopes. The library is currently at version 3.1.1 and is under active maintenance, with recent releases focusing on bug fixes and dependency updates after a period of hiatus. It distinguishes itself by offering a robust, compliant foundation that can be integrated with any Node.js HTTP framework (like Express or Koa via official wrappers), supporting promises, Node-style callbacks, and async/await for model interactions. It doesn't dictate a specific storage mechanism, allowing developers to plug in their preferred database (e.g., PostgreSQL, MongoDB, Redis).

npm install oauth2-server
INSTALL
IMPORT
SIG · OAUTH2-SERVER
O
oauth2-server
auth-securityjavascriptv3.1.1
Install
Import
Disk
Pass rate
0/ 6
Env Coverage0 / 6
glibc
1822
musl
1822
Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
node 18226 runs
build_error
glibc
node 18226 runs
build_error
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

OAuth2Server
import OAuth2Server from 'oauth2-server';
const OAuth2Server = require('oauth2-server').OAuth2Server;
The primary class. While CommonJS `require` works, the module is designed for ES modules. If using CommonJS, it's typically imported as a default, not a named export from the root.
Request, Response
import { Request, Response } from 'oauth2-server';
import { OAuth2Request, OAuth2Response } from 'oauth2-server';
These are classes that wrap native Node.js request and response objects, enabling framework-agnostic operations. Ensure you use the exact names `Request` and `Response`.
express-oauth-server (wrapper)
import OAuth2Server from '@oauthjs/express-oauth-server';
import OAuth2Server from 'oauth2-server'; // Directly with Express
For Express.js applications, it's highly recommended to use the official `@oauthjs/express-oauth-server` wrapper for easier integration, rather than manually wiring `oauth2-server` middleware.

This quickstart sets up a basic OAuth2 server using Express.js and `oauth2-server`, demonstrating `password`, `client_credentials`, `refresh_token`, and `authorization_code` grants with an in-memory data model. It includes token and authorization endpoints, and a protected resource. This minimal model is for illustration; a real application requires persistent storage.

import express from 'express'; import OAuth2Server, { Request, Response } from 'oauth2-server'; import bodyParser from 'body-parser'; // A minimalistic in-memory model for demonstration purposes. const model = { // Client storage clients: [{ id: 'client1', secret: 'clientsecret', redirectUris: ['http://localhost:3000/oauth/callback'], grants: ['authorization_code', 'refresh_token', 'password', 'client_credentials'], }], // Token/Code storage tokens: [], authorizationCodes: [], // Required: getClient(clientId, clientSecret, callback) async getClient(clientId, clientSecret) { const client = this.clients.find(c => c.id === clientId); if (!client) return null; if (clientSecret && client.secret !== clientSecret) return null; return client; }, // Required for authorization_code grant async saveAuthorizationCode(code, client, user) { this.authorizationCodes.push({ code: code.authorizationCode, expiresAt: code.expiresAt, redirectUri: code.redirectUri, scope: code.scope, client: client, user: user }); return code; }, async getAuthorizationCode(authorizationCode) { const code = this.authorizationCodes.find(c => c.code === authorizationCode && c.expiresAt > new Date()); if (!code) return null; // In a real app, delete the code after use: this.authorizationCodes = this.authorizationCodes.filter(c => c.code !== authorizationCode); return code; }, async revokeAuthorizationCode(code) { this.authorizationCodes = this.authorizationCodes.filter(c => c.code !== code.authorizationCode); return true; }, // Required for password, client_credentials, refresh_token grants async saveToken(token, client, user) { this.tokens.push({ accessToken: token.accessToken, accessTokenExpiresAt: token.accessTokenExpiresAt, refreshToken: token.refreshToken, refreshTokenExpiresAt: token.refreshTokenExpiresAt, scope: token.scope, client: client, user: user }); return token; }, async getAccessToken(accessToken) { return this.tokens.find(t => t.accessToken === accessToken && t.accessTokenExpiresAt > new Date()); }, async getRefreshToken(refreshToken) { return this.tokens.find(t => t.refreshToken === refreshToken && t.refreshTokenExpiresAt > new Date()); }, async revokeToken(token) { this.tokens = this.tokens.filter(t => t.refreshToken !== token.refreshToken); return true; }, // Required for password grant async getUser(username, password) { // Dummy user for 'password' grant if (username === 'testuser' && password === 'testpass') { return { id: '123', username: 'testuser' }; } return null; }, // Optional: verifyScope(token, scope) async verifyScope(token, scope) { if (!scope) return true; const requestedScopes = scope.split(' '); const authorizedScopes = token.scope.split(' '); return requestedScopes.every(s => authorizedScopes.includes(s)); } }; const app = express(); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); const oauth = new OAuth2Server({ model: model, accessTokenLifetime: 60 * 60, // 1 hour allowBearerTokensInQueryString: true, // Only for testing, not recommended for production. }); // Grant token endpoint (e.g., /oauth/token for password, client_credentials, refresh_token grants) app.post('/oauth/token', async (req, res) => { try { const token = await oauth.token(new Request(req), new Response(res)); res.json(token); } catch (err) { console.error(err); res.status(err.code || 500).json(err); } }); // Authorize endpoint (e.g., /oauth/authorize for authorization_code grant) app.get('/oauth/authorize', async (req, res) => { // In a real application, render an authorization screen here // For this example, we'll auto-approve if 'user' query param is present if (req.query.response_type === 'code' && req.query.client_id && req.query.redirect_uri) { if (req.query.user === 'approved') { const authRequest = new Request(req); const authResponse = new Response(res); try { const code = await oauth.authorize(authRequest, authResponse, { authenticateHandler: { handle: async () => ({ id: '123', username: 'testuser' }) } }); res.redirect(code.redirectUri + '?code=' + code.authorizationCode); } catch (err) { console.error(err); res.status(err.code || 500).json(err); } } else { // Simulate a login/consent page redirect res.send(` <h1>Authorize Client</h1> <p>Client ${req.query.client_id} wants access to your data.</p> <form action="/oauth/authorize" method="GET"> <input type="hidden" name="response_type" value="${req.query.response_type}"> <input type="hidden" name="client_id" value="${req.query.client_id}"> <input type="hidden" name="redirect_uri" value="${req.query.redirect_uri}"> <input type="hidden" name="scope" value="${req.query.scope || ''}"> <button type="submit" name="user" value="approved">Approve</button> </form> `); } } else { res.status(400).json({ error: 'invalid_request', error_description: 'Missing required parameters for authorization.' }); } }); // Protected resource endpoint app.get('/protected', async (req, res) => { try { const token = await oauth.authenticate(new Request(req), new Response(res)); res.json({ message: 'Hello, ' + token.user.username + '!', tokenInfo: token }); } catch (err) { console.error(err); res.status(err.code || 500).json(err); } }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`OAuth2 server listening on port ${PORT}`); console.log('Try POST to /oauth/token with grant_type=password, username=testuser, password=testpass, client_id=client1, client_secret=clientsecret'); console.log('Try GET to /oauth/authorize with ?response_type=code&client_id=client1&redirect_uri=http://localhost:3000/oauth/callback&scope=read'); console.log('Then navigate to /protected with Authorization: Bearer <access_token>'); });
Debug
Known issues
breakingMigrating from `oauth2-server` 2.x to 3.x involves significant breaking changes. Several server options, such as `grants`, `debug`, `clientIdRegex`, `passthroughErrors`, and `continueAfterResponse`, have been removed. The model specification has also changed, requiring updates to your implementation of methods like `getAccessToken` and `generateAccessToken`. The module now primarily leverages Promises for asynchronous operations, though it retains compatibility with Node-style callbacks, ES6 generators, and async/await.
fix
Consult the official 'Migrating from 2.x to 3.x' guide in the `oauth2-server` documentation. Re-evaluate server options and adapt your model's method signatures and return types to align with the v3 specification.
affects: >=3.0.0
gotchaWhile `oauth2-server` provides a robust implementation of OAuth 2.0 (RFC 6749/6750), the newer OAuth 2.1 standard (a consolidation of security best practices) deprecates several older, insecure flows and introduces new requirements. Notably, the Implicit Grant and Resource Owner Password Credentials (ROPC) flows are removed, and Proof Key for Code Exchange (PKCE) is mandatory for all public clients. OAuth 2.1 also strictly prohibits passing bearer tokens in URL query parameters and requires exact matching for redirect URIs.
fix
Design your application to comply with OAuth 2.1 best practices: use Authorization Code Grant with PKCE for all client types, avoid ROPC and Implicit grants, enforce strict redirect URI validation, and transmit tokens only in the `Authorization` header. `oauth2-server` provides the building blocks, but you must configure your implementation to follow these security standards.
affects: >=3.0.0
gotchaImproper validation of `redirect_uri` can lead to critical security vulnerabilities, including authorization code or token leakage, allowing attackers to hijack user accounts. Overly permissive redirect URIs (e.g., using wildcards or prefix matching) or typos can be exploited.
fix
Always enforce strict, exact matching for registered `redirect_uri` values. Ensure all redirect URIs are pre-registered and validated against an allow-list on your authorization server. Never accept arbitrary redirect URIs.
affects: >=3.0.0
gotchaThe `state` parameter in OAuth 2.0 is crucial for Cross-Site Request Forgery (CSRF) protection. If it's missing, weak (predictable), or not properly validated by the client application, it creates an attack vector where an attacker can complete the authorization flow on behalf of a victim.
fix
Generate a unique, cryptographically secure random string for the `state` parameter for each authorization request. Store this state securely (e.g., in a session or cookie) and verify it upon receiving the callback from the authorization server to ensure the response belongs to the original request.
affects: >=3.0.0
gotchaMisconfigurations within the `model` object, which defines how `oauth2-server` interacts with your data store, are a common source of runtime errors and incorrect behavior. Forgetting to implement a required method (e.g., `getClient`, `saveToken`) or returning an invalid data structure can lead to `invalid_request` or `server_error` responses.
fix
Thoroughly review the `oauth2-server` model specification and ensure all required methods for your chosen grant types are implemented correctly, returning the expected data formats. Use console logging and unit tests for your model implementation to catch issues early.
affects: >=3.0.0
Errors
Common errors & fixes
Error: model must implement getAccessToken()
The configured OAuth2Server model object is missing a required method, `getAccessToken`, or it's not correctly defined. This method is fundamental for authenticating bearer tokens.
fix
Ensure your `model` object passed to `new OAuth2Server()` includes an asynchronous function `getAccessToken(accessToken)` that returns a token object if valid, or `null` otherwise. Review the model specification for all required methods based on your enabled grants.
OAuth2Error: invalid_grant (The provided authorization grant is invalid, expired, revoked, or was issued to another client.)
This error often occurs when an authorization code is used more than once, has expired, or was issued to a different client than the one attempting to exchange it. It can also happen if the refresh token is invalid or revoked.
fix
For authorization codes, ensure they are single-use and quickly invalidated after first use. Verify expiry times for all tokens. Double-check that the `client_id` and `client_secret` used for token exchange match the client that initiated the authorization flow. Inspect logs for more specific reasons (e.g., 'code expired').
OAuth2Error: redirect_uri_mismatch (The redirection URI provided does not match a pre-registered redirection URI for the application.)
The `redirect_uri` sent by the client in the authorization request does not exactly match one of the `redirectUris` configured for that client in your OAuth2 server's model. Even subtle differences, like a trailing slash or case, can cause this.
fix
Ensure the `redirect_uri` parameter in the client's authorization request is an exact string match for one of the `redirectUris` associated with the `client_id` in your OAuth2 model. All valid `redirectUris` should be explicitly listed and strictly validated.
ERR_REQUIRE_ESM: require() of ES Module ... not supported.
You are attempting to use the CommonJS `require()` syntax to import `oauth2-server` (or its wrappers) in a Node.js project configured for ES Modules (`"type": "module"` in `package.json`), or vice versa, in a way that Node.js cannot reconcile.
fix
If your project is ES Module-based, use `import OAuth2Server from 'oauth2-server';`. If your project is CommonJS, use `const OAuth2Server = require('oauth2-server');`. Ensure your `package.json`'s `type` field and module syntax are consistent. Consider using a bundler like Webpack or Rollup for complex module environments.
Upgrade
Version history
3.1.1latest on npm
Audit
Dependencies

No dependency data recorded yet.

Agent activity
9 hits · last 30 days
node
8
OpenAI (training)
1
Resources
oauth2-server — npm install oauth2-server · libregistry