diff --git a/CHANGELOG.md b/CHANGELOG.md index 05cfcaf0..1885d72a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] +## [18.0.0] - 2024-01-18 + +## Breaking Changes + +- The default `DateProvider` implementation relies on `localStorage`. If your environment lacks support for `localStorage`, you must provide custom implementations for either the `DateProvider` or `localStorage`. + +### Added + +- Added a `DateProvider`, that both built-in and custom validators can use instead of `Date.now` to get an estimate of the server clock. +- Added the `dateProvider` prop to the configuration that can be used to customize the built-in `DateProvider`. +- Added `getClockSkewInMillis` as an overrideable function that estimates the time difference between the backend and the client. - Added a test to check that relative URLs get intercepted correctly ## [17.0.5] - 2024-01-03 diff --git a/bundle/bundle.js b/bundle/bundle.js index 690421cf..02d9d81d 100644 --- a/bundle/bundle.js +++ b/bundle/bundle.js @@ -1 +1 @@ -var supertokens;(()=>{"use strict";var e={759:function(e,t,n){var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]o?[2,{isValid:!1,reason:{message:"expired",ageInSeconds:n,maxAgeInSeconds:o}}]:t.includes(e)?[2,{isValid:!0}]:[2,{isValid:!1,reason:{message:"wrong value",expectedToInclude:e,actualValue:t}}])}))}))}}},excludes:function(e,o,s){return void 0===o&&(o=t.defaultMaxAgeInSeconds),{id:void 0!==s?s:t.id,refresh:function(e){return t.refresh(e)},shouldRefresh:function(e,n){return void 0===t.getValueFromPayload(e,n)||void 0!==o&&e[t.id].to?[2,{isValid:!1,reason:{message:"expired",ageInSeconds:n,maxAgeInSeconds:o}}]:t.includes(e)?[2,{isValid:!1,reason:{message:"wrong value",expectedToNotInclude:e,actualValue:t}}]:[2,{isValid:!0}])}))}))}}},includesAll:function(e,o,s){return void 0===o&&(o=t.defaultMaxAgeInSeconds),{id:void 0!==s?s:t.id,refresh:function(e){return t.refresh(e)},shouldRefresh:function(e,n){return void 0===t.getValueFromPayload(e,n)||void 0!==o&&e[t.id].to?[2,{isValid:!1,reason:{message:"expired",ageInSeconds:n,maxAgeInSeconds:o}}]:(a=new Set(t),[2,(u=e.every((function(e){return a.has(e)})))?{isValid:u}:{isValid:u,reason:{message:"wrong value",expectedToInclude:e,actualValue:t}}]))}))}))}}},includesAny:function(e,o,s){return void 0===o&&(o=t.defaultMaxAgeInSeconds),{id:void 0!==s?s:t.id,refresh:function(e){return t.refresh(e)},shouldRefresh:function(e,n){return void 0===t.getValueFromPayload(e,n)||void 0!==o&&e[t.id].to?[2,{isValid:!1,reason:{message:"expired",ageInSeconds:n,maxAgeInSeconds:o}}]:(a=new Set(t),[2,(u=e.some((function(e){return a.has(e)})))?{isValid:u}:{isValid:u,reason:{message:"wrong value",expectedToIncludeAtLeastOneOf:e,actualValue:t}}]))}))}))}}},excludesAll:function(e,o,s){return void 0===o&&(o=t.defaultMaxAgeInSeconds),{id:void 0!==s?s:t.id,refresh:function(e){return t.refresh(e)},shouldRefresh:function(e,n){return void 0===t.getValueFromPayload(e,n)||void 0!==o&&e[t.id].to?[2,{isValid:!1,reason:{message:"expired",ageInSeconds:n,maxAgeInSeconds:o}}]:(a=new Set(t),[2,(u=e.every((function(e){return!a.has(e)})))?{isValid:u}:{isValid:u,reason:{message:"wrong value",expectedToNotInclude:e,actualValue:t}}]))}))}))}}}},this.id=e.id,this.refresh=e.refresh,this.defaultMaxAgeInSeconds=e.defaultMaxAgeInSeconds}return e.prototype.getValueFromPayload=function(e,t){return void 0!==e[this.id]?e[this.id].v:void 0},e.prototype.getLastFetchedTime=function(e,t){return void 0!==e[this.id]?e[this.id].t:void 0},e}();t.PrimitiveArrayClaim=o},911:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.PrimitiveClaim=void 0;var n=function(){function e(e){var t=this;this.validators={hasValue:function(e,n,r){return void 0===n&&(n=t.defaultMaxAgeInSeconds),{id:void 0!==r?r:t.id,refresh:function(e){return t.refresh(e)},shouldRefresh:function(e,r){return void 0===t.getValueFromPayload(e,r)||void 0!==n&&e[t.id].tn?{isValid:!1,reason:{message:"expired",ageInSeconds:i,maxAgeInSeconds:n}}:s!==e?{isValid:!1,reason:{message:"wrong value",expectedValue:e,actualValue:s}}:{isValid:!0}}}}},this.id=e.id,this.refresh=e.refresh,this.defaultMaxAgeInSeconds=e.defaultMaxAgeInSeconds}return e.prototype.getValueFromPayload=function(e,t){return void 0!==e[this.id]?e[this.id].v:void 0},e.prototype.getLastFetchedTime=function(e,t){return void 0!==e[this.id]?e[this.id].t:void 0},e}();t.PrimitiveClaim=n},173:function(e,t){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0}),t.STGeneralError=void 0;var o=function(e){function t(t){var n=e.call(this,t)||this;return n.isSuperTokensGeneralError=!0,n}return r(t,e),t.isThisError=function(e){return!0===e.isSuperTokensGeneralError},t}(Error);t.STGeneralError=o},379:function(e,t,n){var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]=300)throw c;return[4,h.config.postAPIHook({action:"REFRESH_SESSION",fetchResponse:c.clone(),requestInit:u.requestInit,url:u.url,userContext:{}})];case 12:return s.sent(),[4,k(!1)];case 13:return"NOT_EXISTS"===s.sent().status?((0,d.logDebugMessage)("onUnauthorisedResponse: local session doesn't exist, so returning session expired"),[2,{result:"SESSION_EXPIRED"}]):(h.config.onHandleEvent({action:"REFRESH_SESSION",userContext:{}}),(0,d.logDebugMessage)("onUnauthorisedResponse: Sending RETRY signal"),[2,{result:"RETRY"}]);case 14:return v=s.sent(),[4,k(!1)];case 15:return"NOT_EXISTS"===s.sent().status?((0,d.logDebugMessage)("onUnauthorisedResponse: local session doesn't exist, so returning session expired"),[2,{result:"SESSION_EXPIRED",error:v}]):((0,d.logDebugMessage)("onUnauthorisedResponse: sending API_ERROR"),[2,{result:"API_ERROR",error:v}]);case 16:return[4,t.releaseLock("REFRESH_TOKEN_USE")];case 17:return s.sent(),(0,d.logDebugMessage)("onUnauthorisedResponse: Released lock"),[4,k(!1)];case 18:return"NOT_EXISTS"!==s.sent().status?[3,21]:((0,d.logDebugMessage)("onUnauthorisedResponse: local session doesn't exist, so removing anti-csrf and sFrontToken"),[4,f.removeToken()]);case 19:return s.sent(),[4,g.removeToken()];case 20:s.sent(),s.label=21;case 21:return[7];case 22:return[4,k(!1)];case 23:return"NOT_EXISTS"===(b=s.sent()).status?((0,d.logDebugMessage)("onUnauthorisedResponse: lock acquired failed and local session doesn't exist, so sending SESSION_EXPIRED"),[2,{result:"SESSION_EXPIRED"}]):b.status!==e.status||"EXISTS"===b.status&&"EXISTS"===e.status&&b.lastAccessTokenUpdate!==e.lastAccessTokenUpdate?((0,d.logDebugMessage)("onUnauthorisedResponse: lock acquired failed and retrying early because pre and post lastAccessTokenUpdate don't match"),[2,{result:"RETRY"}]):[3,2];case 24:return[2]}}))}))}function y(){(0,d.logDebugMessage)("onTokenUpdate: firing ACCESS_TOKEN_PAYLOAD_UPDATED event"),h.config.onHandleEvent({action:"ACCESS_TOKEN_PAYLOAD_UPDATED",userContext:{}})}function w(e){return o(this,void 0,void 0,(function(){var t;return s(this,(function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,h.recipeImpl.getInvalidClaimsFromResponse({response:e,userContext:{}})];case 1:return(t=n.sent())&&h.config.onHandleEvent({action:"API_INVALID_CLAIM",claimValidationErrors:t,userContext:{}}),[3,3];case 2:return n.sent(),[3,3];case 3:return[2]}}))}))}function k(e){return o(this,void 0,void 0,(function(){var t,n,r;return s(this,(function(o){switch(o.label){case 0:return(0,d.logDebugMessage)("getLocalSessionState: called"),[4,D(p)];case 1:return t=o.sent(),[4,g.doesTokenExists()];case 2:return o.sent()&&void 0!==t?((0,d.logDebugMessage)("getLocalSessionState: returning EXISTS since both frontToken and lastAccessTokenUpdate exists"),[2,{status:"EXISTS",lastAccessTokenUpdate:t}]):[3,3];case 3:return t?((0,d.logDebugMessage)("getLocalSessionState: returning NOT_EXISTS since frontToken was cleared but lastAccessTokenUpdate exists"),[2,{status:"NOT_EXISTS"}]):[3,4];case 4:return n={status:"MAY_EXIST"},e?((0,d.logDebugMessage)("getLocalSessionState: trying to refresh"),[4,m(n)]):[3,7];case 5:return"RETRY"!==(r=o.sent()).result?((0,d.logDebugMessage)("getLocalSessionState: return NOT_EXISTS in case error from backend"+r.result),[2,{status:"NOT_EXISTS"}]):((0,d.logDebugMessage)("getLocalSessionState: Retrying post refresh"),[4,k(e)]);case 6:return[2,o.sent()];case 7:return(0,d.logDebugMessage)("getLocalSessionState: returning: "+n.status),[2,n]}}))}))}function S(e){switch(e){case"access":return"st-access-token";case"refresh":return"st-refresh-token"}}function I(e,t){var n=S(e);return""!==t?((0,d.logDebugMessage)("setToken: saved ".concat(e," token into cookies")),T(n,t,Date.now()+31536e5)):((0,d.logDebugMessage)("setToken: cleared ".concat(e," token from cookies")),T(n,t,0))}function T(e,t,n){var r="Fri, 31 Dec 9999 23:59:59 GMT";n!==Number.MAX_SAFE_INTEGER&&(r=new Date(n).toUTCString());var o=h.config.sessionTokenFrontendDomain;return"localhost"===o||o===c.default.getReferenceOrThrow().windowHandler.location.getHostName()?u.default.getReferenceOrThrow().cookieHandler.setCookie("".concat(e,"=").concat(t,";expires=").concat(r,";path=/;samesite=").concat(h.config.isInIframe?"none;secure":"lax")):u.default.getReferenceOrThrow().cookieHandler.setCookie("".concat(e,"=").concat(t,";expires=").concat(r,";domain=").concat(o,";path=/;samesite=").concat(h.config.isInIframe?"none;secure":"lax"))}function R(e){return o(this,void 0,void 0,(function(){return s(this,(function(t){return[2,D(S(e))]}))}))}function D(e){return o(this,void 0,void 0,(function(){var t,n,r,o;return s(this,(function(s){switch(s.label){case 0:return n="; ",[4,u.default.getReferenceOrThrow().cookieHandler.getCookie()];case 1:return t=n+s.sent(),(r=t.split("; "+e+"=")).length>=2&&void 0!==(o=r.pop())?[2,o.split(";").shift()]:[2,void 0]}}))}))}function E(e,t){return void 0===t&&(t=!1),o(this,void 0,void 0,(function(){var n,r;return s(this,(function(o){switch(o.label){case 0:return(0,d.logDebugMessage)("setTokenHeaders: adding existing tokens as header"),[4,R("access")];case 1:return n=o.sent(),[4,R("refresh")];case 2:return r=o.sent(),!t&&void 0===n||void 0===r?(0,d.logDebugMessage)("setAuthorizationHeaderIfRequired: token for header based auth not found"):e.has("Authorization")?(0,d.logDebugMessage)("setAuthorizationHeaderIfRequired: Authorization header defined by the user, not adding"):((0,d.logDebugMessage)("setAuthorizationHeaderIfRequired: added authorization header"),e.set("Authorization","Bearer ".concat(t?r:n))),[2]}}))}))}function x(e){return o(this,void 0,void 0,(function(){var t,n,r,o,i;return s(this,(function(s){switch(s.label){case 0:return(0,d.logDebugMessage)("saveTokensFromHeaders: Saving updated tokens from the response headers"),null===(t=e.headers.get("st-refresh-token"))?[3,2]:((0,d.logDebugMessage)("saveTokensFromHeaders: saving new refresh token"),[4,I("refresh",t)]);case 1:s.sent(),s.label=2;case 2:return null===(n=e.headers.get("st-access-token"))?[3,4]:((0,d.logDebugMessage)("saveTokensFromHeaders: saving new access token"),[4,I("access",n)]);case 3:s.sent(),s.label=4;case 4:return null===(r=e.headers.get("front-token"))?[3,6]:((0,d.logDebugMessage)("saveTokensFromHeaders: Setting sFrontToken: "+r),[4,g.setItem(r)]);case 5:s.sent(),s.label=6;case 6:return null===(o=e.headers.get("anti-csrf"))?[3,9]:[4,k(!0)];case 7:return"EXISTS"!==(i=s.sent()).status?[3,9]:((0,d.logDebugMessage)("saveTokensFromHeaders: Setting anti-csrf token"),[4,f.setItem(i.lastAccessTokenUpdate,o)]);case 8:s.sent(),s.label=9;case 9:return[2]}}))}))}function M(){return o(this,void 0,void 0,(function(){var e;return s(this,(function(t){switch(t.label){case 0:return(0,d.logDebugMessage)("saveLastAccessTokenUpdate: called"),e=Date.now().toString(),(0,d.logDebugMessage)("saveLastAccessTokenUpdate: setting "+e),[4,T(p,e,Number.MAX_SAFE_INTEGER)];case 1:return t.sent(),[4,T("sIRTFrontend","",0)];case 2:return t.sent(),[2]}}))}))}function A(){return o(this,void 0,void 0,(function(){function e(){return o(this,void 0,void 0,(function(){var e,t,n,r,o;return s(this,(function(s){switch(s.label){case 0:return t="; ",[4,u.default.getReferenceOrThrow().cookieHandler.getCookie()];case 1:return e=t+s.sent(),(n=e.split("; sAntiCsrf=")).length>=2&&void 0!==(r=n.pop())?void 0===(o=r.split(";").shift())?[2,null]:[2,o]:[2,null]}}))}))}var t;return s(this,(function(n){switch(n.label){case 0:return(0,d.logDebugMessage)("getAntiCSRFToken: called"),[4,k(!0)];case 1:return"EXISTS"!==n.sent().status?((0,d.logDebugMessage)("getAntiCSRFToken: Returning because local session state != EXISTS"),[2,null]):[4,e()];case 2:return t=n.sent(),(0,d.logDebugMessage)("getAntiCSRFToken: returning: "+t),[2,t]}}))}))}function _(e){return o(this,void 0,void 0,(function(){return s(this,(function(t){switch(t.label){case 0:return(0,d.logDebugMessage)("setAntiCSRF: called: "+e),void 0===e?[3,2]:[4,T(v,e,Number.MAX_SAFE_INTEGER)];case 1:return t.sent(),[3,4];case 2:return[4,T(v,"",0)];case 3:t.sent(),t.label=4;case 4:return[2]}}))}))}function C(){return o(this,void 0,void 0,(function(){var e;return s(this,(function(t){switch(t.label){case 0:return(0,d.logDebugMessage)("getFrontTokenFromCookie: called"),[4,D(b)];case 1:return[2,void 0===(e=t.sent())?null:e]}}))}))}function O(e){return JSON.parse(decodeURIComponent(escape(atob(e))))}function P(){return o(this,void 0,void 0,(function(){var e;return s(this,(function(t){switch(t.label){case 0:return(0,d.logDebugMessage)("getFrontToken: called"),[4,k(!0)];case 1:return"EXISTS"!==t.sent().status?((0,d.logDebugMessage)("getFrontToken: Returning because sIRTFrontend != EXISTS"),[2,null]):[4,C()];case 2:return e=t.sent(),(0,d.logDebugMessage)("getFrontToken: returning: "+e),[2,e]}}))}))}function H(e){return o(this,void 0,void 0,(function(){var t,n,r;return s(this,(function(o){switch(o.label){case 0:return(0,d.logDebugMessage)("setFrontToken: called"),[4,C()];case 1:return null!==(t=o.sent())&&void 0!==e&&(n=O(t).up,r=O(e).up,JSON.stringify(n)!==JSON.stringify(r)&&y()),void 0!==e?[3,3]:[4,T(b,"",0)];case 2:return o.sent(),[3,5];case 3:return[4,T(b,e,Number.MAX_SAFE_INTEGER)];case 4:o.sent(),o.label=5;case 5:return[2]}}))}))}function F(e,t,n){if(null!=n){var r="remove"!==n;(0,d.logDebugMessage)("fireSessionUpdateEventsIfNecessary wasLoggedIn: ".concat(e," frontTokenExistsAfter: ").concat(r," status: ").concat(t)),e?r||(t===h.config.sessionExpiredStatusCode?((0,d.logDebugMessage)("onUnauthorisedResponse: firing UNAUTHORISED event"),h.config.onHandleEvent({action:"UNAUTHORISED",sessionExpiredOrRevoked:!0,userContext:{}})):((0,d.logDebugMessage)("onUnauthorisedResponse: firing SIGN_OUT event"),h.config.onHandleEvent({action:"SIGN_OUT",userContext:{}}))):r&&((0,d.logDebugMessage)("onUnauthorisedResponse: firing SESSION_CREATED event"),h.config.onHandleEvent({action:"SESSION_CREATED",userContext:{}}))}else(0,d.logDebugMessage)("fireSessionUpdateEventsIfNecessary returning early because the front token was not updated")}t.onUnauthorisedResponse=m,t.onTokenUpdate=y,t.onInvalidClaimResponse=w,t.getLocalSessionState=k,t.getStorageNameForToken=S,t.setToken=I,t.getTokenForHeaderAuth=R,t.saveLastAccessTokenUpdate=M,t.setAntiCSRF=_,t.getFrontToken=P,t.setFrontToken=H,t.fireSessionUpdateEventsIfNecessary=F},569:function(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,s){function i(e){try{u(r.next(e))}catch(e){s(e)}}function a(e){try{u(r.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,a)}u((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,s,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]{Object.defineProperty(t,"__esModule",{value:!0}),t.logDebugMessage=t.disableLogging=t.enableLogging=void 0;var r=n(255),o=!1;t.enableLogging=function(){o=!0},t.disableLogging=function(){o=!1},t.logDebugMessage=function(e){o&&console.log("".concat("com.supertokens",' {t: "').concat((new Date).toISOString(),'", message: "').concat(e,'", supertokens-website-ver: "').concat(r.package_version,'"}'))}},992:(e,t)=>{function n(e){return/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.isAnIpAddress=void 0,t.isAnIpAddress=n;function r(e,t){void 0===t&&(t=!1),e=e.trim();try{if(!e.startsWith("http://")&&!e.startsWith("https://"))throw new Error("converting to proper URL");var o=new URL(e);return t?o.hostname.startsWith("localhost")||n(o.hostname)?"http://"+o.host:"https://"+o.host:o.protocol+"//"+o.host}catch(e){}if(e.startsWith("/"))throw new Error("Please provide a valid domain name");if(0===e.indexOf(".")&&(e=e.substr(1)),(-1!==e.indexOf(".")||e.startsWith("localhost"))&&!e.startsWith("http://")&&!e.startsWith("https://")){e="https://"+e;try{return new URL(e),r(e,!0)}catch(e){}}throw new Error("Please provide a valid domain name")}t.default=function(e){var t=this;this.getAsStringDangerous=function(){return t.value},this.value=r(e)}},260:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0});function n(e){e=e.trim();try{if(!e.startsWith("http://")&&!e.startsWith("https://"))throw new Error("converting to proper URL");return"/"===(e=new URL(e).pathname).charAt(e.length-1)?e.substr(0,e.length-1):e}catch(e){}if((function(e){if(-1===e.indexOf(".")||e.startsWith("/"))return!1;try{return-1!==new URL(e).hostname.indexOf(".")}catch(e){}try{return-1!==new URL("http://"+e).hostname.indexOf(".")}catch(e){}return!1}(e)||e.startsWith("localhost"))&&!e.startsWith("http://")&&!e.startsWith("https://"))return n(e="http://"+e);"/"!==e.charAt(0)&&(e="/"+e);try{return new URL("http://example.com"+e),n("http://example.com"+e)}catch(e){throw new Error("Please provide a valid URL path")}}t.default=function e(t){var r=this;this.startsWith=function(e){return r.value.startsWith(e.value)},this.appendPath=function(t){return new e(r.value+t.value)},this.getAsStringDangerous=function(){return r.value},this.value=n(t)}},743:function(e,t){var n,r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,s){function i(e){try{u(r.next(e))}catch(e){s(e)}}function a(e){try{u(r.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,a)}u((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,s,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]=0;n--)if(e.history[n]==t)return e.history[n]},this.reset=function(){e.history=[]},this.waitForEvent=function(t,n){return void 0===n&&(n=7e3),r(e,void 0,void 0,(function(){var e,r=this;return o(this,(function(o){return e=Date.now(),[2,new Promise((function(o){var s=r;!function r(){var i=s.getEventByLastEventByName(t);void 0===i?Date.now()-e>n?o(void 0):setTimeout(r,1e3):o(i)}()}))]}))}))}}return e.getInstance=function(){return null==e.instance&&(e.instance=new e),e.instance},e}();t.ProcessState=s},994:function(e,t,n){var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]=300)throw r;return[4,e.postAPIHook({action:"SIGN_OUT",requestInit:n.requestInit,url:n.url,fetchResponse:r.clone(),userContext:t.userContext})];case 4:return s.sent(),[4,r.clone().json()];case 5:if("GENERAL_ERROR"===(o=s.sent()).status)throw(0,c.logDebugMessage)("doRequest: Throwing general error"),a=void 0===o.message?"No Error Message Provided":o.message,new l.STGeneralError(a);return[2]}}))}))},getInvalidClaimsFromResponse:function(e){return o(this,void 0,void 0,(function(){var t;return s(this,(function(n){switch(n.label){case 0:return"body"in e.response?[4,e.response.clone().json()]:[3,2];case 1:return t=n.sent(),[3,3];case 2:t="string"==typeof e.response.data?JSON.parse(e.response.data):e.response.data,n.label=3;case 3:return[2,t.claimValidationErrors]}}))}))},getGlobalClaimValidators:function(e){return e.claimValidatorsAddedByOtherRecipes},validateClaims:function(e){return o(this,void 0,void 0,(function(){var t,n,r,o,i,a,u,c,l;return s(this,(function(s){switch(s.label){case 0:return[4,this.getAccessTokenPayloadSecurely({userContext:e.userContext})];case 1:t=s.sent(),n=0,r=e.claimValidators,s.label=2;case 2:return n1){var u=n.split(":")[n.split(":").length-1];"string"!=typeof(i=u)||isNaN(i)||isNaN(parseFloat(i))||(a+=":"+u,o=""===r.port?o:o+":"+r.port)}return n.startsWith(".")?("."+o).endsWith(a):o===a}}}},272:function(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,s){function i(e){try{u(r.next(e))}catch(e){s(e)}}function a(e){try{u(r.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,a)}u((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,s,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]{Object.defineProperty(t,"__esModule",{value:!0}),t.CookieHandlerReference=void 0;var r=n(272),o=function(){function e(e){var t=function(e){return e};void 0!==e&&(t=e),this.cookieHandler=t(r.defaultCookieHandlerImplementation)}return e.init=function(t){void 0===e.instance&&(e.instance=new e(t))},e.getReferenceOrThrow=function(){if(void 0===e.instance)throw new Error("SuperTokensCookieHandler must be initialized before calling this method.");return e.instance},e}();t.CookieHandlerReference=o,t.default=o},318:function(e,t,n){var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]{Object.defineProperty(t,"__esModule",{value:!0}),t.LockFactoryReference=void 0;var r=n(895),o=function(){function e(e){this.lockFactory=e}return e.init=function(t,n){void 0===this.instance&&(this.instance=new e(null!=t?t:function(e){return function(){return Promise.resolve(new r.default(e))}}(n)))},e.getReferenceOrThrow=function(){if(void 0===e.instance)throw new Error("SuperTokensLockReference must be initialized before calling this method.");return e.instance},e}();t.LockFactoryReference=o,t.default=o},153:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SessionClaimValidatorStore=void 0;var n=function(){function e(){}return e.claimValidatorsAddedByOtherRecipes=[],e.addClaimValidatorFromOtherRecipe=function(t){e.claimValidatorsAddedByOtherRecipes.push(t)},e.getClaimValidatorsAddedByOtherRecipes=function(){return e.claimValidatorsAddedByOtherRecipes},e}();t.SessionClaimValidatorStore=n,t.default=n},586:function(e,t){var n=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,s){function i(e){try{u(r.next(e))}catch(e){s(e)}}function a(e){try{u(r.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,a)}u((r=r.apply(e,t||[])).next())}))},r=this&&this.__generator||function(e,t){var n,r,o,s,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]{Object.defineProperty(t,"__esModule",{value:!0}),t.WindowHandlerReference=void 0;var r=n(586),o=function(){function e(e){var t=function(e){return e};void 0!==e&&(t=e),this.windowHandler=t(r.defaultWindowHandlerImplementation)}return e.init=function(t){void 0===e.instance&&(e.instance=new e(t))},e.getReferenceOrThrow=function(){if(void 0===e.instance)throw new Error("SuperTokensWindowHandler must be initialized before calling this method.");return e.instance},e}();t.WindowHandlerReference=o,t.default=o},255:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.supported_fdi=t.package_version=void 0,t.package_version="17.0.5",t.supported_fdi=["1.16","1.17","1.18"]},648:function(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,s){function i(e){try{u(r.next(e))}catch(e){s(e)}}function a(e){try{u(r.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,a)}u((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,s,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]0?setTimeout(n,t):n(null)}}window.addEventListener("storage",i),e.addToWaiting(i);var a=setTimeout(i,Math.max(0,t-Date.now()))}))];case 1:return n.sent(),[2]}}))}))},e.addToWaiting=function(t){this.removeFromWaiting(t),void 0!==e.waiters&&e.waiters.push(t)},e.removeFromWaiting=function(t){void 0!==e.waiters&&(e.waiters=e.waiters.filter((function(e){return e!==t})))},e.notifyWaiters=function(){void 0!==e.waiters&&e.waiters.slice().forEach((function(e){return e()}))},e.prototype.releaseLock=function(e){return r(this,void 0,void 0,(function(){return o(this,(function(t){switch(t.label){case 0:return[4,this.releaseLock__private__(e)];case 1:return[2,t.sent()]}}))}))},e.prototype.releaseLock__private__=function(t){return r(this,void 0,void 0,(function(){var n,r,s,c;return o(this,(function(o){switch(o.label){case 0:return n=void 0===this.storageHandler?u:this.storageHandler,r=a+"-"+t,null===(s=n.getItemSync(r))?[2]:(c=JSON.parse(s)).id!==this.id?[3,2]:[4,i.default().lock(c.iat)];case 1:o.sent(),this.acquiredIatSet.delete(c.iat),n.removeItemSync(r),i.default().unlock(c.iat),e.notifyWaiters(),o.label=2;case 2:return[2]}}))}))},e.lockCorrector=function(t){for(var n=Date.now()-5e3,r=t,o=[],s=0;;){var i=r.keySync(s);if(null===i)break;o.push(i),s++}for(var u=!1,c=0;c{Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){var e=this;this.locked=new Map,this.addToLocked=function(t,n){var r=e.locked.get(t);void 0===r?void 0===n?e.locked.set(t,[]):e.locked.set(t,[n]):void 0!==n&&(r.unshift(n),e.locked.set(t,r))},this.isLocked=function(t){return e.locked.has(t)},this.lock=function(t){return new Promise((function(n,r){e.isLocked(t)?e.addToLocked(t,n):(e.addToLocked(t),n())}))},this.unlock=function(t){var n=e.locked.get(t);if(void 0!==n&&0!==n.length){var r=n.pop();e.locked.set(t,n),void 0!==r&&setTimeout(r,0)}else e.locked.delete(t)}}return e.getInstance=function(){return void 0===e.instance&&(e.instance=new e),e.instance},e}();t.default=function(){return n.getInstance()}},225:function(e,t){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,n=1,r=arguments.length;n{Object.defineProperty(t,"__esModule",{value:!0}),t.OverrideableBuilder=void 0;var r=n(225),o=function(){function e(e){this.layers=[e],this.proxies=[]}return e.prototype.override=function(e){for(var t=(0,r.getProxyObject)(this.layers[0]),n=e(t,this),o=0,s=Object.keys(this.layers[0]);o=0;--o){var s=e.layers[o][n];if(null!=s)return s.bind(e.result).apply(void 0,r)}}},c=this,l=0;l{"use strict";var e={759:function(e,t,n){var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]n?[2,{isValid:!1,reason:{message:"expired",ageInSeconds:r,maxAgeInSeconds:n}}]:t.includes(e)?[2,{isValid:!0}]:[2,{isValid:!1,reason:{message:"wrong value",expectedToInclude:e,actualValue:t}}])}))}))}}},excludes:function(e,n,i){void 0===n&&(n=t.defaultMaxAgeInSeconds);var a=s.default.getReferenceOrThrow().dateProvider;return{id:void 0!==i?i:t.id,refresh:function(e){return t.refresh(e)},shouldRefresh:function(e,r){return void 0===t.getValueFromPayload(e,r)||void 0!==n&&e[t.id].tn?[2,{isValid:!1,reason:{message:"expired",ageInSeconds:r,maxAgeInSeconds:n}}]:t.includes(e)?[2,{isValid:!1,reason:{message:"wrong value",expectedToNotInclude:e,actualValue:t}}]:[2,{isValid:!0}])}))}))}}},includesAll:function(e,n,i){void 0===n&&(n=t.defaultMaxAgeInSeconds);var a=s.default.getReferenceOrThrow().dateProvider;return{id:void 0!==i?i:t.id,refresh:function(e){return t.refresh(e)},shouldRefresh:function(e,r){return void 0===t.getValueFromPayload(e,r)||void 0!==n&&e[t.id].tn?[2,{isValid:!1,reason:{message:"expired",ageInSeconds:r,maxAgeInSeconds:n}}]:(u=new Set(t),[2,(c=e.every((function(e){return u.has(e)})))?{isValid:c}:{isValid:c,reason:{message:"wrong value",expectedToInclude:e,actualValue:t}}]))}))}))}}},includesAny:function(e,n,i){void 0===n&&(n=t.defaultMaxAgeInSeconds);var a=s.default.getReferenceOrThrow().dateProvider;return{id:void 0!==i?i:t.id,refresh:function(e){return t.refresh(e)},shouldRefresh:function(e,r){return void 0===t.getValueFromPayload(e,r)||void 0!==n&&e[t.id].tn?[2,{isValid:!1,reason:{message:"expired",ageInSeconds:r,maxAgeInSeconds:n}}]:(u=new Set(t),[2,(c=e.some((function(e){return u.has(e)})))?{isValid:c}:{isValid:c,reason:{message:"wrong value",expectedToIncludeAtLeastOneOf:e,actualValue:t}}]))}))}))}}},excludesAll:function(e,n,i){void 0===n&&(n=t.defaultMaxAgeInSeconds);var a=s.default.getReferenceOrThrow().dateProvider;return{id:void 0!==i?i:t.id,refresh:function(e){return t.refresh(e)},shouldRefresh:function(e,r){return void 0===t.getValueFromPayload(e,r)||void 0!==n&&e[t.id].tn?[2,{isValid:!1,reason:{message:"expired",ageInSeconds:r,maxAgeInSeconds:n}}]:(u=new Set(t),[2,(c=e.every((function(e){return!u.has(e)})))?{isValid:c}:{isValid:c,reason:{message:"wrong value",expectedToNotInclude:e,actualValue:t}}]))}))}))}}}},this.id=e.id,this.refresh=e.refresh,this.defaultMaxAgeInSeconds=e.defaultMaxAgeInSeconds}return e.prototype.getValueFromPayload=function(e,t){return void 0!==e[this.id]?e[this.id].v:void 0},e.prototype.getLastFetchedTime=function(e,t){return void 0!==e[this.id]?e[this.id].t:void 0},e}();t.PrimitiveArrayClaim=i},911:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.PrimitiveClaim=void 0;var r=n(671),o=function(){function e(e){var t=this;this.validators={hasValue:function(e,n,o){void 0===n&&(n=t.defaultMaxAgeInSeconds);var s=r.default.getReferenceOrThrow().dateProvider;return{id:void 0!==o?o:t.id,refresh:function(e){return t.refresh(e)},shouldRefresh:function(e,r){return void 0===t.getValueFromPayload(e,r)||void 0!==n&&e[t.id].tn?{isValid:!1,reason:{message:"expired",ageInSeconds:a,maxAgeInSeconds:n}}:i!==e?{isValid:!1,reason:{message:"wrong value",expectedValue:e,actualValue:i}}:{isValid:!0}}}}},this.id=e.id,this.refresh=e.refresh,this.defaultMaxAgeInSeconds=e.defaultMaxAgeInSeconds}return e.prototype.getValueFromPayload=function(e,t){return void 0!==e[this.id]?e[this.id].v:void 0},e.prototype.getLastFetchedTime=function(e,t){return void 0!==e[this.id]?e[this.id].t:void 0},e}();t.PrimitiveClaim=o},173:function(e,t){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0}),t.STGeneralError=void 0;var o=function(e){function t(t){var n=e.call(this,t)||this;return n.isSuperTokensGeneralError=!0,n}return r(t,e),t.isThisError=function(e){return!0===e.isSuperTokensGeneralError},t}(Error);t.STGeneralError=o},379:function(e,t,n){var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]=300)throw c;return[4,p.config.postAPIHook({action:"REFRESH_SESSION",fetchResponse:c.clone(),requestInit:u.requestInit,url:u.url,userContext:{}})];case 12:return s.sent(),[4,S(!1)];case 13:return"NOT_EXISTS"===s.sent().status?((0,d.logDebugMessage)("onUnauthorisedResponse: local session doesn't exist, so returning session expired"),[2,{result:"SESSION_EXPIRED"}]):(p.config.onHandleEvent({action:"REFRESH_SESSION",userContext:{}}),(0,d.logDebugMessage)("onUnauthorisedResponse: Sending RETRY signal"),[2,{result:"RETRY"}]);case 14:return v=s.sent(),[4,S(!1)];case 15:return"NOT_EXISTS"===s.sent().status?((0,d.logDebugMessage)("onUnauthorisedResponse: local session doesn't exist, so returning session expired"),[2,{result:"SESSION_EXPIRED",error:v}]):((0,d.logDebugMessage)("onUnauthorisedResponse: sending API_ERROR"),[2,{result:"API_ERROR",error:v}]);case 16:return[4,t.releaseLock("REFRESH_TOKEN_USE")];case 17:return s.sent(),(0,d.logDebugMessage)("onUnauthorisedResponse: Released lock"),[4,S(!1)];case 18:return"NOT_EXISTS"!==s.sent().status?[3,21]:((0,d.logDebugMessage)("onUnauthorisedResponse: local session doesn't exist, so removing anti-csrf and sFrontToken"),[4,g.removeToken()]);case 19:return s.sent(),[4,h.removeToken()];case 20:s.sent(),s.label=21;case 21:return[7];case 22:return[4,S(!1)];case 23:return"NOT_EXISTS"===(b=s.sent()).status?((0,d.logDebugMessage)("onUnauthorisedResponse: lock acquired failed and local session doesn't exist, so sending SESSION_EXPIRED"),[2,{result:"SESSION_EXPIRED"}]):b.status!==e.status||"EXISTS"===b.status&&"EXISTS"===e.status&&b.lastAccessTokenUpdate!==e.lastAccessTokenUpdate?((0,d.logDebugMessage)("onUnauthorisedResponse: lock acquired failed and retrying early because pre and post lastAccessTokenUpdate don't match"),[2,{result:"RETRY"}]):[3,2];case 24:return[2]}}))}))}function w(){(0,d.logDebugMessage)("onTokenUpdate: firing ACCESS_TOKEN_PAYLOAD_UPDATED event"),p.config.onHandleEvent({action:"ACCESS_TOKEN_PAYLOAD_UPDATED",userContext:{}})}function y(e){return o(this,void 0,void 0,(function(){var t;return s(this,(function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,p.recipeImpl.getInvalidClaimsFromResponse({response:e,userContext:{}})];case 1:return(t=n.sent())&&p.config.onHandleEvent({action:"API_INVALID_CLAIM",claimValidationErrors:t,userContext:{}}),[3,3];case 2:return n.sent(),[3,3];case 3:return[2]}}))}))}function S(e){return o(this,void 0,void 0,(function(){var t,n,r;return s(this,(function(o){switch(o.label){case 0:return(0,d.logDebugMessage)("getLocalSessionState: called"),[4,E(v)];case 1:return t=o.sent(),[4,h.doesTokenExists()];case 2:return o.sent()&&void 0!==t?((0,d.logDebugMessage)("getLocalSessionState: returning EXISTS since both frontToken and lastAccessTokenUpdate exists"),[2,{status:"EXISTS",lastAccessTokenUpdate:t}]):[3,3];case 3:return t?((0,d.logDebugMessage)("getLocalSessionState: returning NOT_EXISTS since frontToken was cleared but lastAccessTokenUpdate exists"),[2,{status:"NOT_EXISTS"}]):[3,4];case 4:return n={status:"MAY_EXIST"},e?((0,d.logDebugMessage)("getLocalSessionState: trying to refresh"),[4,k(n)]):[3,7];case 5:return"RETRY"!==(r=o.sent()).result?((0,d.logDebugMessage)("getLocalSessionState: return NOT_EXISTS in case error from backend"+r.result),[2,{status:"NOT_EXISTS"}]):((0,d.logDebugMessage)("getLocalSessionState: Retrying post refresh"),[4,S(e)]);case 6:return[2,o.sent()];case 7:return(0,d.logDebugMessage)("getLocalSessionState: returning: "+n.status),[2,n]}}))}))}function T(e){switch(e){case"access":return"st-access-token";case"refresh":return"st-refresh-token"}}function I(e,t){var n=T(e);return""!==t?((0,d.logDebugMessage)("setToken: saved ".concat(e," token into cookies")),R(n,t,Date.now()+31536e5)):((0,d.logDebugMessage)("setToken: cleared ".concat(e," token from cookies")),R(n,t,0))}function R(e,t,n){var r="Fri, 31 Dec 9999 23:59:59 GMT";n!==Number.MAX_SAFE_INTEGER&&(r=new Date(n).toUTCString());var o=p.config.sessionTokenFrontendDomain;return"localhost"===o||o===c.default.getReferenceOrThrow().windowHandler.location.getHostName()?u.default.getReferenceOrThrow().cookieHandler.setCookie("".concat(e,"=").concat(t,";expires=").concat(r,";path=/;samesite=").concat(p.config.isInIframe?"none;secure":"lax")):u.default.getReferenceOrThrow().cookieHandler.setCookie("".concat(e,"=").concat(t,";expires=").concat(r,";domain=").concat(o,";path=/;samesite=").concat(p.config.isInIframe?"none;secure":"lax"))}function D(e){return o(this,void 0,void 0,(function(){return s(this,(function(t){return[2,E(T(e))]}))}))}function E(e){return o(this,void 0,void 0,(function(){var t,n,r,o;return s(this,(function(s){switch(s.label){case 0:return n="; ",[4,u.default.getReferenceOrThrow().cookieHandler.getCookie()];case 1:return t=n+s.sent(),(r=t.split("; "+e+"=")).length>=2&&void 0!==(o=r.pop())?[2,o.split(";").shift()]:[2,void 0]}}))}))}function M(e,t){return void 0===t&&(t=!1),o(this,void 0,void 0,(function(){var n,r;return s(this,(function(o){switch(o.label){case 0:return(0,d.logDebugMessage)("setTokenHeaders: adding existing tokens as header"),[4,D("access")];case 1:return n=o.sent(),[4,D("refresh")];case 2:return r=o.sent(),!t&&void 0===n||void 0===r?(0,d.logDebugMessage)("setAuthorizationHeaderIfRequired: token for header based auth not found"):e.has("Authorization")?(0,d.logDebugMessage)("setAuthorizationHeaderIfRequired: Authorization header defined by the user, not adding"):((0,d.logDebugMessage)("setAuthorizationHeaderIfRequired: added authorization header"),e.set("Authorization","Bearer ".concat(t?r:n))),[2]}}))}))}function x(e){return o(this,void 0,void 0,(function(){var n,r,o,i,a;return s(this,(function(s){switch(s.label){case 0:return(0,d.logDebugMessage)("saveTokensFromHeaders: Saving updated tokens from the response headers"),null===(n=e.headers.get("st-refresh-token"))?[3,2]:((0,d.logDebugMessage)("saveTokensFromHeaders: saving new refresh token"),[4,I("refresh",n)]);case 1:s.sent(),s.label=2;case 2:return null===(r=e.headers.get("st-access-token"))?[3,4]:((0,d.logDebugMessage)("saveTokensFromHeaders: saving new access token"),[4,I("access",r)]);case 3:s.sent(),s.label=4;case 4:return null===(o=e.headers.get("front-token"))?[3,6]:((0,d.logDebugMessage)("saveTokensFromHeaders: Setting sFrontToken: "+o),[4,h.setItem(o)]);case 5:s.sent(),(0,t.updateClockSkewUsingFrontToken)({frontToken:o,responseHeaders:e.headers}),s.label=6;case 6:return null===(i=e.headers.get("anti-csrf"))?[3,9]:[4,S(!0)];case 7:return"EXISTS"!==(a=s.sent()).status?[3,9]:((0,d.logDebugMessage)("saveTokensFromHeaders: Setting anti-csrf token"),[4,g.setItem(a.lastAccessTokenUpdate,i)]);case 8:s.sent(),s.label=9;case 9:return[2]}}))}))}function A(){return o(this,void 0,void 0,(function(){var e;return s(this,(function(t){switch(t.label){case 0:return(0,d.logDebugMessage)("saveLastAccessTokenUpdate: called"),e=Date.now().toString(),(0,d.logDebugMessage)("saveLastAccessTokenUpdate: setting "+e),[4,R(v,e,Number.MAX_SAFE_INTEGER)];case 1:return t.sent(),[4,R("sIRTFrontend","",0)];case 2:return t.sent(),[2]}}))}))}function C(){return o(this,void 0,void 0,(function(){function e(){return o(this,void 0,void 0,(function(){var e,t,n,r,o;return s(this,(function(s){switch(s.label){case 0:return t="; ",[4,u.default.getReferenceOrThrow().cookieHandler.getCookie()];case 1:return e=t+s.sent(),(n=e.split("; sAntiCsrf=")).length>=2&&void 0!==(r=n.pop())?void 0===(o=r.split(";").shift())?[2,null]:[2,o]:[2,null]}}))}))}var t;return s(this,(function(n){switch(n.label){case 0:return(0,d.logDebugMessage)("getAntiCSRFToken: called"),[4,S(!0)];case 1:return"EXISTS"!==n.sent().status?((0,d.logDebugMessage)("getAntiCSRFToken: Returning because local session state != EXISTS"),[2,null]):[4,e()];case 2:return t=n.sent(),(0,d.logDebugMessage)("getAntiCSRFToken: returning: "+t),[2,t]}}))}))}function _(e){return o(this,void 0,void 0,(function(){return s(this,(function(t){switch(t.label){case 0:return(0,d.logDebugMessage)("setAntiCSRF: called: "+e),void 0===e?[3,2]:[4,R(b,e,Number.MAX_SAFE_INTEGER)];case 1:return t.sent(),[3,4];case 2:return[4,R(b,"",0)];case 3:t.sent(),t.label=4;case 4:return[2]}}))}))}function O(){return o(this,void 0,void 0,(function(){var e;return s(this,(function(t){switch(t.label){case 0:return(0,d.logDebugMessage)("getFrontTokenFromCookie: called"),[4,E(m)];case 1:return[2,void 0===(e=t.sent())?null:e]}}))}))}function P(e){return JSON.parse(decodeURIComponent(escape(atob(e))))}function H(){return o(this,void 0,void 0,(function(){var e;return s(this,(function(t){switch(t.label){case 0:return(0,d.logDebugMessage)("getFrontToken: called"),[4,S(!0)];case 1:return"EXISTS"!==t.sent().status?((0,d.logDebugMessage)("getFrontToken: Returning because sIRTFrontend != EXISTS"),[2,null]):[4,O()];case 2:return e=t.sent(),(0,d.logDebugMessage)("getFrontToken: returning: "+e),[2,e]}}))}))}function F(e){return o(this,void 0,void 0,(function(){var t,n,r;return s(this,(function(o){switch(o.label){case 0:return(0,d.logDebugMessage)("setFrontToken: called"),[4,O()];case 1:return null!==(t=o.sent())&&void 0!==e&&(n=P(t).up,r=P(e).up,JSON.stringify(n)!==JSON.stringify(r)&&w()),void 0!==e?[3,3]:[4,R(m,"",0)];case 2:return o.sent(),[3,5];case 3:return[4,R(m,e,Number.MAX_SAFE_INTEGER)];case 4:o.sent(),o.label=5;case 5:return[2]}}))}))}function U(e,t,n){if(null!=n){var r="remove"!==n;(0,d.logDebugMessage)("fireSessionUpdateEventsIfNecessary wasLoggedIn: ".concat(e," frontTokenExistsAfter: ").concat(r," status: ").concat(t)),e?r||(t===p.config.sessionExpiredStatusCode?((0,d.logDebugMessage)("onUnauthorisedResponse: firing UNAUTHORISED event"),p.config.onHandleEvent({action:"UNAUTHORISED",sessionExpiredOrRevoked:!0,userContext:{}})):((0,d.logDebugMessage)("onUnauthorisedResponse: firing SIGN_OUT event"),p.config.onHandleEvent({action:"SIGN_OUT",userContext:{}}))):r&&((0,d.logDebugMessage)("onUnauthorisedResponse: firing SESSION_CREATED event"),p.config.onHandleEvent({action:"SESSION_CREATED",userContext:{}}))}else(0,d.logDebugMessage)("fireSessionUpdateEventsIfNecessary returning early because the front token was not updated")}t.onUnauthorisedResponse=k,t.onTokenUpdate=w,t.onInvalidClaimResponse=y,t.getLocalSessionState=S,t.getStorageNameForToken=T,t.setToken=I,t.getTokenForHeaderAuth=D,t.saveLastAccessTokenUpdate=A,t.setAntiCSRF=_,t.getFrontToken=H,t.setFrontToken=F,t.fireSessionUpdateEventsIfNecessary=U,t.updateClockSkewUsingFrontToken=function(e){var t=e.frontToken,n=e.responseHeaders;if((0,d.logDebugMessage)("updateClockSkewUsingFrontToken: frontToken: "+t),null!=t&&"remove"!==t){var r=P(t),o=p.recipeImpl.getClockSkewInMillis({accessTokenPayload:r.up,responseHeaders:n});f.default.getReferenceOrThrow().dateProvider.setClientClockSkewInMillis(o),(0,d.logDebugMessage)("updateClockSkewUsingFrontToken: Client clock synchronized successfully")}else(0,d.logDebugMessage)("updateClockSkewUsingFrontToken: the access token payload wasn't updated or is being removed, skipping clock skew update")}},569:function(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,s){function i(e){try{u(r.next(e))}catch(e){s(e)}}function a(e){try{u(r.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,a)}u((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,s,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]{Object.defineProperty(t,"__esModule",{value:!0}),t.logDebugMessage=t.disableLogging=t.enableLogging=void 0;var r=n(255),o=!1;t.enableLogging=function(){o=!0},t.disableLogging=function(){o=!1},t.logDebugMessage=function(e){o&&console.log("".concat("com.supertokens",' {t: "').concat((new Date).toISOString(),'", message: "').concat(e,'", supertokens-website-ver: "').concat(r.package_version,'"}'))}},992:(e,t)=>{function n(e){return/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.isAnIpAddress=void 0,t.isAnIpAddress=n;function r(e,t){void 0===t&&(t=!1),e=e.trim();try{if(!e.startsWith("http://")&&!e.startsWith("https://"))throw new Error("converting to proper URL");var o=new URL(e);return t?o.hostname.startsWith("localhost")||n(o.hostname)?"http://"+o.host:"https://"+o.host:o.protocol+"//"+o.host}catch(e){}if(e.startsWith("/"))throw new Error("Please provide a valid domain name");if(0===e.indexOf(".")&&(e=e.substr(1)),(-1!==e.indexOf(".")||e.startsWith("localhost"))&&!e.startsWith("http://")&&!e.startsWith("https://")){e="https://"+e;try{return new URL(e),r(e,!0)}catch(e){}}throw new Error("Please provide a valid domain name")}t.default=function(e){var t=this;this.getAsStringDangerous=function(){return t.value},this.value=r(e)}},260:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0});function n(e){e=e.trim();try{if(!e.startsWith("http://")&&!e.startsWith("https://"))throw new Error("converting to proper URL");return"/"===(e=new URL(e).pathname).charAt(e.length-1)?e.substr(0,e.length-1):e}catch(e){}if((function(e){if(-1===e.indexOf(".")||e.startsWith("/"))return!1;try{return-1!==new URL(e).hostname.indexOf(".")}catch(e){}try{return-1!==new URL("http://"+e).hostname.indexOf(".")}catch(e){}return!1}(e)||e.startsWith("localhost"))&&!e.startsWith("http://")&&!e.startsWith("https://"))return n(e="http://"+e);"/"!==e.charAt(0)&&(e="/"+e);try{return new URL("http://example.com"+e),n("http://example.com"+e)}catch(e){throw new Error("Please provide a valid URL path")}}t.default=function e(t){var r=this;this.startsWith=function(e){return r.value.startsWith(e.value)},this.appendPath=function(t){return new e(r.value+t.value)},this.getAsStringDangerous=function(){return r.value},this.value=n(t)}},743:function(e,t){var n,r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,s){function i(e){try{u(r.next(e))}catch(e){s(e)}}function a(e){try{u(r.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,a)}u((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,s,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]=0;n--)if(e.history[n]==t)return e.history[n]},this.reset=function(){e.history=[]},this.waitForEvent=function(t,n){return void 0===n&&(n=7e3),r(e,void 0,void 0,(function(){var e,r=this;return o(this,(function(o){return e=Date.now(),[2,new Promise((function(o){var s=r;!function r(){var i=s.getEventByLastEventByName(t);void 0===i?Date.now()-e>n?o(void 0):setTimeout(r,1e3):o(i)}()}))]}))}))}}return e.getInstance=function(){return null==e.instance&&(e.instance=new e),e.instance},e}();t.ProcessState=s},994:function(e,t,n){var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]=300)throw r;return[4,e.postAPIHook({action:"SIGN_OUT",requestInit:n.requestInit,url:n.url,fetchResponse:r.clone(),userContext:t.userContext})];case 4:return s.sent(),[4,r.clone().json()];case 5:if("GENERAL_ERROR"===(o=s.sent()).status)throw(0,c.logDebugMessage)("doRequest: Throwing general error"),a=void 0===o.message?"No Error Message Provided":o.message,new l.STGeneralError(a);return[2]}}))}))},getInvalidClaimsFromResponse:function(e){return o(this,void 0,void 0,(function(){var t;return s(this,(function(n){switch(n.label){case 0:return"body"in e.response?[4,e.response.clone().json()]:[3,2];case 1:return t=n.sent(),[3,3];case 2:t="string"==typeof e.response.data?JSON.parse(e.response.data):e.response.data,n.label=3;case 3:return[2,t.claimValidationErrors]}}))}))},getGlobalClaimValidators:function(e){return e.claimValidatorsAddedByOtherRecipes},validateClaims:function(e){return o(this,void 0,void 0,(function(){var t,n,r,o,i,a,u,c,l;return s(this,(function(s){switch(s.label){case 0:return[4,this.getAccessTokenPayloadSecurely({userContext:e.userContext})];case 1:t=s.sent(),n=0,r=e.claimValidators,s.label=2;case 2:return n1){var u=n.split(":")[n.split(":").length-1];"string"!=typeof(i=u)||isNaN(i)||isNaN(parseFloat(i))||(a+=":"+u,o=""===r.port?o:o+":"+r.port)}return n.startsWith(".")?("."+o).endsWith(a):o===a},getClockSkewInMillis:function(e){var t=e.accessTokenPayload;(0,c.logDebugMessage)("getClockSkewInMillis: called");var n=null==t?void 0:t.iat;if(void 0===n||"number"!=typeof n)return(0,c.logDebugMessage)("getClockSkewInMillis: payload iat is undefined or not a number. This may happen due to an unsupported backend sdk. Returning 0"),0;var r=1e3*n-Date.now();return(0,c.logDebugMessage)("getClockSkewInMillis: returning "+r),r}}}},272:function(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,s){function i(e){try{u(r.next(e))}catch(e){s(e)}}function a(e){try{u(r.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,a)}u((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,s,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]{Object.defineProperty(t,"__esModule",{value:!0}),t.CookieHandlerReference=void 0;var r=n(272),o=function(){function e(e){var t=function(e){return e};void 0!==e&&(t=e),this.cookieHandler=t(r.defaultCookieHandlerImplementation)}return e.init=function(t){void 0===e.instance&&(e.instance=new e(t))},e.getReferenceOrThrow=function(){if(void 0===e.instance)throw new Error("SuperTokensCookieHandler must be initialized before calling this method.");return e.instance},e}();t.CookieHandlerReference=o,t.default=o},812:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DateProvider=void 0;var r=n(958),o=function(){function e(){this.clockSkewInMillis=0}return e.init=function(){if(void 0===e.instance){e.instance=new e;var t=r.default.getReferenceOrThrow().windowHandler.localStorage.getItemSync(e.CLOCK_SKEW_KEY),n=null!==t?parseInt(t,10):0;e.instance.setClientClockSkewInMillis(n)}},e.getReferenceOrThrow=function(){if(void 0===e.instance)throw new Error("DateProvider must be initialized before calling this method.");return e.instance},e.prototype.setClientClockSkewInMillis=function(t){this.clockSkewInMillis=t,r.default.getReferenceOrThrow().windowHandler.localStorage.setItemSync(e.CLOCK_SKEW_KEY,String(t))},e.prototype.getClientClockSkewInMillis=function(){return this.clockSkewInMillis},e.prototype.now=function(){return Date.now()+this.getClientClockSkewInMillis()},e.CLOCK_SKEW_KEY="__st_clockSkewInMillis",e}();t.DateProvider=o},671:(e,t,n)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DateProviderReference=void 0;var r=n(812),o=function(){function e(e){var t=function(e){return e};void 0!==e&&(t=e),r.DateProvider.init();var n=r.DateProvider.getReferenceOrThrow();this.dateProvider=t(n)}return e.init=function(t){void 0===e.instance&&(e.instance=new e(t))},e.getReferenceOrThrow=function(){if(void 0===e.instance)throw new Error("SuperTokensDateProvider must be initialized before calling this method.");return e.instance},e}();t.DateProviderReference=o,t.default=o},318:function(e,t,n){var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]{Object.defineProperty(t,"__esModule",{value:!0}),t.LockFactoryReference=void 0;var r=n(895),o=function(){function e(e){this.lockFactory=e}return e.init=function(t,n){void 0===this.instance&&(this.instance=new e(null!=t?t:function(e){return function(){return Promise.resolve(new r.default(e))}}(n)))},e.getReferenceOrThrow=function(){if(void 0===e.instance)throw new Error("SuperTokensLockReference must be initialized before calling this method.");return e.instance},e}();t.LockFactoryReference=o,t.default=o},153:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SessionClaimValidatorStore=void 0;var n=function(){function e(){}return e.claimValidatorsAddedByOtherRecipes=[],e.addClaimValidatorFromOtherRecipe=function(t){e.claimValidatorsAddedByOtherRecipes.push(t)},e.getClaimValidatorsAddedByOtherRecipes=function(){return e.claimValidatorsAddedByOtherRecipes},e}();t.SessionClaimValidatorStore=n,t.default=n},586:function(e,t){var n=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,s){function i(e){try{u(r.next(e))}catch(e){s(e)}}function a(e){try{u(r.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,a)}u((r=r.apply(e,t||[])).next())}))},r=this&&this.__generator||function(e,t){var n,r,o,s,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]{Object.defineProperty(t,"__esModule",{value:!0}),t.WindowHandlerReference=void 0;var r=n(586),o=function(){function e(e){var t=function(e){return e};void 0!==e&&(t=e),this.windowHandler=t(r.defaultWindowHandlerImplementation)}return e.init=function(t){void 0===e.instance&&(e.instance=new e(t))},e.getReferenceOrThrow=function(){if(void 0===e.instance)throw new Error("SuperTokensWindowHandler must be initialized before calling this method.");return e.instance},e}();t.WindowHandlerReference=o,t.default=o},255:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.supported_fdi=t.package_version=void 0,t.package_version="18.0.0",t.supported_fdi=["1.16","1.17","1.18"]},648:function(e,t,n){var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,s){function i(e){try{u(r.next(e))}catch(e){s(e)}}function a(e){try{u(r.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,a)}u((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,s,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&s[0]?r.return:s[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,s[1])).done)return o;switch(r=0,o&&(s=[2&s[0],o.value]),s[0]){case 0:case 1:o=s;break;case 4:return i.label++,{value:s[1],done:!1};case 5:i.label++,r=s[1],s=[0];continue;case 7:s=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]0&&o[o.length-1])||6!==s[0]&&2!==s[0])){i=0;continue}if(3===s[0]&&(!o||s[1]>o[0]&&s[1]0?setTimeout(n,t):n(null)}}window.addEventListener("storage",i),e.addToWaiting(i);var a=setTimeout(i,Math.max(0,t-Date.now()))}))];case 1:return n.sent(),[2]}}))}))},e.addToWaiting=function(t){this.removeFromWaiting(t),void 0!==e.waiters&&e.waiters.push(t)},e.removeFromWaiting=function(t){void 0!==e.waiters&&(e.waiters=e.waiters.filter((function(e){return e!==t})))},e.notifyWaiters=function(){void 0!==e.waiters&&e.waiters.slice().forEach((function(e){return e()}))},e.prototype.releaseLock=function(e){return r(this,void 0,void 0,(function(){return o(this,(function(t){switch(t.label){case 0:return[4,this.releaseLock__private__(e)];case 1:return[2,t.sent()]}}))}))},e.prototype.releaseLock__private__=function(t){return r(this,void 0,void 0,(function(){var n,r,s,c;return o(this,(function(o){switch(o.label){case 0:return n=void 0===this.storageHandler?u:this.storageHandler,r=a+"-"+t,null===(s=n.getItemSync(r))?[2]:(c=JSON.parse(s)).id!==this.id?[3,2]:[4,i.default().lock(c.iat)];case 1:o.sent(),this.acquiredIatSet.delete(c.iat),n.removeItemSync(r),i.default().unlock(c.iat),e.notifyWaiters(),o.label=2;case 2:return[2]}}))}))},e.lockCorrector=function(t){for(var n=Date.now()-5e3,r=t,o=[],s=0;;){var i=r.keySync(s);if(null===i)break;o.push(i),s++}for(var u=!1,c=0;c{Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){var e=this;this.locked=new Map,this.addToLocked=function(t,n){var r=e.locked.get(t);void 0===r?void 0===n?e.locked.set(t,[]):e.locked.set(t,[n]):void 0!==n&&(r.unshift(n),e.locked.set(t,r))},this.isLocked=function(t){return e.locked.has(t)},this.lock=function(t){return new Promise((function(n,r){e.isLocked(t)?e.addToLocked(t,n):(e.addToLocked(t),n())}))},this.unlock=function(t){var n=e.locked.get(t);if(void 0!==n&&0!==n.length){var r=n.pop();e.locked.set(t,n),void 0!==r&&setTimeout(r,0)}else e.locked.delete(t)}}return e.getInstance=function(){return void 0===e.instance&&(e.instance=new e),e.instance},e}();t.default=function(){return n.getInstance()}},225:function(e,t){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,n=1,r=arguments.length;n{Object.defineProperty(t,"__esModule",{value:!0}),t.OverrideableBuilder=void 0;var r=n(225),o=function(){function e(e){this.layers=[e],this.proxies=[]}return e.prototype.override=function(e){for(var t=(0,r.getProxyObject)(this.layers[0]),n=e(t,this),o=0,s=Object.keys(this.layers[0]);o=0;--o){var s=e.layers[o][n];if(null!=s)return s.bind(e.result).apply(void 0,r)}}},c=this,l=0;lBooleanClaim | supertokens-website
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

