Gathering detailed insights and metrics for oauth-fetch
Gathering detailed insights and metrics for oauth-fetch
Gathering detailed insights and metrics for oauth-fetch
Gathering detailed insights and metrics for oauth-fetch
@scenid/redux-oauth-fetch-flow
Make fetch requests via redux action and oauth
oauth-fetch-json
This module simplifies the logic for making oauth authenticated requests for JSON. This is compatible with passport oauth strategies. If no oauth session is available, a standard fetch will be used.
@fetch-middlewares/oauth
An OAuth 2.0 client middleware for Fetch
fetch-plus-oauth
A convenient Fetch API library with first-class middleware support
oauth-fetch is a lightweight, flexible HTTP client built on top of the native fetch API to simplify making requests to both public and OAuth-protected resources (Bearer and DPoP).
npm install oauth-fetch
Typescript
Module System
Node Version
NPM Version
TypeScript (99.59%)
JavaScript (0.41%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
1 Stars
141 Commits
1 Watchers
1 Branches
1 Contributors
Updated on Jul 01, 2025
Latest Version
1.0.52
Package Id
oauth-fetch@1.0.52
Unpacked Size
195.90 kB
Size
48.54 kB
File Count
8
NPM Version
11.3.0
Node Version
20.18.0
Published on
Jul 01, 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
oauth-fetch is a lightweight HTTP client built on top of the native fetch
API, designed to simplify making requests to both public and OAuth-protected resources (Bearer and DPoP). It features a flexible, identity-agnostic design that allows seamless integration with any OAuth-compliant provider and removes the typical boilerplate code required to handle requests and responses.
Bearer
, DPoP
, and unauthenticated requests out-of-the-box, adapting seamlessly based on the provided token type.application/json
, multipart/form-data
) and parses responses accordingly — no manual parsing required.fetch
API, ensuring minimal bundle size and maximum compatibility.1npm install oauth-fetch
1yarn add oauth-fetch
1<script type="module"> 2 import { OAuthFetch, DPoPUtils, AbstractTokenProvider } from "https://esm.sh/oauth-fetch"; 3</script>
The core of oauth-fetch
's flexibility is the concept of Token Provider. This is an abstract class that defines the contract for managing the token lifecycle. By using a custom token provider, you can integrate with any OAuth-compliant identity provider.
Below there are practical implementations of custom token providers integrating popular identity providers like Auth0, Clerk, and WorkOS.
First, we create a Auth0TokenProvider
to use the @auth0/auth0-spa-js
SDK.
1// auth0-token-provider.ts 2 3import { type Auth0Client, type GetTokenSilentlyOptions } from "@auth0/auth0-spa-js"; 4import { AbstractTokenProvider, type TokenProviderGetTokenResponse } from "oauth-fetch"; 5 6export class Auth0TokenProvider extends AbstractTokenProvider<GetTokenSilentlyOptions> { 7 private auth0: Auth0Client; 8 9 constructor(auth0: Auth0Client, config?: GetTokenSilentlyOptions) { 10 super(config); 11 this.auth0 = auth0; 12 } 13 14 async getToken(): Promise<TokenProviderGetTokenResponse> { 15 try { 16 const accessToken = await this.auth0.getTokenSilently(this.config); 17 18 return { 19 access_token: accessToken, 20 token_type: "Bearer", 21 }; 22 } catch { 23 throw new Error("Failed to retrieve access token."); 24 } 25 } 26}
After creating your token provider, you can initialize the OAuthFetch
client and configure the tokenProvider
with the Auth0 client to manage the token lifecycle. Additionally, you can pass extra configuration to the getToken()
method using withConfigOverrides
for fine-grained control over each request.
1// index.ts 2 3import { OAuthFetch } from 'oauth-fetch'; 4import { Auth0Client } from "@auth0/auth0-spa-js"; 5 6import { Auth0TokenProvider } from './auth0-token-provider'; 7 8const auth0 = new Auth0Client({ 9 domain: "auth0.oauthlabs.com", 10 clientId: "UapVm2tv...", 11 authorizationParams: { 12 redirect_uri: window.location.origin, 13 } 14}); 15 16const tokenProvider = new Auth0TokenProvider(auth0); 17 18const oauthClient = new OAuthFetch({ 19 baseUrl: "https://api.example.com", 20 tokenProvider, 21}); 22 23// Make a GET request 24await oauthClient.get("/me/profile"); 25 26// Make a PATCH request with a body passing `authorizationParams.scope` for just this call 27await oauthClient.patch( 28 "/me/profile", 29 { 30 first_name: "Jacobo", 31 company_name: "Auth0", 32 }, 33 { 34 tokenProvider: tokenProvider.withConfigOverrides({ 35 authorizationParams: { 36 scope: "write:profile", 37 }, 38 }), 39 } 40);
First, we create a ClerkTokenProvider
to use the @clerk/clerk-react
SDK.
1// clerk-token-provider.ts 2 3import { AbstractTokenProvider, type TokenProviderGetTokenResponse } from "oauth-fetch"; 4import { type GetTokenOptions, type UseAuthReturn} from "@clerk/types"; 5 6export class ClerkTokenProvider extends AbstractTokenProvider<GetTokenOptions> { 7 private clerk; 8 9 constructor(clerk: UseAuthReturn, config?: GetTokenSilentlyOptions) { 10 super(config); 11 this.clerk = clerk; 12 } 13 14 async getToken(): Promise<TokenProviderGetTokenResponse> { 15 try { 16 const accessToken = await this.clerk.getToken(this.config); 17 18 if (!accessToken) { 19 throw new Error(); 20 } 21 22 return { 23 access_token: accessToken, 24 token_type: "Bearer", 25 }; 26 } catch { 27 throw new Error('Failed to retrieve access token.'); 28 } 29 } 30}
After creating your token provider, you can initialize the OAuthFetch
client and configure the tokenProvider
with the Clerk client to manage the token lifecycle. Additionally, you can pass extra configuration to the getToken()
method using getTokenConfig
for fine-grained control over each request.
1// index.ts 2 3import { useAuth } from "@clerk/clerk-react"; 4 5import { ClerkTokenProvider } from "./clerk-token-provider"; 6 7const clerk = useAuth(); 8const tokenProvider = new ClerkTokenProvider(clerk); 9 10const oauthClient = new OAuthFetch({ 11 baseUrl: "https://api.example.com", 12 tokenProvider, 13}); 14 15// Make a GET request 16await oauthClient.get("/me/profile"); 17 18// Make a GET request passing `organizationId` to `getToken()` 19await oauthClient.get("/me/profile", { 20 tokenProvider: tokenProvider.withConfigOverrides({ 21 organizationId: "...", 22 }), 23});
First, we create a WorkOSTokenProvider
to use the @workos-inc/authkit-react
SDK.
1// workos-token-provider.ts 2 3import { 4 AbstractTokenProvider, 5 type TokenProviderGetTokenResponse, 6} from "oauth-fetch"; 7import { useAuth } from "@workos-inc/authkit-react"; 8 9export class WorkOSTokenProvider extends AbstractTokenProvider { 10 private workos; 11 12 constructor(workos: typeof useAuth) { 13 super(); 14 this.workos = workos(); 15 } 16 17 async getToken(): Promise<TokenProviderGetTokenResponse> { 18 try { 19 const accessToken = await this.workos.getAccessToken(); 20 21 return { 22 access_token: accessToken, 23 token_type: "Bearer", 24 }; 25 } catch { 26 throw new Error("Failed to retrieve access token."); 27 } 28 } 29}
After creating your token provider, you can initialize the OAuthFetch
client and configure the tokenProvider
with the WorkOS client to manage the token lifecycle.
1// index.ts 2 3import { useAuth } from "@workos-inc/authkit-react"; 4 5import { WorkOSTokenProvider } from "./workos-token-provider"; 6 7// Note we don't call `useAuth()` directly here because it will be invoked within the class constructor to inherit its types. WorkOS does not expose types for `useAuth()`. 8const workos = useAuth; 9const tokenProvider = new WorkOSTokenProvider(workos); 10 11const oauthClient = new OAuthFetch({ 12 baseUrl: "https://api.example.com", 13 tokenProvider, 14}); 15 16// Make a GET request 17await oauthClient.get("/me/profile");
The AbstractTokenProvider
class includes a feature to customize token acquisition on a per-request basis using configuration overrides. This allows you to create a new instance of your token provider with modified getToken()
options without affecting the global instance or other requests.
This is particularly useful when you need to adjust parameters like scopes, audiences, or other provider-specific options dynamically for individual API calls.
1const profileTokenProvider = tokenProvider.withConfigOverrides({ 2 authorizationParams: { 3 scope: "read:profile", 4 audience: "https://api.example.com", 5 }, 6}); 7 8await oauthClient.get("/me/profile", { 9 tokenProvider: profileTokenProvider, 10});
[!TIP] When implementing your token provider, ensure you inject
AbstractTokenProvider<YourGetTokenOptionsType>
to fully benefit from TypeScript's type inference and autocompletion inwithGetTokenConfig()
overrides.Example using
AbstractTokenProvider<GetTokenSilentlyOptions>
with Auth0:
This mode simply performs unauthenticated HTTP requests. isProtected
is set to false
, so no tokens are attached to the request.
1import { OAuthFetch } from 'oauth-fetch'; 2 3const publicClient = new OAuthFetch({ 4 baseUrl: "https://api.example.com", 5 isProtected: false, 6}); 7 8// Make a GET request 9await publicClient.get("/posts/e1c43825-e1a8-416b-b968-f399138050e3");
In this mode, oauth-fetch
retrieves an access token from the provided tokenProvider
and includes it in the Authorization
header of every request:
1import { OAuthFetch } from 'oauth-fetch'; 2import { MyTokenProvider } from './my-token-provider'; 3 4const bearerClient = new OAuthFetch({ 5 baseUrl: "https://api.example.com", 6 tokenProvider: new MyTokenProvider(), 7}); 8 9// Make a GET request 10await bearerClient.get("/me/profile"); 11 12// Make a PATCH request with a body 13await bearerClient.patch('/me/profile', { 14 first_name: 'Jacobo', 15 company_name: 'Auth0' 16});
DPoP (Demonstration of Proof-of-Possession) enhances security by binding the access token to a cryptographic proof. When oauth-fetch
is configured with a dpopKeyPair
, it prepares the client to support DPoP if the tokenProvider
returns a DPoP token type:
1import { OAuthFetch } from 'oauth-fetch'; 2import { MyTokenProvider } from './my-token-provider'; 3 4const dpopKeyPair = await DPoPUtils.generateKeyPair(); 5 6const dpopClient = new OAuthFetch({ 7 baseUrl: "https://api.example.com", 8 tokenProvider: new MyTokenProvider(), 9 dpopKeyPair, 10}); 11 12// Make a GET request 13await dpopClient.get("/me/profile"); 14 15// Make a PATCH request with a body 16await dpopClient.patch('/me/profile', { 17 first_name: 'Jacobo', 18 company_name: 'Auth0' 19});
tokenProvider
returns a token of type DPoP
, oauth-fetch
generates a proof for every request.DPoP-Nonce
header, it will be cached and included in the next request's proof automatically.DPoP-Nonce
in a 401 Unauthorized
response, oauth-fetch
retries the request, generating a new proof with the provided nonce.dpopKeyPair
is provided but the tokenProvider
returns a Bearer
token, oauth-fetch
will not attempt to use DPoP for that request, falling back to Bearer
authentication.When an API responds with a non-successful status code (e.g., 4xx or 5xx), we will throw an instance of ApiResponseError
error. This error is desigend to:
Response
object for full access to headers, status codes, and raw data if needed.In this example, the API returns a 400 Bad Request
response with the following JSON error body:
1{ 2 "error_code": "invalid_property", 3 "error_description": "The company_name property doesn't exist" 4}
This error response is automatically parsed, and you can access the JSON properties directly:
1import { ApiResponseError } from "oauth-fetch"; 2 3try { 4 await dpopClient.patch('/me/profile', { 5 first_name: 'Jacobo', 6 company_name: 'Auth0' 7 }); 8} catch (e) { 9 if (e instanceof ApiResponseError) { 10 // Acess to raw response 11 console.error("Request failed with status:", error.response.status); 12 console.error("Status text:", error.response.statusText); 13 14 // Access to parsed body 15 console.error("Error Code:", error.body.error_code); 16 console.error("Error Description:", error.body.error_description); 17 } else { 18 // Handle unexpected error 19 } 20}
You can override authentication and request settings per request:
1// Include additional headers 2await bearerClient.patch( 3 "/me/profile", 4 { 5 first_name: "Jacobo", 6 company_name: "Auth0", 7 }, 8 { 9 headers: { 10 "Correlation-ID": "bfd3c24e-e755-45c3-af09-e00b55d80dd8", 11 }, 12 } 13); 14 15// Disable authentication for the request 16await bearerClient.get("/posts/e1c43825-e1a8-416b-b968-f399138050e3", { 17 isProtected: false, 18}); 19 20 21// Include additional native fetch options 22await bearerClient.get("/posts/e1c43825-e1a8-416b-b968-f399138050e3", { 23 isProtected: false, 24 mode: 'cors', 25 credentials: 'include', 26}); 27 28 29// Include additional config for the token provider just for this call 30await oauthClient.post( 31 "/me/authentication-methods/enroll", 32 { 33 type: "passkey", 34 }, 35 { 36 tokenProvider: tokenProvider.withConfigOverrides({ 37 authorizationParams: { 38 scope: "write:authentication-methods", 39 }, 40 }), 41 } 42);
By default, we use globalThis.fetch
, but you can provide your own fetch
implementation, whether it's a customized version, a polyfill, or even a wrapper around another HTTP client.
1const client = new OAuthFetch({ 2 baseUrl: 'https://api.example.com', 3 isProtected: false, 4 customFetch: async (url, options) => { 5 // Custom fetch logic 6 } 7});
We provide standalone OAuth utilities that you can use directly in your flows. These utilities, such as DPoP proof generation, are available as separate classes, allowing you to implement OAuth features independently without needing to use OAuthFetch
.
Generate a DPoP key pair and create cryptographic proofs that can be used for both binding tokens and securely consuming protected APIs.
1import { DPoPUtils } from 'oauth-fetch'; 2 3// Generate a DPoP key pair 4const keyPair = await DPoPUtils.generateKeyPair(); 5 6// Calculate JWK Thumbprint 7const jwkThumbprint = await DPoPUtils.calculateJwkThumbprint(keyPair.publicKey); 8 9// Generate a DPoP proof for a request 10const accessToken = 'eyJhbGciOiJIUzI1NiIsInR5...'; 11 12const proof = await DPoPUtils.generateProof({ 13 url: new URL('https://api.example.com/me/profile'), 14 method: 'GET', 15 dpopKeyPair: keyPair, 16 accessToken, 17}); 18 19// Include the DPoP proof in the request headers 20const response = await fetch('https://api.example.com/me/profile', { 21 method: 'GET', 22 headers: { 23 'Authorization': `DPoP ${accessToken}`, 24 'DPoP': proof, 25 }, 26});
[!NOTE] When using
OAuthFetch
, and if thegetToken()
method returns aDPoP
token type, we will automatically handle the generation of the DPoP proof and its inclusion in the headers.
No vulnerabilities found.
No security vulnerabilities found.