@kinde-oss/kinde-auth-pkce-js
Version:
Kinde PKCE authentication for SPAs
2 lines (1 loc) • 9.92 kB
JavaScript
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).createKindeClient=t()}(this,(function(){"use strict";const e="4.3.0",t="pkce-code-verifier";var r,o;!function(e){e.s="string",e.i="integer",e.b="boolean"}(r||(r={})),function(e){e.token_bundle="kinde_token",e.access_token="kinde_access_token",e.id_token="kinde_id_token",e.user="user",e.refresh_token="kinde_refresh_token"}(o||(o={}));class n extends Error{}function a(e){let t=e.replace(/-/g,"+").replace(/_/g,"/");switch(t.length%4){case 0:break;case 2:t+="==";break;case 3:t+="=";break;default:throw new Error("base64 string is not of the correct length")}try{return function(e){return decodeURIComponent(atob(e).replace(/(.)/g,((e,t)=>{let r=t.charCodeAt(0).toString(16).toUpperCase();return r.length<2&&(r="0"+r),"%"+r})))}(t)}catch(e){return atob(t)}}function i(e,t){if("string"!=typeof e)throw new n("Invalid token specified: must be a string");t||(t={});const r=!0===t.header?0:1,o=e.split(".")[r];if("string"!=typeof o)throw new n(`Invalid token specified: missing part #${r+1}`);let i;try{i=a(o)}catch(e){throw new n(`Invalid token specified: invalid base64 for part #${r+1} (${e.message})`)}try{return JSON.parse(i)}catch(e){throw new n(`Invalid token specified: invalid json for part #${r+1} (${e.message})`)}}n.prototype.name="InvalidTokenError";async function s(e){return function(e){const t=Array.from(new Uint8Array(e));return btoa(String.fromCharCode.apply(null,t)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}(await function(e){const t=(new TextEncoder).encode(e);return window.crypto.subtle.digest("SHA-256",t)}(e))}function c(){const e=new Uint32Array(28);return window.crypto.getRandomValues(e),Array.from(e,(e=>("0"+e.toString(16)).substr(-2))).join("")}const d=e=>{const t=Math.floor(Date.now()/1e3);return e.exp-30>t},l=(()=>{let e={};return{reset:()=>{e={}},getItem:t=>e[t],removeItem:t=>{delete e[t]},setItem:(t,r)=>{e[t]=r}}})(),u=(e,t="access_token")=>{const r=l.getItem(`kinde_${t}`);return r?{name:e,value:r[e]}:null},p=(e,t="access_token")=>{const r=u(e,t);return r&&r.value},_=(e,t,o)=>{const n=p("feature_flags"),a=n&&n[e]?n[e]:{};if(null==a.v&&null==t)throw Error(`Flag ${e} was not found, and no default value has been provided`);if(o&&a.t&&o!==a.t)throw Error(`Flag ${e} is of type ${r[a.t]} - requested type ${r[o]}`);return{code:e,type:r[a.t||o],value:null==a.v?t:a.v,is_default:null==a.v}},g=(e,t)=>{try{return _(e,t,"b").value}catch(e){return console.error(e),e}},f=(e,t)=>{try{return _(e,t,"i").value}catch(e){return console.error(e),e}},h=(e,t)=>{try{return _(e,t,"s").value}catch(e){return console.error(e),e}},m=()=>({orgCodes:p("org_codes","id_token")??[]}),w=["iss","azp"],y=(e,t)=>{if(!e)throw new Error("ID token is required");if(w.forEach((r=>{if(!e.payload[r])throw new Error(`(${r}) claim is required.`);if(e.payload[r]!==t[r])throw new Error(`${r} claim mismatch. Expected: "${t[r]}", Received: "${e.payload[r]}"`)})),"RS256"!==e.header.alg)throw new Error(`Unsupported signature alg. Expected: "RS256", Received: "${e.header.alg}"`);if(t.aud){if(!Array.isArray(e.payload.aud))throw new Error("(aud) claim must be an array");if(!t.aud.split(" ").every((t=>e.payload.aud.includes(t))))throw new Error(`(aud) claim mismatch. Expected: "${t.aud}", Received: "${e.payload.aud.join(", ")}"`)}if(!d(e.payload))throw new Error("Token expired");return!0};return async r=>{if(!r)throw Error("Please provide your Kinde credentials");if(r!==Object(r))throw Error("The Kinde SDK must be initiated with an object");const{audience:n,client_id:a,domain:w,is_dangerously_use_local_storage:k=!1,redirect_uri:v,logout_uri:S=v,on_redirect_callback:$,on_error_callback:b,scope:I="openid profile email offline",proxy_redirect_uri:E,_framework:U,_frameworkVersion:T}=r;if(n&&"string"!=typeof n)throw Error("Please supply a valid audience for your api");if(I&&"string"!=typeof I)throw Error("Please supply a valid scope");if(!v||"string"!=typeof r.redirect_uri)throw Error("Please supply a valid redirect_uri for your users to be redirected after successful authentication");if(!w||"string"!=typeof w)throw Error("Please supply a valid Kinde domain so we can connect to your account");if("boolean"!=typeof k)throw TypeError("Please supply a boolean value for is_dangerously_use_local_storage");const P=a||"spa@live",R="localhost"===location.hostname||"127.0.0.1"===location.hostname,O=!R&&!k&&"kinde.com"!==new URL(w).hostname.split(".").slice(-2).join(".");const C=R||k,x={audience:n,client_id:P,redirect_uri:v,authorization_endpoint:`${w}/oauth2/auth`,token_endpoint:`${w}/oauth2/token`,requested_scopes:I,domain:w,_framework:U,_frameworkVersion:T},j=e=>{if(!e||e.error)return;const t=i(e.id_token),r=i(e.id_token,{header:!0}),s=i(e.access_token),c=i(e.access_token,{header:!0}),d={iss:w,azp:a,aud:n};y({payload:t,header:r},{...d,aud:a}),y({payload:s,header:c},d),l.setItem(o.token_bundle,e),l.setItem(o.access_token,s),l.setItem(o.id_token,t),t.sub&&l.setItem(o.user,{id:t.sub,given_name:t.given_name,family_name:t.family_name,email:t.email,picture:t.picture}),C?localStorage.setItem(o.refresh_token,e.refresh_token):l.setItem(o.refresh_token,e.refresh_token)},A=async({tokenType:t}={tokenType:o.access_token})=>{const r=C?localStorage.getItem(o.refresh_token):l.getItem(o.refresh_token);var n;if(r||O&&(n="_kbrte",document.cookie.split("; ").find((e=>e.split("=")[0]===n))?.split("=")[1]))try{const n=await fetch(x.token_endpoint,{method:"POST",...O&&{credentials:"include"},headers:new Headers({"Content-type":"application/x-www-form-urlencoded; charset=UTF-8","Kinde-SDK":`\n ${x._framework||"JavaScript"}/${x._frameworkVersion||e}`}),body:new URLSearchParams({client_id:x.client_id,grant_type:"refresh_token",...!O&&r&&{refresh_token:r}})}),a=await n.json();return j(a),t===o.id_token?a.id_token:a.access_token}catch(e){console.error(e)}},L=async(e,t)=>{const r=l.getItem(o.token_bundle);if(!r||t.isForceRefresh)return await A({tokenType:e});const n=l.getItem(e);return d(n)?e===o.access_token?r.access_token:r.id_token:await A({tokenType:e})},F=async(e={})=>await L(o.access_token,e),K=()=>{const e=new URL(window.location.toString());e.search="",window.history.pushState({},"",e)},z=async e=>{const{app_state:r={},prompt:o,is_create_org:a,org_name:i="",org_code:d,authUrlParams:l={}}=e;r.kindeOriginUrl||(r.kindeOriginUrl=window.location.href);const{state:u,code_challenge:p,url:_}=await(async(e,r)=>{const o=c(),n=c(),a=await s(n);return sessionStorage.setItem(`${t}-${o}`,JSON.stringify({codeVerifier:n,appState:r})),{state:o,code_challenge:a,url:new URL(e)}})(x.authorization_endpoint,r),g={redirect_uri:v,client_id:P,response_type:"code",scope:x.requested_scopes,code_challenge:p,code_challenge_method:"S256",state:u,supports_reauth:"true"};o&&(g.prompt=o),d&&(g.org_code=d),a&&(g.is_create_org=String(a),g.org_name=i);const f=new URLSearchParams(Object.assign(l,g));n&&n.trim().split(/\s+/).forEach((e=>{f.append("audience",e)})),_.search=String(f),window.location.href=_.toString()},J=async e=>{await z({...e})},V=()=>l.getItem(o.user),D=e=>{const t=e.has("code"),r=e.has("error");if(!t&&!r)return!1;const{protocol:o,host:n,pathname:a}=window.location,i=E||`${o}//${n}${a}`;return i===v||i===`${v}/`};return await(async()=>{const r=new URLSearchParams(window.location.search);D(r)?await(async r=>{const o=r.get("code"),n=r.get("state"),a=r.get("error");if("login_link_expired"===a?.toLowerCase()){const e=r.get("reauth_state");if(e){const t=atob(e);try{const e=JSON.parse(t);e&&J(e)}catch(e){throw new Error(e instanceof Error?e.message:"Unknown Error parsing reauth state")}}return}const i=sessionStorage.getItem(`${t}-${n}`);if(i){if(a){const e=r.get("error"),o=r.get("error_description");K(),sessionStorage.removeItem(`${t}-${n}`);const{appState:a}=JSON.parse(i);return b?b({error:e,errorDescription:o,state:n,appState:a}):window.location.href=a.kindeOriginUrl,!1}const{appState:s,codeVerifier:c}=JSON.parse(i);try{const r=await fetch(x.token_endpoint,{method:"POST",...O&&{credentials:"include"},headers:new Headers({"Content-type":"application/x-www-form-urlencoded; charset=UTF-8","Kinde-SDK":`${x._framework||"JavaScript"}/${x._frameworkVersion||e}`}),body:new URLSearchParams({client_id:x.client_id,code:o,code_verifier:c,grant_type:"authorization_code",redirect_uri:x.redirect_uri})}),a=await r.json();j(a),K(),sessionStorage.removeItem(`${t}-${n}`);const i=V();$&&$(i,s)}catch(e){console.error(e),sessionStorage.removeItem(`${t}-${n}`)}}else console.error("Invalid state")})(r):(O||C)&&await A()})(),{getToken:F,getIdToken:async(e={})=>await L(o.id_token,e),getUser:V,getUserProfile:async()=>{const e={Accept:"application/json",Authorization:`Bearer ${await F()}`};try{const t=await fetch(`${x.domain}/oauth2/v2/user_profile`,{method:"GET",headers:e}),r=await t.json();return l.setItem(o.user,{id:r.sub,given_name:r.given_name,family_name:r.family_name,email:r.email,picture:r.picture}),l.getItem(o.user)}catch(e){console.error(e)}},login:J,logout:async()=>{const e=new URL(`${x.domain}/logout`);try{l.reset(),C&&localStorage.removeItem(o.refresh_token);const t=new URLSearchParams({redirect:S});e.search=String(t),window.location.href=e.toString()}catch(e){console.error(e)}},register:async e=>{await z({...e,prompt:"create"})},isAuthenticated:async()=>{const e=l.getItem(o.access_token);if(!e)return!1;return d(e)||await A(),!0},createOrg:async e=>{await z({...e,prompt:"create",is_create_org:!0})},getClaim:u,getFlag:_,getBooleanFlag:g,getStringFlag:h,getIntegerFlag:f,getPermissions:()=>{const e=p("org_code");return{permissions:p("permissions")??[],orgCode:e}},getPermission:e=>{const t=p("org_code");return{isGranted:(p("permissions")??[]).some((t=>t===e)),orgCode:t}},getOrganization:()=>({orgCode:p("org_code")}),getUserOrganizations:m}}}));