Index

Constructors

  • new BooleanClaim(config: PrimitiveClaimConfig): BooleanClaim

Properties

defaultMaxAgeInSeconds: undefined | number
id: string
refresh: ((userContext: any) => Promise<void>)

Type declaration

validators: { hasValue: ((val: boolean, maxAgeInSeconds?: undefined | number, id?: string) => SessionClaimValidator) } & BooleanValidators

Methods

  • getLastFetchedTime(payload: any, _userContext?: any): undefined | number
  • getValueFromPayload(payload: any, _userContext?: any): boolean

Legend

  • Constructor
  • Property
  • Method
  • Inherited property
  • Inherited method
  • Static property
  • Static method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/PrimitiveArrayClaim.html b/docs/classes/PrimitiveArrayClaim.html index a149ab71..11399c23 100644 --- a/docs/classes/PrimitiveArrayClaim.html +++ b/docs/classes/PrimitiveArrayClaim.html @@ -1,3 +1,3 @@ -PrimitiveArrayClaim | supertokens-website
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class PrimitiveArrayClaim<ValueType>

Type Parameters

  • ValueType

Hierarchy

  • PrimitiveArrayClaim

Index

Constructors

  • new PrimitiveArrayClaim<ValueType>(config: PrimitiveArrayClaimConfig): PrimitiveArrayClaim<ValueType>

