Low-Level OAuth 2 / OpenID Connect Client API
oauth4webapi is a low-level JavaScript client library for implementing OAuth 2.1, OAuth 2.0 (with Security BCPs), FAPI 2.0, and OpenID Connect protocols. It provides a comprehensive set of routines for authorization server metadata discovery, various grant flows (Authorization Code Flow with PKCE, Refresh Token, Device Authorization, CIBA, Client Credentials), DPoP, Token Introspection/Revocation, PAR, UserInfo requests, and JWT-secured mechanisms. The library emphasizes secure, up-to-date best practices and is designed to run consistently across browser and non-browser JavaScript runtimes, including Node.js, Deno, and Bun. Currently at version 3.8.5, it receives frequent patch and minor releases, indicated by the detailed changelog. A key differentiator is its zero-dependency footprint and OpenID Connect certification, promoting high-assurance security standards without external dependencies.
Common errors
-
TypeError: require is not a function
cause Attempting to use `require('oauth4webapi')` in an ES Module context, or in a Node.js project configured for ESM when the library only exports ESM.fixChange your import statement to `import * as oauth from 'oauth4webapi'`. If your project is CommonJS, consider setting `"type": "module"` in `package.json` or use `import('oauth4webapi')` dynamically. -
ReferenceError: crypto is not defined
cause The runtime environment (e.g., older Node.js versions or specific browser environments) does not provide a global `crypto` object with the Web Cryptography API.fixEnsure you are running in a modern JavaScript runtime (Node.js >= 15, modern browsers, Deno, Bun, Cloudflare Workers). For older Node.js, you might need a polyfill, though it's generally recommended to upgrade. -
TypeError: subtle.generateKeyPair is not a function
cause The `crypto.subtle` object is available, but the specific Web Cryptography API method required (e.g., `generateKeyPair` for DPoP) is not implemented or is not available in the current context (e.g., secure contexts only in browsers).fixVerify that your runtime fully supports the Web Cryptography API methods used by oauth4webapi. In browser contexts, ensure your application is served over HTTPS. If in Node.js, ensure sufficient version. -
Error: DPoP nonce caching caveats with customFetch
cause When providing a custom `fetch` implementation, DPoP nonces might not be correctly handled or cached, leading to replay protection issues or failed requests.fixReview the DPoP nonce handling in your custom `fetch` implementation. Ensure it correctly processes `DPoP-Nonce` headers and updates the nonce accordingly, or consider using the library's default fetch behavior if possible. (Addressed in v3.8.2 with a workaround, but custom fetch still needs care.)
Warnings
- breaking oauth4webapi is an ESM-only package. Projects requiring CommonJS must use dynamic import() or transpile their code to ESM. Direct `require()` statements will result in runtime errors.
- gotcha The library relies heavily on the Web Cryptography API, which may have varying levels of support or specific quirks across different JavaScript runtimes (e.g., Node.js, Deno, Bun, Cloudflare Workers, browsers). While efforts are made for compatibility, edge cases can arise.
- gotcha As a low-level client, oauth4webapi requires developers to manage the entire OAuth/OIDC flow, including PKCE verifiers, nonces, state parameters, and cookie/session management. Errors in handling these can lead to security vulnerabilities.
- deprecated Older, less secure OAuth 2.0 flows or patterns without PKCE are discouraged. While the library supports various profiles, it promotes the latest Security Best Current Practices (BCP) and FAPI 2.0 standards.
- gotcha A recent refactor in v3.8.5 accounts for an upcoming Web Cryptography change, which might subtly affect existing integrations relying on specific Web Crypto behaviors, though it's likely a forward-compatibility fix.
Install
-
npm install oauth4webapi -
yarn add oauth4webapi -
pnpm add oauth4webapi
Imports
- oauth
const oauth = require('oauth4webapi')import * as oauth from 'oauth4webapi'
- discover
import oauth from 'oauth4webapi'; oauth.discover(...)
import { discover } from 'oauth4webapi' - generatePKCE
import { generatePKCE } from 'oauth4webapi'
Quickstart
import { discover, calculatePKCEChallenge, generatePKCE, authorizationUrl, processAuthorizationResponse, exchangeCode, Issuer, Client, TokenEndpointResponse, JWKS } from 'oauth4webapi';
const as = await discover(new URL('https://accounts.google.com/.well-known/openid-configuration'));
const client: Client = {
client_id: process.env.GOOGLE_CLIENT_ID ?? '',
client_secret: process.env.GOOGLE_CLIENT_SECRET ?? '',
token_endpoint_auth_method: 'client_secret_post'
};
const redirect_uri = 'http://localhost:3000/callback';
async function initiateOAuthFlow() {
const pkce = generatePKCE();
const code_challenge = await calculatePKCEChallenge(pkce);
const authUrl = new URL(authorizationUrl(as, client, {
redirect_uri,
scope: 'openid email profile',
response_type: 'code',
code_challenge,
code_challenge_method: 'S256',
nonce: 'some-random-nonce'
}));
console.log('Please visit this URL to authenticate:', authUrl.toString());
// In a real application, you would redirect the user to authUrl.
// For this example, you'd manually visit and get the code from the callback.
}
async function handleCallback(requestUrl: string) {
const currentUrl = new URL(requestUrl);
const params = oauth.validateAuthResponse(as, client, currentUrl);
if (oauth.is= (params instanceof Error)) {
console.error('Authorization response error:', params);
return;
}
const response = await exchangeCode(as, client, params.code, {
redirect_uri,
code_verifier: 'your_pkce_code_verifier_here' // Replace with actual verifier from initiateOAuthFlow
});
if (response instanceof Error) {
console.error('Token exchange error:', response);
return;
}
console.log('Access Token:', response.access_token);
console.log('ID Token:', response.id_token);
// Further validation and usage of tokens would follow
}
// Example usage (simplified for quickstart)
initiateOAuthFlow();
// To test handleCallback, you'd simulate a redirect after authentication
// handleCallback('http://localhost:3000/callback?code=YOUR_CODE_HERE&state=YOUR_STATE_HERE');