UNPKG

@quadible/web-sdk

Version:

The web sdk for Quadible's behavioral authentication service.

118 lines 5.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.screenFrameCheckInterval = void 0; exports.resetScreenFrameWatch = resetScreenFrameWatch; exports.getUnstableScreenFrame = getUnstableScreenFrame; exports.default = getScreenFrame; const browser_1 = require("../utils/browser"); const data_1 = require("../utils/data"); exports.screenFrameCheckInterval = 2500; const roundingPrecision = 10; // The type is readonly to protect from unwanted mutations let screenFrameBackup; let screenFrameSizeTimeoutId; /** * Starts watching the screen frame size. When a non-zero size appears, the size is saved and the watch is stopped. * Later, when `getScreenFrame` runs, it will return the saved non-zero size if the current size is null. * * This trick is required to mitigate the fact that the screen frame turns null in some cases. * See more on this at https://github.com/fingerprintjs/fingerprintjs/issues/568 */ function watchScreenFrame() { if (screenFrameSizeTimeoutId !== undefined) { return; } const checkScreenFrame = () => { const frameSize = getCurrentScreenFrame(); if (isFrameSizeNull(frameSize)) { screenFrameSizeTimeoutId = setTimeout(checkScreenFrame, exports.screenFrameCheckInterval); } else { screenFrameBackup = frameSize; screenFrameSizeTimeoutId = undefined; } }; checkScreenFrame(); } /** * For tests only */ function resetScreenFrameWatch() { if (screenFrameSizeTimeoutId !== undefined) { clearTimeout(screenFrameSizeTimeoutId); screenFrameSizeTimeoutId = undefined; } screenFrameBackup = undefined; } /** * A version of the entropy source without stabilization. * * Warning for package users: * This function is out of Semantic Versioning, i.e. can change unexpectedly. Usage is at your own risk. */ function getUnstableScreenFrame() { watchScreenFrame(); return async () => { let frameSize = getCurrentScreenFrame(); if (isFrameSizeNull(frameSize)) { if (screenFrameBackup) { return [...screenFrameBackup]; } if ((0, browser_1.getFullscreenElement)()) { // Some browsers set the screen frame to zero when programmatic fullscreen is on. // There is a chance of getting a non-zero frame after exiting the fullscreen. // See more on this at https://github.com/fingerprintjs/fingerprintjs/issues/568 await (0, browser_1.exitFullscreen)(); frameSize = getCurrentScreenFrame(); } } if (!isFrameSizeNull(frameSize)) { screenFrameBackup = frameSize; } return frameSize; }; } /** * A version of the entropy source with stabilization to make it suitable for static fingerprinting. * * Sometimes the available screen resolution changes a bit, e.g. 1900x1440 → 1900x1439. A possible reason: macOS Dock * shrinks to fit more icons when there is too little space. The rounding is used to mitigate the difference. * * The frame width is always 0 in private mode of Safari 17, so the frame is not used in Safari 17. */ function getScreenFrame() { if ((0, browser_1.isWebKit)() && (0, browser_1.isWebKit616OrNewer)() && (0, browser_1.isSafariWebKit)()) { return () => Promise.resolve(undefined); } const screenFrameGetter = getUnstableScreenFrame(); return async () => { const frameSize = await screenFrameGetter(); const processSize = (sideSize) => (sideSize === null ? null : (0, data_1.round)(sideSize, roundingPrecision)); // It might look like I don't know about `for` and `map`. // In fact, such code is used to avoid TypeScript issues without using `as`. return [processSize(frameSize[0]), processSize(frameSize[1]), processSize(frameSize[2]), processSize(frameSize[3])]; }; } function getCurrentScreenFrame() { const s = screen; // Some browsers return screen resolution as strings, e.g. "1200", instead of a number, e.g. 1200. // I suspect it's done by certain plugins that randomize browser properties to prevent fingerprinting. // // Some browsers (IE, Edge ≤18) don't provide `screen.availLeft` and `screen.availTop`. The property values are // replaced with 0 in such cases to not lose the entropy from `screen.availWidth` and `screen.availHeight`. return [ (0, data_1.replaceNaN)((0, data_1.toFloat)(s.availTop), null), (0, data_1.replaceNaN)((0, data_1.toFloat)(s.width) - (0, data_1.toFloat)(s.availWidth) - (0, data_1.replaceNaN)((0, data_1.toFloat)(s.availLeft), 0), null), (0, data_1.replaceNaN)((0, data_1.toFloat)(s.height) - (0, data_1.toFloat)(s.availHeight) - (0, data_1.replaceNaN)((0, data_1.toFloat)(s.availTop), 0), null), (0, data_1.replaceNaN)((0, data_1.toFloat)(s.availLeft), null), ]; } function isFrameSizeNull(frameSize) { for (let i = 0; i < 4; ++i) { if (frameSize[i]) { return false; } } return true; } //# sourceMappingURL=screen_frame.js.map