Properties

defaultMaxAgeInSeconds: undefined | number
id: string
refresh: ((userContext: any) => Promise<void>)

Type declaration

validators: { excludes: ((val: ValueType, maxAgeInSeconds?: undefined | number, id?: string) => SessionClaimValidator); excludesAll: ((val: ValueType[], maxAgeInSeconds?: undefined | number, id?: string) => SessionClaimValidator); includes: ((val: ValueType, maxAgeInSeconds?: undefined | number, id?: string) => SessionClaimValidator); includesAll: ((val: ValueType[], maxAgeInSeconds?: undefined | number, id?: string) => SessionClaimValidator); includesAny: ((val: ValueType[], maxAgeInSeconds?: undefined | number, id?: string) => SessionClaimValidator) } = ...

Type declaration

Methods

  • getLastFetchedTime(payload: any, _userContext?: any): undefined | number
  • getValueFromPayload(payload: any, _userContext?: any): ValueType[]

Legend

  • Constructor
  • Property
  • Method
  • Inherited property
  • Inherited method
  • Static property
  • Static method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/PrimitiveClaim.html b/docs/classes/PrimitiveClaim.html index b0f86261..64d19f91 100644 --- a/docs/classes/PrimitiveClaim.html +++ b/docs/classes/PrimitiveClaim.html @@ -1,3 +1,3 @@ -PrimitiveClaim | supertokens-website
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class PrimitiveClaim<ValueType>

