@quadible/web-sdk
Version:
The web sdk for Quadible's behavioral authentication service.
118 lines • 5.16 kB
JavaScript
;
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