Skip to content

Commit

Permalink
Remove fingerprint code (#7)
Browse files Browse the repository at this point in the history
* Fix broken urls

* Add an IIF to the constructor

* Comment all fingerprinting code

* Ignore build output from eslint

* Remove fingerprinting code

* Make publishable key required

* Give field more descriptive name

* Set publishable key
  • Loading branch information
hwhmeikle authored Jun 2, 2022
1 parent d5f8660 commit 75853b5
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 198 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"parserOptions": {
"ecmaVersion": 2021
},
"extends": ["plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"]
"extends": ["plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
"ignorePatterns": ["dist"]
}
12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/index.d.ts",
"keywords": ["authsignal", "mfa", "2fa", "authentication"],
"keywords": [
"authsignal",
"mfa",
"2fa",
"authentication"
],
"license": "MIT",
"repository": "git:https://github.com/authsignal/authsignal-browser.git",
"sideEffects": false,
Expand All @@ -23,7 +28,6 @@
"devDependencies": {
"@rollup/plugin-commonjs": "^22.0.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@types/basiclightbox": "^5.0.1",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.25.0",
"@typescript-eslint/parser": "^5.25.0",
Expand All @@ -36,7 +40,9 @@
"tslib": "^2.4.0",
"typescript": "^4.6.4"
},
"files": ["dist"],
"files": [
"dist"
],
"exports": {
".": {
"import": "./dist/esm/index.js",
Expand Down
148 changes: 19 additions & 129 deletions src/AuthsignalBrowser.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,31 @@
import * as FingerprintJS from "@fingerprintjs/fingerprintjs";
import {v4 as uuidv4} from "uuid";

import {setCookie, generateId, getCookieDomain, getCookie, getHostWithProtocol, reformatDate} from "./helpers";
import {
Challenge,
AuthsignalOptions,
AnonymousId,
RegisterAnonymousIdRequest,
RegisterIdentityRequest,
UserProps,
Mfa,
} from "./types";
import {setCookie, getCookieDomain, getCookie} from "./helpers";
import {Challenge, AuthsignalOptions, Mfa} from "./types";
import {PopupHandler} from "./PopupHandler";

export function authsignalBrowser(publishableKey: string, options?: AuthsignalOptions): AuthsignalBrowser {
const client = new AuthsignalBrowser();
client.init(publishableKey, options);
return client;
}
export class AuthsignalBrowser {
private anonymousId = "";
private publishableKey = "";
private cookieDomain = "";
private idCookieName = "";
private trackingHost = "";
// Could do with a fingerprintClient interface
private fingerprintClient?: FingerprintJS.Agent;
private deviceFingerprint?: string;

async init(publishableKey: string, options?: AuthsignalOptions): Promise<void> {
this.cookieDomain = options?.cookieDomain || getCookieDomain();
this.idCookieName = options?.cookieName || "__as_aid";

this.trackingHost = getHostWithProtocol(options?.trackingHost || "t.authsignal.com");
this.fingerprintClient = await FingerprintJS.load({
monitoring: false,
});

const anonymousId = this.getAnonymousId();
this.anonymousId = anonymousId.idCookie;
const isGeneratedAnonymousId = anonymousId.generated;

const agent = await this.fingerprintClient.get();
this.deviceFingerprint = agent.visitorId;
private anonymousIdCookieName = "";
private publishableKey = "";

if (!publishableKey) {
throw Error("IntegrationError");
}
constructor({publishableKey, cookieDomain, cookieName}: AuthsignalOptions) {
this.publishableKey = publishableKey;

// If the anonymous Id is newly generated
// register it to the authsignal backend
if (isGeneratedAnonymousId) {
const registerAnonymousIdRequest = this.buildRegisterAnonymousIdRequest();
await this.registerAnonymousId(registerAnonymousIdRequest);
}
}

async identify(props: UserProps): Promise<void> {
console.log("AS user identified", props);

const request = {...props, anonymousId: this.anonymousId};
return await this.registerIdentity(request);
}

private getAnonymousId(): AnonymousId {
const idCookie = getCookie(this.idCookieName);
if (idCookie) {
return {idCookie, generated: false};
this.cookieDomain = cookieDomain || getCookieDomain();
this.anonymousIdCookieName = cookieName || "__as_aid";

const idCookie = getCookie(this.anonymousIdCookieName);
if (!idCookie) {
this.anonymousId = uuidv4();
setCookie({
name: this.anonymousIdCookieName,
value: this.anonymousId,
expire: Infinity,
domain: this.cookieDomain,
secure: document.location.protocol !== "http:",
});
}
const newId = generateId();

setCookie("__as_aid", newId, Infinity, this.cookieDomain, document.location.protocol !== "http:");

return {idCookie: newId, generated: true};
}

mfa({url}: Mfa) {
Expand Down Expand Up @@ -104,67 +57,4 @@ export class AuthsignalBrowser {
});
}
}

private async registerIdentity(request: RegisterIdentityRequest): Promise<void> {
console.log(request);
const result = await this.sendJson("/identity", request);

return result;
}

private async registerAnonymousId(request: RegisterAnonymousIdRequest): Promise<void> {
console.log(request);
const result = await this.sendJson("/register", request);

return result;
}

private buildRegisterAnonymousIdRequest(): RegisterAnonymousIdRequest {
const now = new Date();

return {
deviceFingerprint: this.deviceFingerprint || "",
anonymousId: this.anonymousId,
sourceHost: this.cookieDomain,
utcTime: reformatDate(now.toISOString()),
clientData: {
userAgent: navigator.userAgent || "",
fonts: screen.width + "x" + screen.height,
language: navigator.language,
tzOffset: now.getTimezoneOffset(),
screenResolution: screen.width + "x" + screen.height,
viewPort:
Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0) +
"x" +
Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0),
},
};
}

private sendJson(path: string, payload: unknown) {
const jsonString = JSON.stringify(payload);
const url = `${this.trackingHost}/api/v1/client/${path}?publishableKey=${this.publishableKey}`;

// Think about encapsulating the payloads around a message contract
// SDK client versions, additional meta data
return this.xmlHttpReqTransport(url, jsonString);
}

private xmlHttpReqTransport(url: string, json: string): Promise<void> {
const req = new XMLHttpRequest();
return new Promise((resolve, reject) => {
req.onerror = () => {
reject(new Error(`Failed to send JSON. See console logs`));
};
req.onload = () => {
if (req.status !== 200) {
reject(new Error(`Failed to send JSON. Error code: ${req.status}. See logs for details`));
}
resolve();
};
req.open("POST", url);
req.setRequestHeader("Content-Type", "application/json");
req.send(json);
});
}
}
42 changes: 9 additions & 33 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import {v4 as uuidv4} from "uuid";
type CookieOptions = {
name: string;
value: string;
expire: number;
domain: string;
secure: boolean;
};

export const setCookie = (name: string, value: string, expire: number, domain: string, secure: boolean): void => {
export const setCookie = ({name, value, expire, domain, secure}: CookieOptions): void => {
const expireString = expire === Infinity ? " expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + expire;
document.cookie =
encodeURIComponent(name) +
Expand All @@ -13,11 +19,7 @@ export const setCookie = (name: string, value: string, expire: number, domain: s
};

export const getCookieDomain = (): string => {
return location.hostname.replace("www.", "");
};

export const generateId = (): string => {
return uuidv4();
return document.location.hostname.replace("www.", "");
};

export const getCookie = (name: string) => {
Expand All @@ -35,29 +37,3 @@ export const getCookie = (name: string) => {
) || null
);
};

export const getHostWithProtocol = (host: string) => {
while (endsWith(host, "/")) {
host = host.substr(0, host.length - 1);
}
if (host.indexOf("https://") === 0 || host.indexOf("https://") === 0) {
return host;
} else {
return "//" + host;
}
};

function endsWith(str: string, suffix: string) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

export const reformatDate = (strDate: string) => {
const end = strDate.split(".")[1];
if (!end) {
return strDate;
}
if (end.length >= 7) {
return strDate;
}
return strDate.slice(0, -1) + "0".repeat(7 - end.length) + "Z";
};
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export {authsignalBrowser, AuthsignalBrowser} from "./AuthsignalBrowser";
export {AuthsignalBrowser} from "./AuthsignalBrowser";
export * from "./types";
32 changes: 1 addition & 31 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,8 @@ export type Mfa = {
url: string;
};