Type Parameters

  • ValueType

Hierarchy

Index

Constructors

  • new PrimitiveClaim<ValueType>(config: PrimitiveClaimConfig): PrimitiveClaim<ValueType>

Properties

defaultMaxAgeInSeconds: undefined | number
id: string
refresh: ((userContext: any) => Promise<void>)

Type declaration

validators: { hasValue: ((val: ValueType, maxAgeInSeconds?: undefined | number, id?: string) => SessionClaimValidator) } = ...

Type declaration

Methods

  • getLastFetchedTime(payload: any, _userContext?: any): undefined | number
  • getValueFromPayload(payload: any, _userContext?: any): ValueType

Legend

  • Constructor
  • Property
  • Method
  • Inherited property
  • Inherited method
  • Static property
  • Static method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/default.html b/docs/classes/default.html index 98d4db62..70b8a0f6 100644 --- a/docs/classes/default.html +++ b/docs/classes/default.html @@ -1 +1 @@ -default | supertokens-website
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

axiosInterceptorQueue: (() => void)[] = []
getClaimValue: (<T>(input: { claim: SessionClaim<T>; userContext?: any }) => Promise<undefined | T>) = ...

Type declaration

    • <T>(input: { claim: SessionClaim<T>; userContext?: any }): Promise<undefined | T>
    • Type Parameters

      • T

      Parameters

      Returns Promise<undefined | T>

getInvalidClaimsFromResponse: ((input: { response: Response | { data: any }; userContext?: any }) => Promise<ClaimValidationError[]>) = ...

Type declaration

    • Parameters

      • input: { response: Response | { data: any }; userContext?: any }
        • response: Response | { data: any }
        • Optional userContext?: any

      Returns Promise<ClaimValidationError[]>

Methods

  • addAxiosInterceptors(axiosInstance: any, userContext?: any): void
  • deprecated

    Parameters

    • axiosInstance: any
    • Optional userContext: any

    Returns void

  • attemptRefreshingSession(): Promise<boolean>
  • doesSessionExist(input?: { userContext?: any }): Promise<boolean>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<boolean>

  • getAccessToken(input?: { userContext?: any }): Promise<undefined | string>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<undefined | string>

  • getAccessTokenPayloadSecurely(input?: { userContext?: any }): Promise<any>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<any>

  • getUserId(input?: { userContext?: any }): Promise<string>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<string>

  • signOut(input?: { userContext?: any }): Promise<void>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<void>

Legend

  • Constructor
  • Property
  • Method
  • Inherited property
  • Inherited method
  • Static property
  • Static method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +default | supertokens-website
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • default

Index

Constructors

Properties

axiosInterceptorQueue: (() => void)[] = []
getClaimValue: (<T>(input: { claim: SessionClaim<T>; userContext?: any }) => Promise<undefined | T>) = ...

Type declaration

    • <T>(input: { claim: SessionClaim<T>; userContext?: any }): Promise<undefined | T>
    • Type Parameters

      • T

      Parameters

      Returns Promise<undefined | T>

getInvalidClaimsFromResponse: ((input: { response: Response | { data: any }; userContext?: any }) => Promise<ClaimValidationError[]>) = ...

Type declaration

    • Parameters

      • input: { response: Response | { data: any }; userContext?: any }
        • response: Response | { data: any }
        • Optional userContext?: any

      Returns Promise<ClaimValidationError[]>

Methods

  • addAxiosInterceptors(axiosInstance: any, userContext?: any): void
  • deprecated

    Parameters

    • axiosInstance: any
    • Optional userContext: any

    Returns void

  • attemptRefreshingSession(): Promise<boolean>
  • doesSessionExist(input?: { userContext?: any }): Promise<boolean>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<boolean>

  • getAccessToken(input?: { userContext?: any }): Promise<undefined | string>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<undefined | string>

  • getAccessTokenPayloadSecurely(input?: { userContext?: any }): Promise<any>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<any>

  • getUserId(input?: { userContext?: any }): Promise<string>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<string>

  • signOut(input?: { userContext?: any }): Promise<void>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<void>

Legend

  • Constructor
  • Property
  • Method
  • Inherited property
  • Inherited method
  • Static property
  • Static method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/modules.html b/docs/modules.html index 12298e2f..48d3f29e 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -1,12 +1,12 @@ -supertokens-website
Options
All
  • Public
  • Public/Protected
  • All
Menu

supertokens-website

Index

Type Aliases

ClaimValidationError: { reason?: any; validatorId: string }

Type declaration

  • Optional reason?: any
  • validatorId: string
ClaimValidationResult: { isValid: true } | { isValid: false; reason?: any }
InputType: { apiBasePath?: string; apiDomain: string; autoAddCredentials?: boolean; cookieHandler?: CookieHandlerInput; enableDebugLogs?: boolean; invalidClaimStatusCode?: number; isInIframe?: boolean; lockFactory?: LockFactory; onHandleEvent?: EventHandler; override?: { functions?: any }; postAPIHook?: RecipePostAPIHookFunction; preAPIHook?: RecipePreAPIHookFunction; sessionExpiredStatusCode?: number; sessionTokenBackendDomain?: string; sessionTokenFrontendDomain?: string; tokenTransferMethod?: "cookie" | "header"; windowHandler?: WindowHandlerInput }

