UNPKG

oauth-fetch

Version:

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).

3 lines 9.81 kB
var n={TOKEN_PROVIDER:{REQUIRED:"tokenProvider is required for protected resources",MISSING_ACCESS_TOKEN:"Token provider didn't return an access_token",MISSING_TOKEN_TYPE:"Token provider didn't return a token_type",UNSUPPORTED_TOKEN_TYPE:"Token provider returned an unsupported token type. Supported types are: DPoP, Bearer"},RESPONSE:{BODY_PARSING_ERROR:"Failed to parse the response body",NON_SUCCESSFUL:(r,e,t)=>`[${e}] request to [${r.href}] returned ${t.status} status code (${t.statusText})`},DPOP:{REQUIRED:"dpopKeyPair is required for protected resources with DPoP token type",INVALID_INSTANCE:"dpopKeyPair must contain valid CryptoKey instances for both public and private keys",PRIVATE_KEY_NON_EXPORTABLE:"dpopKeyPair.privateKey should not be exportable for security reasons",PRIVATE_KEY_SIGN_USAGE:"dpopKeyPair.privateKey must include 'sign' usage permission"},CRYPTO:{UNSUPPORTED_PUBLIC_KEY_TYPE:"Unsupported public key type. Supported public key types are: RSA, EC, OKP",UNSUPPORTED_ALGORITHM:"Unsupported algorithm",UNSUPPORTED_ALGORITHM_CONFIGURATION:"Unsupported algorithm or curve/modulus combination",UNSUPPORTED_RSA_HASH_ALGORITHM:"Unsupported RSA hash algorithm. Supported algorithms are: SHA-256, SHA-384, SHA-512",INVALID_RSA_MODULUS_LENGTH:"RSA key modulus length must be at least 2048 bits",UNSUPPORTED_ECDSA_CURVE:"Unsupported ECDSA curve. Supported curves are: P-256, P-384, P-521",UNSUPPORTED_RSA_PSS_HASH_ALGORITHM:"Unsupported RSA-PSS hash algorithm. Supported algorithms are: SHA-256, SHA-384, SHA-512"}},E=class extends Error{constructor(e){super(e),this.name="BaseError";}},s=class extends E{constructor(e){super(e),this.name="ConfigurationError";}},h=class extends E{constructor(e){super(e),this.name="TokenProviderError";}},f=class extends E{response;status;statusText;body;constructor(e,t,o){super(e),this.name="ApiResponseError",this.status=t.status,this.statusText=t.statusText,this.response=t,this.body=o;}};function l(r){return btoa(String.fromCharCode(...r)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function H(r=32){return l(crypto.getRandomValues(new Uint8Array(r)))}async function C(r){let{kty:e,e:t,n:o,x:a,y:c,crv:p}=await crypto.subtle.exportKey("jwk",r);return {kty:e,e:t,n:o,x:a,y:c,crv:p}}async function w(r){let t=new TextEncoder().encode(r),o=await crypto.subtle.digest("SHA-256",t);return l(new Uint8Array(o))}function B(r){switch(r.algorithm.hash.name){case "SHA-256":return "RS256";case "SHA-384":return "RS384";case "SHA-512":return "RS512";default:throw new s(n.CRYPTO.UNSUPPORTED_RSA_HASH_ALGORITHM)}}function j(r){switch(r.algorithm.hash.name){case "SHA-256":return "PS256";case "SHA-384":return "PS384";case "SHA-512":return "PS512";default:throw new s(n.CRYPTO.UNSUPPORTED_RSA_PSS_HASH_ALGORITHM)}}function V(r){switch(r.algorithm.namedCurve){case "P-256":return "ES256";case "P-384":return "ES384";case "P-521":return "ES512";default:throw new s(n.CRYPTO.UNSUPPORTED_ECDSA_CURVE)}}function K(r){switch(r.algorithm.name){case "RSA-PSS":return j(r);case "RSASSA-PKCS1-v1_5":return B(r);case "ECDSA":return V(r);case "Ed25519":case "EdDSA":return "Ed25519";default:throw new s(n.CRYPTO.UNSUPPORTED_ALGORITHM)}}function J(r){switch(r.algorithm.namedCurve){case "P-256":return "SHA-256";case "P-384":return "SHA-384";case "P-521":return "SHA-512";default:throw new s(n.CRYPTO.UNSUPPORTED_ECDSA_CURVE)}}function k(r){let e=r.algorithm;if(typeof e.modulusLength!="number"||e.modulusLength<2048)throw new s(n.CRYPTO.INVALID_RSA_MODULUS_LENGTH)}function $(r){switch(r.algorithm.name){case "ECDSA":return {name:r.algorithm.name,hash:J(r)};case "RSA-PSS":{k(r);let e=r.algorithm.hash.name;switch(e){case "SHA-256":case "SHA-384":case "SHA-512":{let t=parseInt(e.slice(-3),10)>>3;return {name:r.algorithm.name,saltLength:t}}default:throw new s(n.CRYPTO.UNSUPPORTED_RSA_PSS_HASH_ALGORITHM)}}case "RSASSA-PKCS1-v1_5":return k(r),r.algorithm.name;case "Ed25519":return r.algorithm.name;default:throw new s(n.CRYPTO.UNSUPPORTED_ALGORITHM)}}async function x(r,e,t){let o=S=>l(new TextEncoder().encode(JSON.stringify(S))),a=o(r),c=o(e),p=`${a}.${c}`,u=await crypto.subtle.sign($(t),t,new TextEncoder().encode(p)),d=l(new Uint8Array(u));return `${p}.${d}`}var i={JSON:"json",TEXT:"text",FORM_DATA:"formData",FORM_URL_ENCODED:"formUrlEncoded"},R={GET:"GET",POST:"POST",PATCH:"PATCH",PUT:"PUT",DELETE:"DELETE"},O={BEARER:"Bearer",DPOP:"DPoP"},I={ECDSA:["P-256","P-384","P-521"],"RSA-PSS":["2048","3072","4096"],EdDSA:["Ed25519"]};function g(r){if(!r)throw new s(n.DPOP.REQUIRED);let{publicKey:e,privateKey:t}=r;if(!(e instanceof CryptoKey)||!(t instanceof CryptoKey))throw new s(n.DPOP.INVALID_INSTANCE);if(t.extractable)throw new s(n.DPOP.PRIVATE_KEY_NON_EXPORTABLE);if(!t.usages.includes("sign"))throw new s(n.DPOP.PRIVATE_KEY_SIGN_USAGE)}function b({algorithm:r,curveOrModulus:e}){let t=I[r];if(!t)throw new s(n.CRYPTO.UNSUPPORTED_ALGORITHM);if(!t.includes(e))throw new s(n.CRYPTO.UNSUPPORTED_ALGORITHM_CONFIGURATION)}var y=class{constructor(){}static#t(e,t){switch(e){case "ECDSA":return {name:e,namedCurve:t};case "RSA-PSS":return {name:e,modulusLength:parseInt(t,10),publicExponent:new Uint8Array([1,0,1]),hash:"SHA-256"};case "EdDSA":return {name:"Ed25519"};default:throw new s(n.CRYPTO.UNSUPPORTED_ALGORITHM_CONFIGURATION)}}static async calculateJwkThumbprint(e){let t=await C(e),o={};switch(t.kty){case "RSA":o.e=t.e,o.kty=t.kty,o.n=t.n;break;case "EC":o.crv=t.crv,o.kty=t.kty,o.x=t.x,o.y=t.y;break;case "OKP":o.crv=t.crv,o.kty=t.kty,o.x=t.x;break;default:throw new s(n.CRYPTO.UNSUPPORTED_PUBLIC_KEY_TYPE)}let a=JSON.stringify(o);return w(a)}static async generateKeyPair({algorithm:e="ECDSA",curveOrModulus:t="P-256"}={}){b({algorithm:e,curveOrModulus:t});let o=await crypto.subtle.generateKey(this.#t(e,t),false,["sign","verify"]);return {publicKey:o.publicKey,privateKey:o.privateKey}}static async generateProof({url:e,method:t,dpopKeyPair:o,nonce:a,accessToken:c}){g(o);let p={alg:K(o.publicKey),typ:"dpop+jwt",jwk:await C(o.publicKey)},u={iat:Math.floor(Date.now()/1e3),jti:H(),htm:t,htu:e.origin+e.pathname,...a&&{nonce:a},...c&&{ath:await w(c)}};return x(p,u,o.privateKey)}};var T={[i.JSON]:"application/json",[i.TEXT]:"text/plain",[i.FORM_DATA]:"multipart/form-data",[i.FORM_URL_ENCODED]:"application/x-www-form-urlencoded"};async function D(r){let e=r.headers.get("Content-Type");return e?e.includes(T[i.JSON])?await r.json():e.includes(T[i.TEXT])?await r.text():e.includes(T[i.FORM_DATA])?await r.formData():e.includes(T[i.FORM_URL_ENCODED])?await r.text():(console.warn(`Unsupported Content-Type: ${e}. Returning as text.`),await r.text()):r.status===204?null:(console.warn("No Content-Type header. Returning response as text."),await r.text())}function G(r,e){if(e)switch(r){case i.JSON:return JSON.stringify(e);case i.TEXT:return String(e);case i.FORM_DATA:{let t=new FormData;return Object.entries(e).forEach(([o,a])=>{t.append(o,a);}),t}case i.FORM_URL_ENCODED:{let t=new URLSearchParams;return Object.entries(e).forEach(([o,a])=>{t.append(o,a);}),t.toString()}default:return console.warn(`Unsupported Content-Type: ${r}. Using text format.`),String(e)}}var m=class{config;constructor(e){this.config=e;}withConfigOverrides(e){let t=this.constructor,o={...this.config,...e};return new t(o)}};function L(r){if(!("tokenProvider"in r)||!(r.tokenProvider instanceof m))throw new s(n.TOKEN_PROVIDER.REQUIRED)}function Y(r){if(!(r instanceof m))throw new s(n.TOKEN_PROVIDER.REQUIRED)}function M(r){let{token_type:e,access_token:t}=r;if(!t)throw new h(n.TOKEN_PROVIDER.MISSING_ACCESS_TOKEN);if(!e)throw new h(n.TOKEN_PROVIDER.MISSING_TOKEN_TYPE);if(!Object.values(O).includes(e))throw new h(n.TOKEN_PROVIDER.UNSUPPORTED_TOKEN_TYPE)}var N=class{#t;#r;#o;#s;#a;#i;#n;constructor(e){this.#t=e.baseUrl,this.#r=e.contentType??i.JSON,this.#o=e.isProtected??true,this.#n=e.customFetch??globalThis.fetch.bind(globalThis),this.#o&&(L(e),this.#a=e.tokenProvider,this.#i=e.dpopKeyPair);}async#c(e){let t=new Headers;if(t.set("Content-Type",T[e.contentType]),e.isProtected??this.#o){let a=e.tokenProvider??this.#a;Y(a);let c=await a.getToken();M(c);let{token_type:p,access_token:u}=c;t.set("Authorization",`${p} ${u}`),p.toUpperCase()===O.DPOP.toUpperCase()&&(g(e.dpopKeyPair),t.set("DPoP",await y.generateProof({url:e.url,method:e.method,dpopKeyPair:e.dpopKeyPair,nonce:e.nonce,accessToken:u})));}return e.headers&&new Headers(e.headers).forEach((c,p)=>{t.set(p,c);}),t}async#e({endpoint:e,method:t,body:o,headers:a,isProtected:c,tokenProvider:p,...u}){let d=new URL(e,this.#t),S=async _=>({method:t,headers:await this.#c({url:d,method:t,isProtected:c,tokenProvider:p,contentType:this.#r,dpopKeyPair:this.#i,nonce:_??this.#s,headers:a}),body:G(this.#r,o),...u}),U=await S(),P=await this.#n(d,U),A=P.headers.get("dpop-nonce");if(A&&(this.#s=A,!P.ok)){let _=P.headers.get("www-authenticate"),F=await D(P).catch(()=>{});if(_?.includes("use_dpop_nonce")||F?.error==="use_dpop_nonce"){let q=await S(A);P=await this.#n(d,q);}}let v=await D(P).catch(()=>{});if(P.ok)return v;throw new f(n.RESPONSE.NON_SUCCESSFUL(d,U.method,P),P,v)}async get(e,t){return await this.#e({endpoint:e,method:R.GET,...t})}async delete(e,t,o){return await this.#e({endpoint:e,method:R.DELETE,body:t,...o})}async post(e,t,o){return await this.#e({endpoint:e,method:R.POST,body:t,...o})}async patch(e,t,o){return await this.#e({endpoint:e,method:R.PATCH,body:t,...o})}async put(e,t,o){return await this.#e({endpoint:e,method:R.PUT,body:t,...o})}}; export{m as AbstractTokenProvider,f as ApiResponseError,E as BaseError,s as ConfigurationError,I as DPOP_SUPPORTED_ALGORITHMS,y as DPoPUtils,n as ERR_DESCRIPTION,i as HTTP_CONTENT_TYPE,R as HTTP_METHOD,N as OAuthFetch,O as SUPPORTED_TOKEN_TYPES,h as TokenProviderError};//# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map