Gathering detailed insights and metrics for @badgateway/oauth2-client
Gathering detailed insights and metrics for @badgateway/oauth2-client
Gathering detailed insights and metrics for @badgateway/oauth2-client
Gathering detailed insights and metrics for @badgateway/oauth2-client
npm install @badgateway/oauth2-client
Typescript
Module System
Min. Node Version
Node Version
NPM Version
TypeScript (95.38%)
JavaScript (3.48%)
Makefile (1.14%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
315 Stars
479 Commits
42 Forks
10 Watchers
4 Branches
19 Contributors
Updated on Jul 06, 2025
Latest Version
3.2.0
Package Id
@badgateway/oauth2-client@3.2.0
Unpacked Size
159.92 kB
Size
38.72 kB
File Count
35
NPM Version
10.8.1
Node Version
20.16.0
Published on
Apr 23, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
This package contains an OAuth2 client. It aims to be a fully-featured OAuth2 utility library, for Node.js, Browsers and written in Typescript.
This OAuth2 client is only 4KB gzipped, it has 0 dependencies and
relies on modern APIs like fetch()
and Web Crypto which are built-in
since Node 18.
authorization_code
grant with optional PKCE support.password
and client_credentials
grant.fetch()
wrapper that automatically adds Bearer tokens and refreshes them.1npm i @badgateway/oauth2-client
To get started, set up the Client class.
1import { OAuth2Client } from '@badgateway/oauth2-client'; 2 3const client = new OAuth2Client({ 4 5 // The base URI of your OAuth2 server 6 server: 'https://my-auth-server/', 7 8 // OAuth2 client id 9 clientId: '...', 10 11 // OAuth2 client secret. Only required for 'client_credentials', 'password' 12 // flows. Don't specify this in insecure contexts, such as a browser using 13 // the authorization_code flow. 14 clientSecret: '...', 15 16 17 // The following URIs are all optional. If they are not specified, we will 18 // attempt to discover them using the oauth2 discovery document. 19 // If your server doesn't have support this, you may need to specify these. 20 // you may use relative URIs for any of these. 21 22 23 // Token endpoint. Most flows need this. 24 // If not specified we'll use the information for the discovery document 25 // first, and otherwise default to /token 26 tokenEndpoint: '/token', 27 28 // Authorization endpoint. 29 // 30 // You only need this to generate URLs for authorization_code flows. 31 // If not specified we'll use the information for the discovery document 32 // first, and otherwise default to /authorize 33 authorizationEndpoint: '/authorize', 34 35 // OAuth2 Metadata discovery endpoint. 36 // 37 // This document is used to determine various server features. 38 // If not specified, we assume it's on /.well-known/oauth2-authorization-server 39 discoveryEndpoint: '/.well-known/oauth2-authorization-server', 40 41});
Many functions use or return a 'OAuth2Token' type. This type has the following shape:
1export type OAuth2Token = { 2 accessToken: string; 3 refreshToken: string | null; 4 5 /** 6 * When the Access Token expires. 7 * 8 * This is expressed as a unix timestamp in milliseconds. 9 */ 10 expiresAt: number | null; 11 12 13 /** 14 * If the server returned an OpenID Connect ID token, it will be stored here. 15 */ 16 idToken?: string; 17 18};
1const token = await client.clientCredentials();
1const newToken = await client.refreshToken(oldToken);
1const token = await client.password({ 2 username: '..', 3 password: '..', 4});
The authorization_code
flow is the flow for browser-based applications,
and roughly consists of 3 major steps:
code
is exchanged for a access and refresh token.This library provides support for these steps, but there's no requirement to use its functionality as the system is mostly stateless.
1import { OAuth2Client, generateCodeVerifier } from 'client'; 2 3const client = new OAuth2Client({ 4 server: 'https://authserver.example/', 5 clientId: '...', 6 7 // Note, if urls cannot be auto-detected, also specify these: 8 tokenEndpoint: '/token', 9 authorizationEndpoint: '/authorize', 10});
Redirecting the user to the authorization server
1/** 2 * This generates a security code that must be passed to the various steps. 3 * This is used for 'PKCE' which is an advanced security feature. 4 * 5 * It doesn't break servers that don't support it, but it makes servers that 6 * so support it more secure. 7 * 8 * It's optional to pass this, but recommended. 9 */ 10const codeVerifier = await generateCodeVerifier(); 11 12// In a browser this might work as follows: 13document.location = await client.authorizationCode.getAuthorizeUri({ 14 15 // URL in the app that the user should get redirected to after authenticating 16 redirectUri: 'https://my-app.example/', 17 18 // Optional string that can be sent along to the auth server. This value will 19 // be sent along with the redirect back to the app verbatim. 20 state: 'some-string', 21 22 codeVerifier, 23 24 scope: ['scope1', 'scope2'], 25 26});
Handling the redirect back to the app and obtain token
1const oauth2Token = await client.authorizationCode.getTokenFromCodeRedirect( 2 document.location, 3 { 4 /** 5 * The redirect URI is not actually used for any redirects, but MUST be the 6 * same as what you passed earlier to "authorizationCode" 7 */ 8 redirectUri: 'https://my-app.example/', 9 10 /** 11 * This is optional, but if it's passed then it also MUST be the same as 12 * what you passed in the first step. 13 * 14 * If set, it will verify that the server sent the exact same state back. 15 */ 16 state: 'some-string', 17 18 codeVerifier, 19 20 } 21);
When using an OAuth2-protected API, typically you will need to obtain an Access
token, and then add this token to each request using an Authorization: Bearer
header.
Because access tokens have a limited lifetime, and occasionally needs to be refreshed this is a bunch of potential plumbing.
To make this easier, this library has a 'fetch wrapper'. This is effectively just like a regular fetch function, except it automatically adds the header and will automatically refresh tokens when needed.
Usage:
1import { OAuth2Client, OAuth2Fetch } from '@badgateway/oauth2-client'; 2 3const client = new OAuth2Client({ 4 server: 'https://my-auth-server', 5 clientId: 'my-client-id' 6}); 7 8 9const fetchWrapper = new OAuth2Fetch({ 10 client: client, 11 12 /** 13 * You are responsible for implementing this function. 14 * it's purpose is to supply the 'initial' oauth2 token. 15 */ 16 getNewToken: async () => { 17 18 // Example 19 return client.clientCredentials(); 20 21 // Another example 22 return client.authorizationCode.getToken({ 23 code: '..', 24 redirectUri: '..', 25 }); 26 27 // You can return null to fail the process. You may want to do this 28 // when a user needs to be redirected back to the authorization_code 29 // endpoints. 30 return null; 31 32 }, 33 34 /** 35 * Optional. This will be called for any fatal authentication errors. 36 */ 37 onError: (err) => { 38 // err is of type Error 39 } 40 41});
After set up, you can just call fetch
on the new object to call your API, and
the library will ensure there's always a Bearer
header.
1const response = fetchWrapper.fetch('https://my-api', { 2 method: 'POST', 3 body: 'Hello world' 4});
To keep a user logged in between sessions, you may want to avoid full reauthentication. To do this, you'll need to store authentication token somewhere.
The fetch wrapper has 2 functions to help with this:
1 2const fetchWrapper = new OAuth2Fetch({ 3 client: client, 4 5 getNewToken: async () => { 6 7 // See above! 8 9 }, 10 11 /** 12 * This function is called whenever the active token changes. Using this is 13 * optional, but it may be used to (for example) put the token in off-line 14 * storage for later usage. 15 */ 16 storeToken: (token) => { 17 document.localStorage.setItem('token-store', JSON.stringify(token)); 18 }, 19 20 /** 21 * Also an optional feature. Implement this if you want the wrapper to try a 22 * stored token before attempting a full re-authentication. 23 * 24 * This function may be async. Return null if there was no token. 25 */ 26 getStoredToken: () => { 27 const token = document.localStorage.getItem('token-store'); 28 if (token) return JSON.parse(token); 29 return null; 30 } 31 32});
It might be preferable to use this library as a more traditional 'middleware'.
The OAuth2Fetch object also exposes a mw
function that returns a middleware
for fetch.
1const mw = oauth2.mw(); 2const response = mw( 3 myRequest, 4 req => fetch(req) 5);
This syntax looks a bit wild if you're not used to building middlewares, but this effectively allows you to 'decorate' existing request libraries with functionality from this oauth2 library.
A real example using the Ketting library:
1import { Client } from 'ketting'; 2import { OAuth2Client, OAuth2Fetch } from '@badgateway/oauth2-client'; 3 4/** 5 * Create the oauth2 client 6 */ 7const oauth2Client = new OAuth2Client({ 8 server: 'https://my-auth.example', 9 clientId: 'foo', 10}); 11 12/** 13 * Create the 'fetch helper' 14 */ 15const oauth2Fetch = new OAuth2Fetch({ 16 client: oauth2Client, 17}); 18 19/** 20 * Add the middleware to Ketting 21 */ 22const ketting = new Client('http://api-root'); 23ketting.use(oauth2Fetch.mw());
Introspection (RFC7662) lets you find more information about a token, such as whether it's valid, which user it belongs to, which oauth2 client was used to generate it, etc.
To be able to use it, your authorization server must have support for the introspection endpoint. It's location will be automatically detected using the Metadata discovery document.
1import { OAuth2Client } from '@badgateway/oauth2-client'; 2 3const client = new Client({ 4 server: 'https://auth-server.example/', 5 6 clientId: '...', 7 8 /** 9 * Some servers require OAuth2 clientId/clientSecret to be passed. 10 * If they require it, specify it. If not it's fine to omit. 11 */ 12 clientSecret: '...', 13 14}); 15 16// Get a token 17const token = client.clientCredentials(); 18 19// Introspect! 20console.log(client.introspect(token));
client_id
and client_secret
encodingOAuth2 allows users to encode the client_id
and client_secret
either in a
Authorization: Basic
header or in the POST
request body.
Real-world OAuth2 servers may support one or the other, or both. The OAuth2 spec requires that servers support the Authorization header, and don't recommend using the body.
By default, this library will use the Authorization
header. OAuth2 also
requires that clients percent-encode the client_id
and client_secret
, but
in practice many popular servers break if you do this. By default this library
will not percent encode any characters except the :
character.
You can change this behavior using the authenticatioMethod
flag:
1const client = new OAuth2Client({ 2 server: 'https://auth-server.example/', 3 clientId: '...', 4 clientSecret: '...', 5 authenticationMethod: 'client_secret_post', // encode in POST body 6});
The following 3 values are currently supported:
client_secret_post
- Encode in POST bodyclient_secret_basic
- Encode in Authorization header using the strict
standard rules.client_secret_basic_interop
- Encode in Authorization header using less
strict rules. This is the default and more likely to work with popular
servers (at least some Google and Ebay APIs want this).The current OAuth 2.1 draft switches the recommendation to use
client_secret_post
by default instead. When that document stabilizes and gets
released, this library will also switch to use client_secret_post
by default
in a major release.
If your OAuth2 server supports POST, we recommend you use client_secret_post
as this is more likely to work without a hitch.
If you configured the client using the OAuth2 discovery document, and the
server indicates it prefers client_secret_basic
we will also default to the
strict form.
This package works out of the box with modern browsers and Node 18.
For Node 16 and below, use a 2.x versions of this package and add polyfills. The README.md for the 2.x version of this package contains more information on the exact steps for older Node versions.
No vulnerabilities found.
No security vulnerabilities found.