Type declaration

  • Optional apiBasePath?: string
  • apiDomain: string
  • Optional autoAddCredentials?: boolean
  • Optional cookieHandler?: CookieHandlerInput
  • Optional enableDebugLogs?: boolean
  • Optional invalidClaimStatusCode?: number
  • Optional isInIframe?: boolean
  • Optional lockFactory?: LockFactory
    +supertokens-website
    Options
    All
    • Public
    • Public/Protected
    • All
    Menu

    supertokens-website

    Index

    Type Aliases

    ClaimValidationError: { reason?: any; validatorId: string }

    Type declaration

    • Optional reason?: any
    • validatorId: string
    ClaimValidationResult: { isValid: true } | { isValid: false; reason?: any }
    InputType: { apiBasePath?: string; apiDomain: string; autoAddCredentials?: boolean; cookieHandler?: CookieHandlerInput; dateProvider?: DateProviderInput; enableDebugLogs?: boolean; invalidClaimStatusCode?: number; isInIframe?: boolean; lockFactory?: LockFactory; onHandleEvent?: EventHandler; override?: { functions?: any }; postAPIHook?: RecipePostAPIHookFunction; preAPIHook?: RecipePreAPIHookFunction; sessionExpiredStatusCode?: number; sessionTokenBackendDomain?: string; sessionTokenFrontendDomain?: string; tokenTransferMethod?: "cookie" | "header"; windowHandler?: WindowHandlerInput }

    Type declaration

    • Optional apiBasePath?: string
    • apiDomain: string
    • Optional autoAddCredentials?: boolean
    • Optional cookieHandler?: CookieHandlerInput
    • Optional dateProvider?: DateProviderInput
    • Optional enableDebugLogs?: boolean
    • Optional invalidClaimStatusCode?: number
    • Optional isInIframe?: boolean
    • Optional lockFactory?: LockFactory

      This allows for a Lock factory to be configured, which defaults to browser-tabs-lock. This can be used, for example, by a WebExtension that needs to update cookies for a domain that may or may not have an associated tab open.

      -
    • Optional onHandleEvent?: EventHandler
    • Optional override?: { functions?: any }
    • Optional postAPIHook?: RecipePostAPIHookFunction
    • Optional preAPIHook?: RecipePreAPIHookFunction
    • Optional sessionExpiredStatusCode?: number
    • Optional sessionTokenBackendDomain?: string
    • Optional sessionTokenFrontendDomain?: string
    • Optional tokenTransferMethod?: "cookie" | "header"
    • Optional windowHandler?: WindowHandlerInput
    RecipeInterface: { addAxiosInterceptors: any; addFetchInterceptorsAndReturnModifiedFetch: any; addXMLHttpRequestInterceptor: any; doesSessionExist: any; getAccessTokenPayloadSecurely: any; getGlobalClaimValidators: any; getInvalidClaimsFromResponse: any; getUserId: any; shouldDoInterceptionBasedOnUrl: any; signOut: any; validateClaims: any }

    Type declaration

    • addAxiosInterceptors:function
      • addAxiosInterceptors(input: { axiosInstance: any; userContext: any }): void
      • Parameters

        • input: { axiosInstance: any; userContext: any }
          • axiosInstance: any
          • userContext: any

        Returns void

    • addFetchInterceptorsAndReturnModifiedFetch:function
      • addFetchInterceptorsAndReturnModifiedFetch(input: { originalFetch: any; userContext: any }): ((input: URL | RequestInfo, init?: RequestInit) => Promise<Response>)
      • Parameters

        • input: { originalFetch: any; userContext: any }
          • originalFetch: any
          • userContext: any

        Returns ((input: URL | RequestInfo, init?: RequestInit) => Promise<Response>)

          • (input: URL | RequestInfo, init?: RequestInit): Promise<Response>
          • Parameters

            • input: URL | RequestInfo
            • Optional init: RequestInit

            Returns Promise<Response>

    • addXMLHttpRequestInterceptor:function
      • addXMLHttpRequestInterceptor(input: { userContext: any }): void
      • Parameters

        • input: { userContext: any }
          • userContext: any

        Returns void

    • doesSessionExist:function
      • doesSessionExist(input: { userContext: any }): Promise<boolean>
      • Parameters

        • input: { userContext: any }
          • userContext: any

        Returns Promise<boolean>

    • getAccessTokenPayloadSecurely:function
      • getAccessTokenPayloadSecurely(input: { userContext: any }): Promise<any>
      • Parameters

        • input: { userContext: any }
          • userContext: any

        Returns Promise<any>

    • getGlobalClaimValidators:function
    • getInvalidClaimsFromResponse:function
      • getInvalidClaimsFromResponse(input: { response: { data: any } | Response; userContext: any }): Promise<ClaimValidationError[]>
    • getUserId:function
      • getUserId(input: { userContext: any }): Promise<string>
      • Parameters

        • input: { userContext: any }
          • userContext: any

        Returns Promise<string>

    • shouldDoInterceptionBasedOnUrl:function
      • shouldDoInterceptionBasedOnUrl(toCheckUrl: string, apiDomain: string, sessionTokenBackendDomain: undefined | string): boolean
      • Parameters

        • toCheckUrl: string
        • apiDomain: string
        • sessionTokenBackendDomain: undefined | string

        Returns boolean

    • signOut:function
      • signOut(input: { userContext: any }): Promise<void>
      • Parameters

        • input: { userContext: any }
          • userContext: any

        Returns Promise<void>

    • validateClaims:function
    SessionClaim<ValueType>: { getLastFetchedTime: any; getValueFromPayload: any; refresh: any }

    Type Parameters

    • ValueType

    Type declaration

    • getLastFetchedTime:function
      • getLastFetchedTime(payload: any, _userContext?: any): undefined | number
      • Parameters

        • payload: any
        • Optional _userContext: any

        Returns undefined | number

    • getValueFromPayload:function
      • getValueFromPayload(payload: any, _userContext?: any): undefined | ValueType
      • Parameters

        • payload: any
        • Optional _userContext: any

        Returns undefined | ValueType

    • refresh:function
      • refresh(userContext: any): Promise<void>
    SessionClaimValidator: { id: string; refresh: any; shouldRefresh: any; validate: any }

    Type declaration

    • Readonly id: string
    • refresh:function
      • refresh(userContext: any): Promise<void>
      • +
      • Optional onHandleEvent?: EventHandler
      • Optional override?: { functions?: any }
      • Optional postAPIHook?: RecipePostAPIHookFunction
      • Optional preAPIHook?: RecipePreAPIHookFunction
      • Optional sessionExpiredStatusCode?: number
      • Optional sessionTokenBackendDomain?: string
      • Optional sessionTokenFrontendDomain?: string
      • Optional tokenTransferMethod?: "cookie" | "header"
      • Optional windowHandler?: WindowHandlerInput
    RecipeInterface: { addAxiosInterceptors: any; addFetchInterceptorsAndReturnModifiedFetch: any; addXMLHttpRequestInterceptor: any; doesSessionExist: any; getAccessTokenPayloadSecurely: any; getClockSkewInMillis: any; getGlobalClaimValidators: any; getInvalidClaimsFromResponse: any; getUserId: any; shouldDoInterceptionBasedOnUrl: any; signOut: any; validateClaims: any }

    Type declaration

    • addAxiosInterceptors:function
      • addAxiosInterceptors(input: { axiosInstance: any; userContext: any }): void
      • Parameters

        • input: { axiosInstance: any; userContext: any }
          • axiosInstance: any
          • userContext: any

        Returns void

    • addFetchInterceptorsAndReturnModifiedFetch:function
      • addFetchInterceptorsAndReturnModifiedFetch(input: { originalFetch: any; userContext: any }): ((input: URL | RequestInfo, init?: RequestInit) => Promise<Response>)
      • Parameters

        • input: { originalFetch: any; userContext: any }
          • originalFetch: any
          • userContext: any

        Returns ((input: URL | RequestInfo, init?: RequestInit) => Promise<Response>)

          • (input: URL | RequestInfo, init?: RequestInit): Promise<Response>
          • Parameters

            • input: URL | RequestInfo
            • Optional init: RequestInit

            Returns Promise<Response>

    • addXMLHttpRequestInterceptor:function
      • addXMLHttpRequestInterceptor(input: { userContext: any }): void
      • Parameters

        • input: { userContext: any }
          • userContext: any

        Returns void

    • doesSessionExist:function
      • doesSessionExist(input: { userContext: any }): Promise<boolean>
      • Parameters

        • input: { userContext: any }
          • userContext: any

        Returns Promise<boolean>

    • getAccessTokenPayloadSecurely:function
      • getAccessTokenPayloadSecurely(input: { userContext: any }): Promise<any>
      • Parameters

        • input: { userContext: any }
          • userContext: any

        Returns Promise<any>

    • getClockSkewInMillis:function
      • getClockSkewInMillis(params: { accessTokenPayload: any; responseHeaders: Headers }): number
      • Parameters

        • params: { accessTokenPayload: any; responseHeaders: Headers }
          • accessTokenPayload: any
          • responseHeaders: Headers

        Returns number

    • getGlobalClaimValidators:function
    • getInvalidClaimsFromResponse:function
      • getInvalidClaimsFromResponse(input: { response: { data: any } | Response; userContext: any }): Promise<ClaimValidationError[]>
    • getUserId:function
      • getUserId(input: { userContext: any }): Promise<string>
      • Parameters

        • input: { userContext: any }
          • userContext: any

        Returns Promise<string>

    • shouldDoInterceptionBasedOnUrl:function
      • shouldDoInterceptionBasedOnUrl(toCheckUrl: string, apiDomain: string, sessionTokenBackendDomain: undefined | string): boolean
      • Parameters

        • toCheckUrl: string
        • apiDomain: string
        • sessionTokenBackendDomain: undefined | string

        Returns boolean

    • signOut:function
      • signOut(input: { userContext: any }): Promise<void>
      • Parameters

        • input: { userContext: any }
          • userContext: any

        Returns Promise<void>

    • validateClaims:function
    SessionClaim<ValueType>: { getLastFetchedTime: any; getValueFromPayload: any; refresh: any }

    Type Parameters

    • ValueType

    Type declaration

    • getLastFetchedTime:function
      • getLastFetchedTime(payload: any, _userContext?: any): undefined | number
      • Parameters

        • payload: any
        • Optional _userContext: any

        Returns undefined | number

    • getValueFromPayload:function
      • getValueFromPayload(payload: any, _userContext?: any): undefined | ValueType
      • Parameters

        • payload: any
        • Optional _userContext: any

        Returns undefined | ValueType

    • refresh:function
      • refresh(userContext: any): Promise<void>
    SessionClaimValidator: { id: string; refresh: any; shouldRefresh: any; validate: any }

    Type declaration

    • Readonly id: string
    • refresh:function
      • refresh(userContext: any): Promise<void>
      • Makes an API call that will refresh the claim in the token.

        -

        Parameters

        • userContext: any

        Returns Promise<void>

    • shouldRefresh:function
      • shouldRefresh(accessTokenPayload: any, userContext: any): boolean | Promise<boolean>
    • shouldRefresh:function
      • shouldRefresh(accessTokenPayload: any, userContext: any): boolean | Promise<boolean>
      • Decides if we need to refresh the claim value before checking the payload with validate. E.g.: if the information in the payload is expired, or is not sufficient for this validator.

        -

        Parameters

        • accessTokenPayload: any
        • userContext: any

        Returns boolean | Promise<boolean>

    • validate:function
      • +

        Parameters

        • accessTokenPayload: any
        • userContext: any

        Returns boolean | Promise<boolean>

    • validate:function

    Functions

    • addAxiosInterceptors(axiosInstance: any, userContext?: any): void
    • deprecated

      Parameters

      • axiosInstance: any
      • Optional userContext: any

      Returns void

    • attemptRefreshingSession(): Promise<boolean>
    • doesSessionExist(input?: { userContext?: any }): Promise<boolean>
    • Parameters

      • Optional input: { userContext?: any }
        • Optional userContext?: any

      Returns Promise<boolean>

    • getAccessToken(input?: { userContext?: any }): Promise<undefined | string>
    • Parameters

      • Optional input: { userContext?: any }
        • Optional userContext?: any

      Returns Promise<undefined | string>

    • getAccessTokenPayloadSecurely(input?: { userContext?: any }): Promise<any>
    • Parameters

      • Optional input: { userContext?: any }
        • Optional userContext?: any

      Returns Promise<any>

    • getClaimValue<T>(input: { claim: SessionClaim<T>; userContext?: any }): Promise<undefined | T>
    • getInvalidClaimsFromResponse(input: { response: Response | { data: any }; userContext?: any }): Promise<ClaimValidationError[]>
    • Parameters

      • input: { response: Response | { data: any }; userContext?: any }
        • response: Response | { data: any }
        • Optional userContext?: any

      Returns Promise<ClaimValidationError[]>

    • getUserId(input?: { userContext?: any }): Promise<string>
    • Parameters

      • Optional input: { userContext?: any }
        • Optional userContext?: any

      Returns Promise<string>

    • signOut(input?: { userContext?: any }): Promise<void>
    • Parameters

      • Optional input: { userContext?: any }
        • Optional userContext?: any

      Returns Promise<void>

    Legend

    • Constructor
    • Property
    • Method
    • Inherited property
    • Inherited method
    • Static property
    • Static method

    Settings

    Theme

    Generated using TypeDoc

    \ No newline at end of file +

    Parameters

    • accessTokenPayload: any
    • userContext: any

    Returns ClaimValidationResult | Promise<ClaimValidationResult>

Functions

  • addAxiosInterceptors(axiosInstance: any, userContext?: any): void
  • deprecated

    Parameters

    • axiosInstance: any
    • Optional userContext: any

    Returns void

  • attemptRefreshingSession(): Promise<boolean>
  • doesSessionExist(input?: { userContext?: any }): Promise<boolean>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<boolean>

  • getAccessToken(input?: { userContext?: any }): Promise<undefined | string>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<undefined | string>

  • getAccessTokenPayloadSecurely(input?: { userContext?: any }): Promise<any>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<any>

  • getClaimValue<T>(input: { claim: SessionClaim<T>; userContext?: any }): Promise<undefined | T>
  • getInvalidClaimsFromResponse(input: { response: Response | { data: any }; userContext?: any }): Promise<ClaimValidationError[]>
  • Parameters

    • input: { response: Response | { data: any }; userContext?: any }
      • response: Response | { data: any }
      • Optional userContext?: any

    Returns Promise<ClaimValidationError[]>

  • getUserId(input?: { userContext?: any }): Promise<string>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<string>

  • signOut(input?: { userContext?: any }): Promise<void>
  • Parameters

    • Optional input: { userContext?: any }
      • Optional userContext?: any

    Returns Promise<void>

Legend

  • Constructor
  • Property
  • Method
  • Inherited property
  • Inherited method
  • Static property
  • Static method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/lib/build/axios.js b/lib/build/axios.js index 2660a368..2395a1aa 100644 --- a/lib/build/axios.js +++ b/lib/build/axios.js @@ -798,7 +798,7 @@ function setAuthorizationHeaderIfRequired(requestConfig) { } function saveTokensFromHeaders(response) { return __awaiter(this, void 0, void 0, function () { - var refreshToken, accessToken, frontToken, antiCsrfToken, tok; + var refreshToken, accessToken, frontToken, responseHeaders_1, antiCsrfToken, tok; return __generator(this, function (_b) { switch (_b.label) { case 0: @@ -825,6 +825,18 @@ function saveTokensFromHeaders(response) { return [4 /*yield*/, fetch_1.FrontToken.setItem(frontToken)]; case 5: _b.sent(); + responseHeaders_1 = new Headers(); + Object.entries(response.headers).forEach(function (_b) { + var key = _b[0], + value = _b[1]; + Array.isArray(value) + ? value.forEach(function (item) { + return responseHeaders_1.append(key, item); + }) + : responseHeaders_1.append(key, value); + }); + (0, + fetch_1.updateClockSkewUsingFrontToken)({ frontToken: frontToken, responseHeaders: responseHeaders_1 }); _b.label = 6; case 6: antiCsrfToken = response.headers["anti-csrf"]; diff --git a/lib/build/claims/primitiveArrayClaim.js b/lib/build/claims/primitiveArrayClaim.js index a3e877d4..06d7a5e0 100644 --- a/lib/build/claims/primitiveArrayClaim.js +++ b/lib/build/claims/primitiveArrayClaim.js @@ -132,6 +132,7 @@ var __generator = }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PrimitiveArrayClaim = void 0; +var dateProvider_1 = require("../utils/dateProvider"); var PrimitiveArrayClaim = /** @class */ (function () { function PrimitiveArrayClaim(config) { var _this = this; @@ -140,6 +141,7 @@ var PrimitiveArrayClaim = /** @class */ (function () { if (maxAgeInSeconds === void 0) { maxAgeInSeconds = _this.defaultMaxAgeInSeconds; } + var DateProvider = dateProvider_1.default.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : _this.id, refresh: function (ctx) { @@ -149,7 +151,8 @@ var PrimitiveArrayClaim = /** @class */ (function () { return ( _this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[_this.id].t < Date.now() - maxAgeInSeconds * 1000) + (maxAgeInSeconds !== undefined && + payload[_this.id].t < DateProvider.now() - maxAgeInSeconds * 1000) ); }, validate: function (payload, ctx) { @@ -170,7 +173,7 @@ var PrimitiveArrayClaim = /** @class */ (function () { } ]; } - ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)) / 1000; + ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return [ 2 /*return*/, @@ -207,6 +210,7 @@ var PrimitiveArrayClaim = /** @class */ (function () { if (maxAgeInSeconds === void 0) { maxAgeInSeconds = _this.defaultMaxAgeInSeconds; } + var DateProvider = dateProvider_1.default.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : _this.id, refresh: function (ctx) { @@ -216,7 +220,8 @@ var PrimitiveArrayClaim = /** @class */ (function () { return ( _this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[_this.id].t < Date.now() - maxAgeInSeconds * 1000) + (maxAgeInSeconds !== undefined && + payload[_this.id].t < DateProvider.now() - maxAgeInSeconds * 1000) ); }, validate: function (payload, ctx) { @@ -237,7 +242,7 @@ var PrimitiveArrayClaim = /** @class */ (function () { } ]; } - ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)) / 1000; + ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return [ 2 /*return*/, @@ -274,6 +279,7 @@ var PrimitiveArrayClaim = /** @class */ (function () { if (maxAgeInSeconds === void 0) { maxAgeInSeconds = _this.defaultMaxAgeInSeconds; } + var DateProvider = dateProvider_1.default.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : _this.id, refresh: function (ctx) { @@ -283,7 +289,8 @@ var PrimitiveArrayClaim = /** @class */ (function () { return ( _this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[_this.id].t < Date.now() - maxAgeInSeconds * 1000) + (maxAgeInSeconds !== undefined && + payload[_this.id].t < DateProvider.now() - maxAgeInSeconds * 1000) ); }, validate: function (payload, ctx) { @@ -304,7 +311,7 @@ var PrimitiveArrayClaim = /** @class */ (function () { } ]; } - ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)) / 1000; + ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return [ 2 /*return*/, @@ -344,6 +351,7 @@ var PrimitiveArrayClaim = /** @class */ (function () { if (maxAgeInSeconds === void 0) { maxAgeInSeconds = _this.defaultMaxAgeInSeconds; } + var DateProvider = dateProvider_1.default.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : _this.id, refresh: function (ctx) { @@ -353,7 +361,8 @@ var PrimitiveArrayClaim = /** @class */ (function () { return ( _this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[_this.id].t < Date.now() - maxAgeInSeconds * 1000) + (maxAgeInSeconds !== undefined && + payload[_this.id].t < DateProvider.now() - maxAgeInSeconds * 1000) ); }, validate: function (payload, ctx) { @@ -374,7 +383,7 @@ var PrimitiveArrayClaim = /** @class */ (function () { } ]; } - ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)) / 1000; + ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return [ 2 /*return*/, @@ -414,6 +423,7 @@ var PrimitiveArrayClaim = /** @class */ (function () { if (maxAgeInSeconds === void 0) { maxAgeInSeconds = _this.defaultMaxAgeInSeconds; } + var DateProvider = dateProvider_1.default.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : _this.id, refresh: function (ctx) { @@ -423,7 +433,8 @@ var PrimitiveArrayClaim = /** @class */ (function () { return ( _this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[_this.id].t < Date.now() - maxAgeInSeconds * 1000) + (maxAgeInSeconds !== undefined && + payload[_this.id].t < DateProvider.now() - maxAgeInSeconds * 1000) ); }, validate: function (payload, ctx) { @@ -444,7 +455,7 @@ var PrimitiveArrayClaim = /** @class */ (function () { } ]; } - ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)) / 1000; + ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return [ 2 /*return*/, diff --git a/lib/build/claims/primitiveClaim.js b/lib/build/claims/primitiveClaim.js index 1c87679c..c84d9f2b 100644 --- a/lib/build/claims/primitiveClaim.js +++ b/lib/build/claims/primitiveClaim.js @@ -1,6 +1,7 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PrimitiveClaim = void 0; +var dateProvider_1 = require("../utils/dateProvider"); var PrimitiveClaim = /** @class */ (function () { function PrimitiveClaim(config) { var _this = this; @@ -9,6 +10,7 @@ var PrimitiveClaim = /** @class */ (function () { if (maxAgeInSeconds === void 0) { maxAgeInSeconds = _this.defaultMaxAgeInSeconds; } + var DateProvider = dateProvider_1.default.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : _this.id, refresh: function (ctx) { @@ -18,7 +20,8 @@ var PrimitiveClaim = /** @class */ (function () { return ( _this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[_this.id].t < Date.now() - maxAgeInSeconds * 1000) + (maxAgeInSeconds !== undefined && + payload[_this.id].t < DateProvider.now() - maxAgeInSeconds * 1000) ); }, validate: function (payload, ctx) { @@ -29,7 +32,7 @@ var PrimitiveClaim = /** @class */ (function () { reason: { message: "value does not exist", expectedValue: val, actualValue: claimVal } }; } - var ageInSeconds = (Date.now() - _this.getLastFetchedTime(payload, ctx)) / 1000; + var ageInSeconds = (DateProvider.now() - _this.getLastFetchedTime(payload, ctx)) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return { isValid: false, diff --git a/lib/build/fetch.d.ts b/lib/build/fetch.d.ts index 713a2a24..44fdadc9 100644 --- a/lib/build/fetch.d.ts +++ b/lib/build/fetch.d.ts @@ -64,3 +64,10 @@ export declare function setAntiCSRF(antiCSRFToken: string | undefined): Promise< export declare function getFrontToken(): Promise; export declare function setFrontToken(frontToken: string | undefined): Promise; export declare function fireSessionUpdateEventsIfNecessary(wasLoggedIn: boolean, status: number, frontTokenHeaderFromResponse: string | null | undefined): void; +/** + * Updates the clock skew based on the provided frontToken and responseHeaders. + */ +export declare const updateClockSkewUsingFrontToken: ({ frontToken, responseHeaders }: { + frontToken: string | undefined | null; + responseHeaders: Headers; +}) => void; diff --git a/lib/build/fetch.js b/lib/build/fetch.js index 542fef50..6ce5299f 100644 --- a/lib/build/fetch.js +++ b/lib/build/fetch.js @@ -145,7 +145,8 @@ var __generator = } }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.fireSessionUpdateEventsIfNecessary = +exports.updateClockSkewUsingFrontToken = + exports.fireSessionUpdateEventsIfNecessary = exports.setFrontToken = exports.getFrontToken = exports.setAntiCSRF = @@ -180,6 +181,7 @@ var cookieHandler_1 = require("./utils/cookieHandler"); var windowHandler_1 = require("./utils/windowHandler"); var lockFactory_1 = require("./utils/lockFactory"); var logger_1 = require("./logger"); +var dateProvider_1 = require("./utils/dateProvider"); var AntiCsrfToken = /** @class */ (function () { function AntiCsrfToken() {} AntiCsrfToken.getToken = function (associatedAccessTokenUpdate) { @@ -1015,6 +1017,7 @@ function setToken(tokenType, value) { if (value !== "") { (0, logger_1.logDebugMessage)("setToken: saved ".concat(tokenType, " token into cookies")); // We save the tokens with a 100-year expiration time + // We have to use the client side system clock here, because the cookie expiration will be counted based on that return storeInCookies(name, value, Date.now() + 3153600000); } else { (0, logger_1.logDebugMessage)("setToken: cleared ".concat(tokenType, " token from cookies")); @@ -1162,6 +1165,8 @@ function saveTokensFromHeaders(response) { return [4 /*yield*/, FrontToken.setItem(frontToken)]; case 5: _b.sent(); + (0, + exports.updateClockSkewUsingFrontToken)({ frontToken: frontToken, responseHeaders: response.headers }); _b.label = 6; case 6: antiCsrfToken = response.headers.get("anti-csrf"); @@ -1413,3 +1418,25 @@ function fireSessionUpdateEventsIfNecessary(wasLoggedIn, status, frontTokenHeade } } exports.fireSessionUpdateEventsIfNecessary = fireSessionUpdateEventsIfNecessary; +/** + * Updates the clock skew based on the provided frontToken and responseHeaders. + */ +var updateClockSkewUsingFrontToken = function (_b) { + var frontToken = _b.frontToken, + responseHeaders = _b.responseHeaders; + (0, logger_1.logDebugMessage)("updateClockSkewUsingFrontToken: frontToken: " + frontToken); + if (frontToken === null || frontToken === undefined || frontToken === "remove") { + (0, logger_1.logDebugMessage)( + "updateClockSkewUsingFrontToken: the access token payload wasn't updated or is being removed, skipping clock skew update" + ); + return; + } + var frontTokenPayload = parseFrontToken(frontToken); + var clockSkewInMillis = AuthHttpRequest.recipeImpl.getClockSkewInMillis({ + accessTokenPayload: frontTokenPayload.up, + responseHeaders: responseHeaders + }); + dateProvider_1.default.getReferenceOrThrow().dateProvider.setClientClockSkewInMillis(clockSkewInMillis); + (0, logger_1.logDebugMessage)("updateClockSkewUsingFrontToken: Client clock synchronized successfully"); +}; +exports.updateClockSkewUsingFrontToken = updateClockSkewUsingFrontToken; diff --git a/lib/build/index.js b/lib/build/index.js index ca4fae76..dce13146 100644 --- a/lib/build/index.js +++ b/lib/build/index.js @@ -169,11 +169,13 @@ var windowHandler_1 = require("./utils/windowHandler"); var lockFactory_1 = require("./utils/lockFactory"); var sessionClaimValidatorStore_1 = require("./utils/sessionClaimValidatorStore"); var logger_1 = require("./logger"); +var dateProvider_1 = require("./utils/dateProvider"); var AuthHttpRequest = /** @class */ (function () { function AuthHttpRequest() {} AuthHttpRequest.init = function (options) { cookieHandler_1.default.init(options.cookieHandler); windowHandler_1.default.init(options.windowHandler); + dateProvider_1.default.init(options.dateProvider); lockFactory_1.default.init( options.lockFactory, windowHandler_1.default.getReferenceOrThrow().windowHandler.localStorage diff --git a/lib/build/recipeImplementation.js b/lib/build/recipeImplementation.js index 49f8bcd7..47e6f78b 100644 --- a/lib/build/recipeImplementation.js +++ b/lib/build/recipeImplementation.js @@ -152,6 +152,7 @@ var logger_1 = require("./logger"); var error_1 = require("./error"); var xmlhttprequest_1 = require("./xmlhttprequest"); var utils_1 = require("./utils"); +var dateProvider_1 = require("./utils/dateProvider"); function RecipeImplementation(recipeImplInput) { return { addXMLHttpRequestInterceptor: function (_) { @@ -259,7 +260,8 @@ function RecipeImplementation(recipeImplInput) { if (tokenInfo === undefined) { throw new Error("No session exists"); } - if (!(tokenInfo.ate < Date.now())) return [3 /*break*/, 5]; + if (!(tokenInfo.ate < dateProvider_1.default.getReferenceOrThrow().dateProvider.now())) + return [3 /*break*/, 5]; (0, logger_1.logDebugMessage)("getAccessTokenPayloadSecurely: access token expired. Refreshing session"); return [4 /*yield*/, fetch_1.default.attemptRefreshingSession()]; @@ -299,7 +301,8 @@ function RecipeImplementation(recipeImplInput) { (0, logger_1.logDebugMessage)("doesSessionExist: access token does not exist locally"); return [2 /*return*/, false]; } - if (!(tokenInfo.ate < Date.now())) return [3 /*break*/, 4]; + if (!(tokenInfo.ate < dateProvider_1.default.getReferenceOrThrow().dateProvider.now())) + return [3 /*break*/, 4]; (0, logger_1.logDebugMessage)("doesSessionExist: access token expired. Refreshing session"); return [4 /*yield*/, (0, fetch_1.getLocalSessionState)(false)]; case 2: @@ -535,6 +538,22 @@ function RecipeImplementation(recipeImplInput) { return domain === normalisedsessionDomain; } } + }, + getClockSkewInMillis: function (_a) { + var accessTokenPayload = _a.accessTokenPayload; + (0, logger_1.logDebugMessage)("getClockSkewInMillis: called"); + var tokenIssuedAt = + accessTokenPayload === null || accessTokenPayload === void 0 ? void 0 : accessTokenPayload.iat; + if (tokenIssuedAt === undefined || typeof tokenIssuedAt !== "number") { + (0, logger_1.logDebugMessage)( + "getClockSkewInMillis: payload iat is undefined or not a number. This may happen due to an unsupported backend sdk. Returning 0" + ); + return 0; + } + var estimatedServerTimeNow = tokenIssuedAt * 1000; + var clockSkewInMillis = estimatedServerTimeNow - Date.now(); + (0, logger_1.logDebugMessage)("getClockSkewInMillis: returning " + clockSkewInMillis); + return clockSkewInMillis; } }; } diff --git a/lib/build/types.d.ts b/lib/build/types.d.ts index 1ef7c721..2ebf3eab 100644 --- a/lib/build/types.d.ts +++ b/lib/build/types.d.ts @@ -2,6 +2,7 @@ import OverrideableBuilder from "supertokens-js-override"; import { CookieHandlerInput } from "./utils/cookieHandler/types"; import { WindowHandlerInput } from "./utils/windowHandler/types"; import { LockFactory } from "./utils/lockFactory/types"; +import { DateProviderInput } from "./utils/dateProvider/types"; export declare type Event = { action: "SIGN_OUT" | "REFRESH_SESSION" | "SESSION_CREATED" | "ACCESS_TOKEN_PAYLOAD_UPDATED"; userContext: any; @@ -35,6 +36,7 @@ export declare type InputType = { sessionTokenBackendDomain?: string; cookieHandler?: CookieHandlerInput; windowHandler?: WindowHandlerInput; + dateProvider?: DateProviderInput; preAPIHook?: RecipePreAPIHookFunction; postAPIHook?: RecipePostAPIHookFunction; onHandleEvent?: EventHandler; @@ -123,6 +125,10 @@ export declare type RecipeInterface = { userContext: any; }): SessionClaimValidator[]; shouldDoInterceptionBasedOnUrl(toCheckUrl: string, apiDomain: string, sessionTokenBackendDomain: string | undefined): boolean; + getClockSkewInMillis(params: { + accessTokenPayload: any; + responseHeaders: Headers; + }): number; }; export declare type ClaimValidationResult = { isValid: true; diff --git a/lib/build/utils/dateProvider/defaultImplementation.d.ts b/lib/build/utils/dateProvider/defaultImplementation.d.ts new file mode 100644 index 00000000..84aab9e4 --- /dev/null +++ b/lib/build/utils/dateProvider/defaultImplementation.d.ts @@ -0,0 +1,10 @@ +export declare class DateProvider { + private static instance?; + static readonly CLOCK_SKEW_KEY = "__st_clockSkewInMillis"; + private clockSkewInMillis; + static init(): void; + static getReferenceOrThrow(): DateProvider; + setClientClockSkewInMillis(clockSkewInMillis: number): void; + getClientClockSkewInMillis(): number; + now(): number; +} diff --git a/lib/build/utils/dateProvider/defaultImplementation.js b/lib/build/utils/dateProvider/defaultImplementation.js new file mode 100644 index 00000000..568a73dc --- /dev/null +++ b/lib/build/utils/dateProvider/defaultImplementation.js @@ -0,0 +1,67 @@ +"use strict"; +/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DateProvider = void 0; +var windowHandler_1 = require("../windowHandler"); +var DateProvider = /** @class */ (function () { + function DateProvider() { + this.clockSkewInMillis = 0; + } + // The static init method is used to create a singleton instance of DateProvider, + // as we require access to localStorage for initializing clockSkewInMillis. + // Access to localStorage is available only after WindowHandlerReference is initialized. + // We took the following considerations into account while designing the cache implementation: + // 1. Cache cleared with an active session while the FE clock is wrong (e.g., Safari clears localStorage after 7 days). + // - Resets clockSkewInMillis to 0, corrected in the next front token update. + // 2. Outdated info in the cache making the clock point into the future. + // - May cause validator.shouldRefresh to always be true, corrected by the next front token update. + // 3. Outdated info in the cache making the clock point into the past. + // - May cause validator.shouldRefresh to always be false, fixed during the next token refresh (worst case). + // 4. Other tabs (on different subdomains) refreshing the session with outdated info in the cache. + // - Each subdomain has its own cache (localStorage), similar implications to cases 2 and 3. + // 5. Other tabs refreshing the session with outdated info in memory. + // - May cause validator.shouldRefresh to always be true or false, fixed in the next front token update. + DateProvider.init = function () { + if (DateProvider.instance !== undefined) { + return; + } + DateProvider.instance = new DateProvider(); + var localStorage = windowHandler_1.default.getReferenceOrThrow().windowHandler.localStorage; + var stored = localStorage.getItemSync(DateProvider.CLOCK_SKEW_KEY); + var clockSkewInMillis = stored !== null ? parseInt(stored, 10) : 0; + DateProvider.instance.setClientClockSkewInMillis(clockSkewInMillis); + }; + DateProvider.getReferenceOrThrow = function () { + if (DateProvider.instance === undefined) { + throw new Error("DateProvider must be initialized before calling this method."); + } + return DateProvider.instance; + }; + DateProvider.prototype.setClientClockSkewInMillis = function (clockSkewInMillis) { + this.clockSkewInMillis = clockSkewInMillis; + var localStorage = windowHandler_1.default.getReferenceOrThrow().windowHandler.localStorage; + localStorage.setItemSync(DateProvider.CLOCK_SKEW_KEY, String(clockSkewInMillis)); + }; + DateProvider.prototype.getClientClockSkewInMillis = function () { + return this.clockSkewInMillis; + }; + DateProvider.prototype.now = function () { + return Date.now() + this.getClientClockSkewInMillis(); + }; + DateProvider.CLOCK_SKEW_KEY = "__st_clockSkewInMillis"; + return DateProvider; +})(); +exports.DateProvider = DateProvider; diff --git a/lib/build/utils/dateProvider/index.d.ts b/lib/build/utils/dateProvider/index.d.ts new file mode 100644 index 00000000..508c99bc --- /dev/null +++ b/lib/build/utils/dateProvider/index.d.ts @@ -0,0 +1,16 @@ +import { DateProviderInterface, DateProviderInput } from "./types"; +/** + * A utility class for managing a reference to a DateProviderInterface, allowing customization of time-related operations. + * This class is designed to address clock skew issues between the server and client by providing a consistent mechanism + * for obtaining current time adjusted for clock skew. + * + * @class DateProviderReference + */ +export default class DateProviderReference { + private static instance?; + dateProvider: DateProviderInterface; + constructor(dateProviderInput?: DateProviderInput); + static init(dateProviderInput?: DateProviderInput): void; + static getReferenceOrThrow(): DateProviderReference; +} +export { DateProviderReference }; diff --git a/lib/build/utils/dateProvider/index.js b/lib/build/utils/dateProvider/index.js new file mode 100644 index 00000000..18ff2077 --- /dev/null +++ b/lib/build/utils/dateProvider/index.js @@ -0,0 +1,55 @@ +"use strict"; +/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DateProviderReference = void 0; +var defaultImplementation_1 = require("./defaultImplementation"); +/** + * A utility class for managing a reference to a DateProviderInterface, allowing customization of time-related operations. + * This class is designed to address clock skew issues between the server and client by providing a consistent mechanism + * for obtaining current time adjusted for clock skew. + * + * @class DateProviderReference + */ +var DateProviderReference = /** @class */ (function () { + function DateProviderReference(dateProviderInput) { + var dateProviderFunc = function (original) { + return original; + }; + if (dateProviderInput !== undefined) { + dateProviderFunc = dateProviderInput; + } + // Initialize the DateProvider implementation by calling the init method. + // This is done to ensure that the WindowHandler is initialized before we instantiate the DateProvider. + defaultImplementation_1.DateProvider.init(); + var defaultDateProviderImplementation = defaultImplementation_1.DateProvider.getReferenceOrThrow(); + this.dateProvider = dateProviderFunc(defaultDateProviderImplementation); + } + DateProviderReference.init = function (dateProviderInput) { + if (DateProviderReference.instance !== undefined) { + return; + } + DateProviderReference.instance = new DateProviderReference(dateProviderInput); + }; + DateProviderReference.getReferenceOrThrow = function () { + if (DateProviderReference.instance === undefined) { + throw new Error("SuperTokensDateProvider must be initialized before calling this method."); + } + return DateProviderReference.instance; + }; + return DateProviderReference; +})(); +exports.DateProviderReference = DateProviderReference; +exports.default = DateProviderReference; diff --git a/lib/build/utils/dateProvider/types.d.ts b/lib/build/utils/dateProvider/types.d.ts new file mode 100644 index 00000000..9f3f8beb --- /dev/null +++ b/lib/build/utils/dateProvider/types.d.ts @@ -0,0 +1,6 @@ +export interface DateProviderInterface { + now(): number; + setClientClockSkewInMillis(clockSkewInMillis: number): void; + getClientClockSkewInMillis(): number; +} +export declare type DateProviderInput = (original: DateProviderInterface) => DateProviderInterface; diff --git a/lib/build/utils/dateProvider/types.js b/lib/build/utils/dateProvider/types.js new file mode 100644 index 00000000..9f123731 --- /dev/null +++ b/lib/build/utils/dateProvider/types.js @@ -0,0 +1,16 @@ +"use strict"; +/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/lib/build/version.d.ts b/lib/build/version.d.ts index c5ac7b3f..7f8495e0 100644 --- a/lib/build/version.d.ts +++ b/lib/build/version.d.ts @@ -1,2 +1,2 @@ -export declare const package_version = "17.0.5"; +export declare const package_version = "18.0.0"; export declare const supported_fdi: string[]; diff --git a/lib/build/version.js b/lib/build/version.js index 503d8d9d..c5d9deee 100644 --- a/lib/build/version.js +++ b/lib/build/version.js @@ -15,5 +15,5 @@ exports.supported_fdi = exports.package_version = void 0; * License for the specific language governing permissions and limitations * under the License. */ -exports.package_version = "17.0.5"; +exports.package_version = "18.0.0"; exports.supported_fdi = ["1.16", "1.17", "1.18"]; diff --git a/lib/build/xmlhttprequest.js b/lib/build/xmlhttprequest.js index 563b4b7b..43386e91 100644 --- a/lib/build/xmlhttprequest.js +++ b/lib/build/xmlhttprequest.js @@ -824,6 +824,7 @@ function saveTokensFromHeaders(headers) { return [4 /*yield*/, fetch_1.FrontToken.setItem(frontToken)]; case 5: _a.sent(); + (0, fetch_1.updateClockSkewUsingFrontToken)({ frontToken: frontToken, responseHeaders: headers }); _a.label = 6; case 6: antiCsrfToken = headers.get("anti-csrf"); diff --git a/lib/ts/axios.ts b/lib/ts/axios.ts index 3771c2d0..1be9d296 100644 --- a/lib/ts/axios.ts +++ b/lib/ts/axios.ts @@ -23,7 +23,8 @@ import AuthHttpRequestFetch, { onInvalidClaimResponse, setToken, fireSessionUpdateEventsIfNecessary, - getTokenForHeaderAuth + getTokenForHeaderAuth, + updateClockSkewUsingFrontToken } from "./fetch"; import { PROCESS_STATE, ProcessState } from "./processState"; import WindowHandlerReference from "./utils/windowHandler"; @@ -556,6 +557,15 @@ async function saveTokensFromHeaders(response: AxiosResponse) { if (frontToken !== undefined) { logDebugMessage("doRequest: Setting sFrontToken: " + frontToken); await FrontToken.setItem(frontToken); + + // Converting axios headers to fetch headers to pass to updateClockSkewUsingFrontToken + const responseHeaders = new Headers(); + Object.entries(response.headers).forEach(([key, value]) => { + Array.isArray(value) + ? value.forEach(item => responseHeaders.append(key, item)) + : responseHeaders.append(key, value); + }); + updateClockSkewUsingFrontToken({ frontToken, responseHeaders }); } const antiCsrfToken = response.headers["anti-csrf"]; diff --git a/lib/ts/claims/primitiveArrayClaim.ts b/lib/ts/claims/primitiveArrayClaim.ts index a6f53d4e..f245a331 100644 --- a/lib/ts/claims/primitiveArrayClaim.ts +++ b/lib/ts/claims/primitiveArrayClaim.ts @@ -1,4 +1,5 @@ import { SessionClaimValidator } from "../types"; +import DateProviderReference from "../utils/dateProvider"; export type PrimitiveArrayClaimConfig = { id: string; @@ -31,13 +32,14 @@ export class PrimitiveArrayClaim { maxAgeInSeconds: number | undefined = this.defaultMaxAgeInSeconds, id?: string ): SessionClaimValidator => { + const DateProvider = DateProviderReference.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : this.id, refresh: ctx => this.refresh(ctx), shouldRefresh: (payload, ctx) => this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[this.id].t < Date.now() - maxAgeInSeconds * 1000), + (maxAgeInSeconds !== undefined && payload[this.id].t < DateProvider.now() - maxAgeInSeconds * 1000), validate: async (payload, ctx) => { const claimVal = this.getValueFromPayload(payload, ctx); if (claimVal === undefined) { @@ -46,7 +48,7 @@ export class PrimitiveArrayClaim { reason: { message: "value does not exist", expectedToInclude: val, actualValue: claimVal } }; } - const ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; + const ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return { isValid: false, @@ -72,13 +74,14 @@ export class PrimitiveArrayClaim { maxAgeInSeconds: number | undefined = this.defaultMaxAgeInSeconds, id?: string ): SessionClaimValidator => { + const DateProvider = DateProviderReference.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : this.id, refresh: ctx => this.refresh(ctx), shouldRefresh: (payload, ctx) => this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[this.id].t < Date.now() - maxAgeInSeconds * 1000), + (maxAgeInSeconds !== undefined && payload[this.id].t < DateProvider.now() - maxAgeInSeconds * 1000), validate: async (payload, ctx) => { const claimVal = this.getValueFromPayload(payload, ctx); if (claimVal === undefined) { @@ -91,7 +94,7 @@ export class PrimitiveArrayClaim { } }; } - const ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; + const ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return { isValid: false, @@ -117,13 +120,14 @@ export class PrimitiveArrayClaim { maxAgeInSeconds: number | undefined = this.defaultMaxAgeInSeconds, id?: string ): SessionClaimValidator => { + const DateProvider = DateProviderReference.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : this.id, refresh: ctx => this.refresh(ctx), shouldRefresh: (payload, ctx) => this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[this.id].t < Date.now() - maxAgeInSeconds * 1000), + (maxAgeInSeconds !== undefined && payload[this.id].t < DateProvider.now() - maxAgeInSeconds * 1000), validate: async (payload, ctx) => { const claimVal = this.getValueFromPayload(payload, ctx); if (claimVal === undefined) { @@ -132,7 +136,7 @@ export class PrimitiveArrayClaim { reason: { message: "value does not exist", expectedToInclude: val, actualValue: claimVal } }; } - const ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; + const ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return { isValid: false, @@ -159,13 +163,14 @@ export class PrimitiveArrayClaim { maxAgeInSeconds: number | undefined = this.defaultMaxAgeInSeconds, id?: string ): SessionClaimValidator => { + const DateProvider = DateProviderReference.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : this.id, refresh: ctx => this.refresh(ctx), shouldRefresh: (payload, ctx) => this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[this.id].t < Date.now() - maxAgeInSeconds * 1000), + (maxAgeInSeconds !== undefined && payload[this.id].t < DateProvider.now() - maxAgeInSeconds * 1000), validate: async (payload, ctx) => { const claimVal = this.getValueFromPayload(payload, ctx); if (claimVal === undefined) { @@ -174,7 +179,7 @@ export class PrimitiveArrayClaim { reason: { message: "value does not exist", expectedToInclude: val, actualValue: claimVal } }; } - const ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; + const ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return { isValid: false, @@ -205,13 +210,14 @@ export class PrimitiveArrayClaim { maxAgeInSeconds: number | undefined = this.defaultMaxAgeInSeconds, id?: string ): SessionClaimValidator => { + const DateProvider = DateProviderReference.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : this.id, refresh: ctx => this.refresh(ctx), shouldRefresh: (payload, ctx) => this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[this.id].t < Date.now() - maxAgeInSeconds * 1000), + (maxAgeInSeconds !== undefined && payload[this.id].t < DateProvider.now() - maxAgeInSeconds * 1000), validate: async (payload, ctx) => { const claimVal = this.getValueFromPayload(payload, ctx); if (claimVal === undefined) { @@ -225,7 +231,7 @@ export class PrimitiveArrayClaim { }; } - const ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; + const ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return { isValid: false, diff --git a/lib/ts/claims/primitiveClaim.ts b/lib/ts/claims/primitiveClaim.ts index 4a444114..02b32e12 100644 --- a/lib/ts/claims/primitiveClaim.ts +++ b/lib/ts/claims/primitiveClaim.ts @@ -1,4 +1,5 @@ import { SessionClaimValidator } from "../types"; +import DateProviderReference from "../utils/dateProvider"; export type PrimitiveClaimConfig = { id: string; @@ -31,13 +32,14 @@ export class PrimitiveClaim { maxAgeInSeconds: number | undefined = this.defaultMaxAgeInSeconds, id?: string ): SessionClaimValidator => { + const DateProvider = DateProviderReference.getReferenceOrThrow().dateProvider; return { id: id !== undefined ? id : this.id, refresh: ctx => this.refresh(ctx), shouldRefresh: (payload, ctx) => this.getValueFromPayload(payload, ctx) === undefined || // We know payload[this.id] is defined since the value is not undefined in this branch - (maxAgeInSeconds !== undefined && payload[this.id].t < Date.now() - maxAgeInSeconds * 1000), + (maxAgeInSeconds !== undefined && payload[this.id].t < DateProvider.now() - maxAgeInSeconds * 1000), validate: (payload, ctx) => { const claimVal = this.getValueFromPayload(payload, ctx); if (claimVal === undefined) { @@ -47,7 +49,7 @@ export class PrimitiveClaim { }; } - const ageInSeconds = (Date.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; + const ageInSeconds = (DateProvider.now() - this.getLastFetchedTime(payload, ctx)!) / 1000; if (maxAgeInSeconds !== undefined && ageInSeconds > maxAgeInSeconds) { return { isValid: false, diff --git a/lib/ts/fetch.ts b/lib/ts/fetch.ts index b3472cc1..266da1e3 100644 --- a/lib/ts/fetch.ts +++ b/lib/ts/fetch.ts @@ -19,6 +19,7 @@ import CookieHandlerReference from "./utils/cookieHandler"; import WindowHandlerReference from "./utils/windowHandler"; import LockFactoryReference from "./utils/lockFactory"; import { logDebugMessage } from "./logger"; +import DateProviderReference from "./utils/dateProvider"; export class AntiCsrfToken { private static tokenInfo: @@ -670,6 +671,7 @@ export function setToken(tokenType: TokenType, value: string) { if (value !== "") { logDebugMessage(`setToken: saved ${tokenType} token into cookies`); // We save the tokens with a 100-year expiration time + // We have to use the client side system clock here, because the cookie expiration will be counted based on that return storeInCookies(name, value, Date.now() + 3153600000); } else { logDebugMessage(`setToken: cleared ${tokenType} token from cookies`); @@ -770,6 +772,7 @@ async function saveTokensFromHeaders(response: Response) { if (frontToken !== null) { logDebugMessage("saveTokensFromHeaders: Setting sFrontToken: " + frontToken); await FrontToken.setItem(frontToken); + updateClockSkewUsingFrontToken({ frontToken, responseHeaders: response.headers }); } const antiCsrfToken = response.headers.get("anti-csrf"); if (antiCsrfToken !== null) { @@ -784,6 +787,7 @@ async function saveTokensFromHeaders(response: Response) { export async function saveLastAccessTokenUpdate() { logDebugMessage("saveLastAccessTokenUpdate: called"); + // We are saving the client side time here, but the actual value doesn't matter. const now = Date.now().toString(); logDebugMessage("saveLastAccessTokenUpdate: setting " + now); await storeInCookies(LAST_ACCESS_TOKEN_UPDATE, now, Number.MAX_SAFE_INTEGER); @@ -926,3 +930,33 @@ export function fireSessionUpdateEventsIfNecessary( }); } } + +/** + * Updates the clock skew based on the provided frontToken and responseHeaders. + */ +export const updateClockSkewUsingFrontToken = ({ + frontToken, + responseHeaders +}: { + frontToken: string | undefined | null; + responseHeaders: Headers; +}): void => { + logDebugMessage("updateClockSkewUsingFrontToken: frontToken: " + frontToken); + + if (frontToken === null || frontToken === undefined || frontToken === "remove") { + logDebugMessage( + "updateClockSkewUsingFrontToken: the access token payload wasn't updated or is being removed, skipping clock skew update" + ); + return; + } + + const frontTokenPayload = parseFrontToken(frontToken); + const clockSkewInMillis = AuthHttpRequest.recipeImpl.getClockSkewInMillis({ + accessTokenPayload: frontTokenPayload.up, + responseHeaders + }); + + DateProviderReference.getReferenceOrThrow().dateProvider.setClientClockSkewInMillis(clockSkewInMillis); + + logDebugMessage("updateClockSkewUsingFrontToken: Client clock synchronized successfully"); +}; diff --git a/lib/ts/index.ts b/lib/ts/index.ts index 680d7425..4fd651c9 100644 --- a/lib/ts/index.ts +++ b/lib/ts/index.ts @@ -23,6 +23,7 @@ import WindowHandlerReference from "./utils/windowHandler"; import LockFactoryReference from "./utils/lockFactory"; import { SessionClaimValidatorStore } from "./utils/sessionClaimValidatorStore"; import { enableLogging } from "./logger"; +import DateProviderReference from "./utils/dateProvider"; export default class AuthHttpRequest { private static axiosInterceptorQueue: (() => void)[] = []; @@ -30,6 +31,7 @@ export default class AuthHttpRequest { static init(options: InputType) { CookieHandlerReference.init(options.cookieHandler); WindowHandlerReference.init(options.windowHandler); + DateProviderReference.init(options.dateProvider); LockFactoryReference.init( options.lockFactory, WindowHandlerReference.getReferenceOrThrow().windowHandler.localStorage diff --git a/lib/ts/recipeImplementation.ts b/lib/ts/recipeImplementation.ts index 76625ad0..186b11e0 100644 --- a/lib/ts/recipeImplementation.ts +++ b/lib/ts/recipeImplementation.ts @@ -14,6 +14,7 @@ import { logDebugMessage } from "./logger"; import { STGeneralError } from "./error"; import { addInterceptorsToXMLHttpRequest } from "./xmlhttprequest"; import { normaliseSessionScopeOrThrowError, normaliseURLDomainOrThrowError } from "./utils"; +import DateProviderReference from "./utils/dateProvider"; export default function RecipeImplementation(recipeImplInput: { preAPIHook: RecipePreAPIHookFunction; @@ -99,7 +100,7 @@ export default function RecipeImplementation(recipeImplInput: { throw new Error("No session exists"); } - if (tokenInfo.ate < Date.now()) { + if (tokenInfo.ate < DateProviderReference.getReferenceOrThrow().dateProvider.now()) { logDebugMessage("getAccessTokenPayloadSecurely: access token expired. Refreshing session"); let retry = await AuthHttpRequest.attemptRefreshingSession(); if (retry) { @@ -124,7 +125,7 @@ export default function RecipeImplementation(recipeImplInput: { return false; } - if (tokenInfo.ate < Date.now()) { + if (tokenInfo.ate < DateProviderReference.getReferenceOrThrow().dateProvider.now()) { logDebugMessage("doesSessionExist: access token expired. Refreshing session"); const preRequestLSS = await getLocalSessionState(false); @@ -308,6 +309,29 @@ export default function RecipeImplementation(recipeImplInput: { return domain === normalisedsessionDomain; } } + }, + + getClockSkewInMillis: function ({ + accessTokenPayload + }: { + accessTokenPayload: any; + responseHeaders: Headers; + }): number { + logDebugMessage("getClockSkewInMillis: called"); + + const tokenIssuedAt = accessTokenPayload?.iat; + if (tokenIssuedAt === undefined || typeof tokenIssuedAt !== "number") { + logDebugMessage( + `getClockSkewInMillis: payload iat is undefined or not a number. This may happen due to an unsupported backend sdk. Returning 0` + ); + return 0; + } + + const estimatedServerTimeNow = tokenIssuedAt * 1000; + const clockSkewInMillis = estimatedServerTimeNow - Date.now(); + logDebugMessage("getClockSkewInMillis: returning " + clockSkewInMillis); + + return clockSkewInMillis; } }; } diff --git a/lib/ts/types.ts b/lib/ts/types.ts index 9c6cea43..8e6f9d1b 100644 --- a/lib/ts/types.ts +++ b/lib/ts/types.ts @@ -17,6 +17,7 @@ import OverrideableBuilder from "supertokens-js-override"; import { CookieHandlerInput } from "./utils/cookieHandler/types"; import { WindowHandlerInput } from "./utils/windowHandler/types"; import { LockFactory } from "./utils/lockFactory/types"; +import { DateProviderInput } from "./utils/dateProvider/types"; export type Event = | { @@ -57,6 +58,7 @@ export type InputType = { sessionTokenBackendDomain?: string; cookieHandler?: CookieHandlerInput; windowHandler?: WindowHandlerInput; + dateProvider?: DateProviderInput; preAPIHook?: RecipePreAPIHookFunction; postAPIHook?: RecipePostAPIHookFunction; onHandleEvent?: EventHandler; @@ -150,6 +152,8 @@ export type RecipeInterface = { apiDomain: string, sessionTokenBackendDomain: string | undefined ): boolean; + + getClockSkewInMillis(params: { accessTokenPayload: any; responseHeaders: Headers }): number; }; export type ClaimValidationResult = { isValid: true } | { isValid: false; reason?: any }; diff --git a/lib/ts/utils/dateProvider/defaultImplementation.ts b/lib/ts/utils/dateProvider/defaultImplementation.ts new file mode 100644 index 00000000..648bc317 --- /dev/null +++ b/lib/ts/utils/dateProvider/defaultImplementation.ts @@ -0,0 +1,72 @@ +/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +import WindowHandlerReference from "../windowHandler"; + +export class DateProvider { + private static instance?: DateProvider; + public static readonly CLOCK_SKEW_KEY = "__st_clockSkewInMillis"; + private clockSkewInMillis: number = 0; + + // The static init method is used to create a singleton instance of DateProvider, + // as we require access to localStorage for initializing clockSkewInMillis. + // Access to localStorage is available only after WindowHandlerReference is initialized. + + // We took the following considerations into account while designing the cache implementation: + // 1. Cache cleared with an active session while the FE clock is wrong (e.g., Safari clears localStorage after 7 days). + // - Resets clockSkewInMillis to 0, corrected in the next front token update. + // 2. Outdated info in the cache making the clock point into the future. + // - May cause validator.shouldRefresh to always be true, corrected by the next front token update. + // 3. Outdated info in the cache making the clock point into the past. + // - May cause validator.shouldRefresh to always be false, fixed during the next token refresh (worst case). + // 4. Other tabs (on different subdomains) refreshing the session with outdated info in the cache. + // - Each subdomain has its own cache (localStorage), similar implications to cases 2 and 3. + // 5. Other tabs refreshing the session with outdated info in memory. + // - May cause validator.shouldRefresh to always be true or false, fixed in the next front token update. + + static init() { + if (DateProvider.instance !== undefined) { + return; + } + + DateProvider.instance = new DateProvider(); + const localStorage = WindowHandlerReference.getReferenceOrThrow().windowHandler.localStorage; + const stored = localStorage.getItemSync(DateProvider.CLOCK_SKEW_KEY); + const clockSkewInMillis = stored !== null ? parseInt(stored, 10) : 0; + DateProvider.instance.setClientClockSkewInMillis(clockSkewInMillis); + } + + static getReferenceOrThrow(): DateProvider { + if (DateProvider.instance === undefined) { + throw new Error("DateProvider must be initialized before calling this method."); + } + + return DateProvider.instance; + } + + setClientClockSkewInMillis(clockSkewInMillis: number): void { + this.clockSkewInMillis = clockSkewInMillis; + const localStorage = WindowHandlerReference.getReferenceOrThrow().windowHandler.localStorage; + localStorage.setItemSync(DateProvider.CLOCK_SKEW_KEY, String(clockSkewInMillis)); + } + + getClientClockSkewInMillis(): number { + return this.clockSkewInMillis; + } + + now(): number { + return Date.now() + this.getClientClockSkewInMillis(); + } +} diff --git a/lib/ts/utils/dateProvider/index.ts b/lib/ts/utils/dateProvider/index.ts new file mode 100644 index 00000000..ca35d673 --- /dev/null +++ b/lib/ts/utils/dateProvider/index.ts @@ -0,0 +1,61 @@ +/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +import { DateProvider } from "./defaultImplementation"; +import { DateProviderInterface, DateProviderInput } from "./types"; + +/** + * A utility class for managing a reference to a DateProviderInterface, allowing customization of time-related operations. + * This class is designed to address clock skew issues between the server and client by providing a consistent mechanism + * for obtaining current time adjusted for clock skew. + * + * @class DateProviderReference + */ +export default class DateProviderReference { + private static instance?: DateProviderReference; + + dateProvider: DateProviderInterface; + + constructor(dateProviderInput?: DateProviderInput) { + let dateProviderFunc: DateProviderInput = original => original; + if (dateProviderInput !== undefined) { + dateProviderFunc = dateProviderInput; + } + + // Initialize the DateProvider implementation by calling the init method. + // This is done to ensure that the WindowHandler is initialized before we instantiate the DateProvider. + DateProvider.init(); + const defaultDateProviderImplementation = DateProvider.getReferenceOrThrow(); + this.dateProvider = dateProviderFunc(defaultDateProviderImplementation); + } + + static init(dateProviderInput?: DateProviderInput): void { + if (DateProviderReference.instance !== undefined) { + return; + } + + DateProviderReference.instance = new DateProviderReference(dateProviderInput); + } + + static getReferenceOrThrow(): DateProviderReference { + if (DateProviderReference.instance === undefined) { + throw new Error("SuperTokensDateProvider must be initialized before calling this method."); + } + + return DateProviderReference.instance; + } +} + +export { DateProviderReference }; diff --git a/lib/ts/utils/dateProvider/types.ts b/lib/ts/utils/dateProvider/types.ts new file mode 100644 index 00000000..81b5f242 --- /dev/null +++ b/lib/ts/utils/dateProvider/types.ts @@ -0,0 +1,22 @@ +/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +export interface DateProviderInterface { + now(): number; + setClientClockSkewInMillis(clockSkewInMillis: number): void; + getClientClockSkewInMillis(): number; +} + +export type DateProviderInput = (original: DateProviderInterface) => DateProviderInterface; diff --git a/lib/ts/version.ts b/lib/ts/version.ts index 19dcc131..e9379327 100644 --- a/lib/ts/version.ts +++ b/lib/ts/version.ts @@ -12,6 +12,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -export const package_version = "17.0.5"; +export const package_version = "18.0.0"; export const supported_fdi = ["1.16", "1.17", "1.18"]; diff --git a/lib/ts/xmlhttprequest.ts b/lib/ts/xmlhttprequest.ts index 6090bdea..0df3cbb4 100644 --- a/lib/ts/xmlhttprequest.ts +++ b/lib/ts/xmlhttprequest.ts @@ -22,7 +22,8 @@ import AuthHttpRequestFetch, { getTokenForHeaderAuth, getLocalSessionState, LocalSessionState, - fireSessionUpdateEventsIfNecessary + fireSessionUpdateEventsIfNecessary, + updateClockSkewUsingFrontToken } from "./fetch"; import { logDebugMessage } from "./logger"; import WindowHandlerReference from "./utils/windowHandler"; @@ -598,6 +599,7 @@ async function saveTokensFromHeaders(headers: Headers) { if (frontToken !== null) { logDebugMessage("saveTokensFromHeaders: Setting sFrontToken: " + frontToken); await FrontToken.setItem(frontToken); + updateClockSkewUsingFrontToken({ frontToken, responseHeaders: headers }); } const antiCsrfToken = headers.get("anti-csrf"); diff --git a/package-lock.json b/package-lock.json index 21ca39b3..1a3c43cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "supertokens-website", - "version": "17.0.5", + "version": "18.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "supertokens-website", - "version": "17.0.5", + "version": "18.0.0", "license": "Apache-2.0", "dependencies": { "browser-tabs-lock": "^1.3.0", diff --git a/package.json b/package.json index 726196aa..64d7ee7f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supertokens-website", - "version": "17.0.5", + "version": "18.0.0", "description": "frontend sdk for website to be used for auth solution.", "main": "index.js", "dependencies": { @@ -68,7 +68,7 @@ "size-limit": [ { "path": "lib/build/bundleEntry.js", - "limit": "20kb" + "limit": "21kb" } ], "license": "Apache-2.0", diff --git a/test/fetch.headers.test.js b/test/fetch.headers.test.js index 4dce2d1e..833233be 100644 --- a/test/fetch.headers.test.js +++ b/test/fetch.headers.test.js @@ -1257,7 +1257,8 @@ describe("Fetch AuthHttpRequest class tests with headers", function () { }); assertEqual(await loginResponse.text(), userId); - assertEqual(localStorage.length, 0); + const keysInLocalStorage = Object.keys(localStorage).filter(key => key !== "__st_clockSkewInMillis"); // __st_clockSkewInMillis will be set on supertokens init + assertEqual(keysInLocalStorage.length, 0); }); } finally { await browser.close(); @@ -1525,7 +1526,8 @@ describe("Fetch AuthHttpRequest class tests with headers", function () { let userId = "testing-supertokens-website"; - assertEqual(localStorage.length, 0); + const keysInLocalStorage = Object.keys(localStorage).filter(key => key !== "__st_clockSkewInMillis"); // __st_clockSkewInMillis will be set on supertokens init + assertEqual(keysInLocalStorage.length, 0); // call sessionDoesExist assertEqual(await supertokens.doesSessionExist(), false); @@ -1580,7 +1582,8 @@ describe("Fetch AuthHttpRequest class tests with headers", function () { }); let userId = "testing-supertokens-website"; - assertEqual(localStorage.length, 0); + const keysInLocalStorage = Object.keys(localStorage).filter(key => key !== "__st_clockSkewInMillis"); // __st_clockSkewInMillis will be set on supertokens init + assertEqual(keysInLocalStorage.length, 0); await fetch(`/login`, { method: "post", diff --git a/test/interception.claims.test.js b/test/interception.claims.test.js index 71708512..46e36c49 100644 --- a/test/interception.claims.test.js +++ b/test/interception.claims.test.js @@ -83,7 +83,9 @@ addGenericTestCases((name, transferMethod, setupFunc, setupArgs = []) => { await page.evaluate( setupFunc, { - // enableDebugLogs: true + // enableDebugLogs: true, + // This isn't used in all tests but it only produces some extra logs + override: ["log_getClockSkewInMillis"] }, ...setupArgs ); @@ -191,5 +193,136 @@ addGenericTestCases((name, transferMethod, setupFunc, setupArgs = []) => { await browser.close(); } }); + + it("should call the claim refresh endpoint once for multiple `shouldRefresh` calls with adjusted clock skew", async function () { + await startST(2 * 60 * 60); // setting accessTokenValidity to 2 hours to avoid refresh issues due to clock skew + try { + let customClaimRefreshCalledCount = 0; + + // Override Date.now() to return the current time plus 1 hour + await page.evaluate(() => { + globalThis.originalNow = Date.now; + Date.now = function () { + return originalNow() + 60 * 60 * 1000; + }; + }); + + await page.setRequestInterception(true); + + page.on("request", req => { + if (req.url() === `${BASE_URL}/update-jwt`) { + customClaimRefreshCalledCount++; + } + req.continue(); + }); + + await page.evaluate(async () => { + const userId = "testing-supertokens-website"; + + // Create a session + const loginResponse = await toTest({ + url: `${BASE_URL}/login`, + method: "post", + headers: { + Accept: "application/json", + "Content-Type": "application/json" + }, + body: JSON.stringify({ userId }) + }); + + assertEqual(loginResponse.responseText, userId); + + const customSessionClaim = new supertokens.BooleanClaim({ + id: "st-custom", + refresh: async () => { + const resp = await toTest({ + url: `${BASE_URL}/update-jwt`, + method: "post", + headers: { + Accept: "application/json", + "Content-Type": "application/json" + }, + body: JSON.stringify({ + "st-custom": { + v: true, + t: originalNow() + } + }) + }); + }, + defaultMaxAgeInSeconds: 300 /* 300 seconds */ + }); + + const customSessionClaimValidator = customSessionClaim.validators.isTrue(); + + await supertokens.validateClaims(() => [customSessionClaimValidator]); + await supertokens.validateClaims(() => [customSessionClaimValidator]); + await supertokens.validateClaims(() => [customSessionClaimValidator]); + }); + + assert.strictEqual(customClaimRefreshCalledCount, 1); + } finally { + await browser.close(); + } + }); + + it("should call getClockSkewInMillis with appropriate headers", async function () { + await startST(); + let clockSkewParams = []; + page.on("console", ev => { + const text = ev.text(); + // console.log(text); + const key = "TEST_getClockSkewInMillis$"; + if (text.startsWith(key)) { + clockSkewParams.push(JSON.parse(text.substr(key.length))); + } + }); + const accessTokenPayload = await page.evaluate(async () => { + const userId = "testing-supertokens-website"; + + // Create a session + await toTest({ + url: `${BASE_URL}/login`, + method: "post", + headers: { + Accept: "application/json", + "Content-Type": "application/json" + }, + body: JSON.stringify({ userId }) + }); + + return await supertokens.getAccessTokenPayloadSecurely(); + }); + + assert.strictEqual(clockSkewParams.length, 1); + assert.deepStrictEqual(clockSkewParams[0].accessTokenPayload, accessTokenPayload); + const expectedHeaders = [ + "access-control-allow-credentials", + "access-control-allow-origin", + "access-control-expose-headers", + "connection", + "content-length", + "content-type", + "date", + "etag", + "front-token", + "keep-alive", + "vary", + "x-powered-by", + ...(transferMethod === "header" ? ["st-access-token", "st-refresh-token"] : ["anti-csrf"]) + ]; + + assert.deepStrictEqual( + new Set(clockSkewParams[0].responseHeaders.map(([key]) => key)), + new Set(expectedHeaders) + ); + + for (const name of expectedHeaders) { + assert.ok( + clockSkewParams[0].responseHeaders.find(([headerName]) => name === headerName), + name + " is undefined in headers" + ); + } + }); }); }); diff --git a/test/interception.testgen.js b/test/interception.testgen.js index 2ca80818..380042b3 100644 --- a/test/interception.testgen.js +++ b/test/interception.testgen.js @@ -12,6 +12,20 @@ module.exports.addGenericTestCases = function (getTestCases) { ...oI, shouldDoInterceptionBasedOnUrl: url => url.includes("doOverride") })); + } else if (reqOverride === "log_getClockSkewInMillis") { + overrideFunctions.push(oI => ({ + ...oI, + getClockSkewInMillis: ({ accessTokenPayload, responseHeaders }) => { + console.log( + "TEST_getClockSkewInMillis$" + + JSON.stringify({ + accessTokenPayload, + responseHeaders: Array.from(responseHeaders.entries()) + }) + ); + return oI.getClockSkewInMillis({ accessTokenPayload, responseHeaders }); + } + })); } } } @@ -56,6 +70,20 @@ module.exports.addGenericTestCases = function (getTestCases) { ...oI, shouldDoInterceptionBasedOnUrl: url => url.includes("doOverride") })); + } else if (reqOverride === "log_getClockSkewInMillis") { + overrideFunctions.push(oI => ({ + ...oI, + getClockSkewInMillis: ({ accessTokenPayload, responseHeaders }) => { + console.log( + "TEST_getClockSkewInMillis$" + + JSON.stringify({ + accessTokenPayload, + responseHeaders: Array.from(responseHeaders.entries()) + }) + ); + return oI.getClockSkewInMillis({ accessTokenPayload, responseHeaders }); + } + })); } } } @@ -129,6 +157,20 @@ module.exports.addGenericTestCases = function (getTestCases) { ...oI, shouldDoInterceptionBasedOnUrl: url => url.includes("doOverride") })); + } else if (reqOverride === "log_getClockSkewInMillis") { + overrideFunctions.push(oI => ({ + ...oI, + getClockSkewInMillis: ({ accessTokenPayload, responseHeaders }) => { + console.log( + "TEST_getClockSkewInMillis$" + + JSON.stringify({ + accessTokenPayload, + responseHeaders: Array.from(responseHeaders.entries()) + }) + ); + return oI.getClockSkewInMillis({ accessTokenPayload, responseHeaders }); + } + })); } } } @@ -188,6 +230,20 @@ module.exports.addGenericTestCases = function (getTestCases) { ...oI, shouldDoInterceptionBasedOnUrl: url => url.includes("doOverride") })); + } else if (reqOverride === "log_getClockSkewInMillis") { + overrideFunctions.push(oI => ({ + ...oI, + getClockSkewInMillis: ({ accessTokenPayload, responseHeaders }) => { + console.log( + "TEST_getClockSkewInMillis$" + + JSON.stringify({ + accessTokenPayload, + responseHeaders: Array.from(responseHeaders.entries()) + }) + ); + return oI.getClockSkewInMillis({ accessTokenPayload, responseHeaders }); + } + })); } } } @@ -244,6 +300,20 @@ module.exports.addGenericTestCases = function (getTestCases) { ...oI, shouldDoInterceptionBasedOnUrl: url => url.includes("doOverride") })); + } else if (reqOverride === "log_getClockSkewInMillis") { + overrideFunctions.push(oI => ({ + ...oI, + getClockSkewInMillis: ({ accessTokenPayload, responseHeaders }) => { + console.log( + "TEST_getClockSkewInMillis$" + + JSON.stringify({ + accessTokenPayload, + responseHeaders: Array.from(responseHeaders.entries()) + }) + ); + return oI.getClockSkewInMillis({ accessTokenPayload, responseHeaders }); + } + })); } } } diff --git a/test/sessionClaimValidatorRefresh.test.js b/test/sessionClaimValidatorRefresh.test.js new file mode 100644 index 00000000..78db1d89 --- /dev/null +++ b/test/sessionClaimValidatorRefresh.test.js @@ -0,0 +1,136 @@ +/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +const assert = require("assert"); +const sinon = require("sinon"); +let jsdom = require("mocha-jsdom"); +const { BooleanClaim } = require("../lib/build"); +const { WindowHandlerReference } = require("../lib/build/utils/windowHandler"); +const { DateProviderReference } = require("../lib/build/utils/dateProvider"); + +const ONE_HOUR_IN_MS = 3600000; + +function withFakeClock(now, cb) { + const clock = sinon.useFakeTimers({ now, shouldAdvanceTime: false }); + + try { + return cb(); + } finally { + clock.restore(); + } +} + +describe("SessionClaimValidator Refresh", () => { + jsdom({ url: "http://localhost" }); + + describe("Client and Server clock are in sync", () => { + before(function () { + WindowHandlerReference.init(); + DateProviderReference.init(); + }); + + it("SessionClaimValidator.shouldRefresh should return false after refreshing", function () { + const tokenPayload = { + "st-test": { + v: false, + t: Date.now() - 20000 // setting claim fetched time to 20 seconds ago + } + }; + + const testClaim = new BooleanClaim({ + id: "st-test", + refresh: () => { + tokenPayload["st-test"].t = Date.now(); + }, + defaultMaxAgeInSeconds: 10 /* 10 seconds */ + }); + + const testClaimValidator = testClaim.validators.isTrue(); + + assert(testClaimValidator.shouldRefresh(tokenPayload) === true); + testClaimValidator.refresh(); + assert(testClaimValidator.shouldRefresh(tokenPayload) === false); + }); + }); + + describe("Client Clock is ahead 1 hour of server time", () => { + before(function () { + WindowHandlerReference.init(); + DateProviderReference.init(); + }); + + it("SessionClaimValidator.shouldRefresh should return true even after calling refresh without adjusting for DateProvider clock skew", function () { + const tokenPayload = { + "st-test": { + v: false, + t: Date.now() + } + }; + + const testClaim = new BooleanClaim({ + id: "st-test", + refresh: () => { + tokenPayload["st-test"].t = Date.now(); + }, + defaultMaxAgeInSeconds: 10 /* 10 seconds */ + }); + + const testClaimValidator = testClaim.validators.isTrue(); + + withFakeClock(Date.now() + ONE_HOUR_IN_MS, () => { + assert(testClaimValidator.shouldRefresh(tokenPayload) === true); + }); + + testClaimValidator.refresh(); + + withFakeClock(Date.now() + ONE_HOUR_IN_MS, () => { + assert(testClaimValidator.shouldRefresh(tokenPayload) === true); + }); + }); + + it("SessionClaimValidator.shouldRefresh should return false after calling refresh once - after adjusting for DateProvider clock skew", function () { + const tokenPayload = { + "st-test": { + v: false, + t: Date.now() - 20000 // setting claim fetched time to 20 seconds ago + } + }; + + const testClaim = new BooleanClaim({ + id: "st-test", + refresh: () => { + // Adjust DateProvider clock skew + DateProviderReference.getReferenceOrThrow().dateProvider.setClientClockSkewInMillis( + -ONE_HOUR_IN_MS + ); + tokenPayload["st-test"].t = Date.now(); + }, + defaultMaxAgeInSeconds: 10 /* 10 seconds */ + }); + + const testClaimValidator = testClaim.validators.isTrue(); + + withFakeClock(Date.now() + ONE_HOUR_IN_MS, () => { + assert(testClaimValidator.shouldRefresh(tokenPayload) === true); + }); + + testClaimValidator.refresh(); + + withFakeClock(Date.now() + ONE_HOUR_IN_MS, () => { + assert(testClaimValidator.shouldRefresh(tokenPayload) === false); + }); + }); + }); +}); diff --git a/test/with-typescript.ts b/test/with-typescript.ts index 995be060..e14d8df8 100644 --- a/test/with-typescript.ts +++ b/test/with-typescript.ts @@ -1,12 +1,14 @@ import supertokens, { addAxiosInterceptors, signOut, getUserId, BooleanClaim, PrimitiveClaim, SessionClaimValidator, validateClaims } from "../"; import axios from "axios"; import { STGeneralError } from '../utils/error'; +import { DateProviderReference } from '../utils/dateProvider'; STGeneralError.isThisError(new Error()) supertokens.addAxiosInterceptors(axios, {}); addAxiosInterceptors(axios, undefined); +DateProviderReference.getReferenceOrThrow().dateProvider.now(); supertokens.attemptRefreshingSession().then((b: boolean) => { console.log(b); diff --git a/utils/dateProvider/index.d.ts b/utils/dateProvider/index.d.ts new file mode 100644 index 00000000..11ea6c22 --- /dev/null +++ b/utils/dateProvider/index.d.ts @@ -0,0 +1,25 @@ +/* Copyright (c) 2022, VRAI Labs and/or its affiliates. All rights reserved. +* +* This software is licensed under the Apache License, Version 2.0 (the +* "License") as published by the Apache Software Foundation. +* +* You may not use this file except in compliance with the License. You may +* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +*/ +export * from "../../lib/build/utils/dateProvider"; +/** + * 'export *' does not re-export a default. + * import SuperTokens from "supertokens-website"; + * the above import statement won't be possible unless either + * - user add "esModuleInterop": true in their tsconfig.json file + * - we do the following change: + */ + import * as _default from "../../lib/build/utils/dateProvider"; + export default _default; + \ No newline at end of file diff --git a/utils/dateProvider/index.js b/utils/dateProvider/index.js new file mode 100644 index 00000000..8ac536eb --- /dev/null +++ b/utils/dateProvider/index.js @@ -0,0 +1,20 @@ +/* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. +*/ +"use strict"; +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +exports.__esModule = true; +__export(require("../../lib/build/utils/dateProvider")); diff --git a/utils/dateProvider/types.d.ts b/utils/dateProvider/types.d.ts new file mode 100644 index 00000000..2d0f2e91 --- /dev/null +++ b/utils/dateProvider/types.d.ts @@ -0,0 +1,25 @@ +/* Copyright (c) 2022, VRAI Labs and/or its affiliates. All rights reserved. +* +* This software is licensed under the Apache License, Version 2.0 (the +* "License") as published by the Apache Software Foundation. +* +* You may not use this file except in compliance with the License. You may +* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +*/ +export * from "../../lib/build/utils/dateProvider/types"; +/** + * 'export *' does not re-export a default. + * import SuperTokens from "supertokens-website"; + * the above import statement won't be possible unless either + * - user add "esModuleInterop": true in their tsconfig.json file + * - we do the following change: + */ + import * as _default from "../../lib/build/utils/dateProvider/types"; + export default _default; + \ No newline at end of file diff --git a/utils/dateProvider/types.js b/utils/dateProvider/types.js new file mode 100644 index 00000000..d0c4cb58 --- /dev/null +++ b/utils/dateProvider/types.js @@ -0,0 +1,20 @@ +/* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. +*/ +"use strict"; +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +exports.__esModule = true; +__export(require("../../lib/build/utils/dateProvider/types"));