Skip to content

Commit

Permalink
Tidy up (#5)
Browse files Browse the repository at this point in the history
* Remove unused var

* Convert snake case to camel case

* Rename class to AuthsignalBrowser

* Refactor challenge methods into single method

* Add mfa method

* Fix ts errors

* Inject style tag for popup

* Remove outdated README info
  • Loading branch information
hwhmeikle committed May 30, 2022
1 parent d426060 commit e332c20
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 93 deletions.
37 changes: 0 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1 @@
# authsignal-browser

## API

### challengeWithPopup

You will need to add the following CSS to your application if using this method:

```css
.dialog-container,
.dialog-overlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}

.dialog-container {
z-index: 2;
display: flex;
}

.dialog-container[aria-hidden="true"] {
display: none;
}

.dialog-overlay {
background-color: rgba(43, 46, 56, 0.9);
}

.dialog-content {
margin: auto;
z-index: 2;
position: relative;
background-color: white;
}
```
68 changes: 36 additions & 32 deletions src/Authsignal.ts → src/AuthsignalBrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ import * as FingerprintJS from "@fingerprintjs/fingerprintjs";

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

export function authsignalClient(publishableKey: string, options?: AuthsignalOptions): AuthsignalClient {
const client = new AuthsignalClient();
export function authsignalBrowser(publishableKey: string, options?: AuthsignalOptions): AuthsignalBrowser {
const client = new AuthsignalBrowser();
client.init(publishableKey, options);
return client;
}
export class AuthsignalClient {
export class AuthsignalBrowser {
private anonymousId = "";
private initialized = false;
private publishableKey = "";
private cookieDomain = "";
private idCookieName = "";
Expand All @@ -28,10 +28,10 @@ export class AuthsignalClient {
private deviceFingerprint?: string;

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

this.trackingHost = getHostWithProtocol(options?.tracking_host || "t.authsignal.com");
this.trackingHost = getHostWithProtocol(options?.trackingHost || "t.authsignal.com");
this.fingerprintClient = await FingerprintJS.load({
monitoring: false,
});
Expand All @@ -54,8 +54,6 @@ export class AuthsignalClient {
const registerAnonymousIdRequest = this.buildRegisterAnonymousIdRequest();
await this.registerAnonymousId(registerAnonymousIdRequest);
}

this.initialized = true;
}

async identify(props: UserProps): Promise<void> {
Expand All @@ -65,7 +63,7 @@ export class AuthsignalClient {
return await this.registerIdentity(request);
}

getAnonymousId(): AnnoymousId {
private getAnonymousId(): AnonymousId {
const idCookie = getCookie(this.idCookieName);
if (idCookie) {
return {idCookie, generated: false};
Expand All @@ -77,28 +75,34 @@ export class AuthsignalClient {
return {idCookie: newId, generated: true};
}

challengeWithRedirect({challengeUrl}: AuthsignalChallenge) {
window.location.href = challengeUrl;
mfa({url}: Mfa) {
window.location.href = url;
}

challengeWithPopup({challengeUrl}: AuthsignalChallenge): Promise<boolean> {
const Popup = new PopupHandler();

Popup.show({challengeUrl});

return new Promise<boolean>((resolve, reject) => {
const handleChallenge = (event: MessageEvent) => {
if (event.data === "authsignal-challenge-success") {
Popup.close();
resolve(true);
} else if (event.data === "authsignal-challenge-failure") {
Popup.close();
reject(false);
}
};

window.addEventListener("message", handleChallenge, false);
});
challenge(challenge: {mode?: "redirect"} & Challenge): undefined;
challenge(challenge: {mode: "popup"} & Challenge): Promise<boolean>;
challenge({challengeUrl, mode = "redirect"}: Challenge) {
if (mode === "redirect") {
window.location.href = challengeUrl;
} else {
const Popup = new PopupHandler();

Popup.show({challengeUrl: challengeUrl});

return new Promise<boolean>((resolve, reject) => {
const handleChallenge = (event: MessageEvent) => {
if (event.data === "authsignal-challenge-success") {
Popup.close();
resolve(true);
} else if (event.data === "authsignal-challenge-failure") {
Popup.close();
reject(false);
}
};

window.addEventListener("message", handleChallenge, false);
});
}
}

private async registerIdentity(request: RegisterIdentityRequest): Promise<void> {
Expand Down
75 changes: 57 additions & 18 deletions src/PopupHandler.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,92 @@
import A11yDialog from "a11y-dialog";
import {AuthsignalChallenge} from "./types";
import {Challenge} from "./types";

const DIALOG_CONTAINER_ID = "authsignal-popup";
const DIALOG_CONTENT_ID = "authsignal-popup-content";
const CONTAINER_ID = "__authsignal-popup-container";
const CONTENT_ID = "__authsignal-popup-content";
const OVERLAY_ID = "__authsignal-popup-overlay";
const STYLE_ID = "__authsignal-popup-style";

class PopupHandler {
private popup: A11yDialog | null = null;

constructor() {
if (document.querySelector(`#${DIALOG_CONTAINER_ID}`)) {
if (document.querySelector(`#${CONTAINER_ID}`)) {
throw new Error("Multiple instances of Authsignal popup is not supported.");
}

this.create();
}

create() {
// Dialog container
// Create dialog container
const container = document.createElement("div");
container.setAttribute("id", DIALOG_CONTAINER_ID);
container.setAttribute("id", CONTAINER_ID);
container.setAttribute("aria-hidden", "true");
container.setAttribute("class", "dialog-container");

// Dialog overlay
// Create dialog overlay
const overlay = document.createElement("div");
overlay.setAttribute("class", "dialog-overlay");
overlay.setAttribute("id", OVERLAY_ID);
overlay.setAttribute("data-a11y-dialog-hide", "true");
container.appendChild(overlay);

// Dialog content
// Create dialog content
const content = document.createElement("div");
content.setAttribute("class", "dialog-content");
content.setAttribute("id", DIALOG_CONTENT_ID);
container.appendChild(content);
content.setAttribute("id", CONTENT_ID);

document.body.appendChild(container);

// Create CSS for dialog
const style = document.createElement("style");
style.setAttribute("id", STYLE_ID);
style.textContent = `
#${CONTAINER_ID},
#${OVERLAY_ID} {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
#${CONTAINER_ID} {
z-index: 2;
display: flex;
}
#${CONTAINER_ID}[aria-hidden='true'] {
display: none;
}
#${OVERLAY_ID} {
background-color: rgba(43, 46, 56, 0.9);
}
#${CONTENT_ID} {
margin: auto;
z-index: 2;
position: relative;
background-color: white;
}
`;

// Attach the created elements
document.head.insertAdjacentElement("beforeend", style);
container.appendChild(overlay);
container.appendChild(content);

this.popup = new A11yDialog(container);
this.popup.on("hide", this.destroy);
}

destroy() {
const dialogEl = document.querySelector(`#${DIALOG_CONTAINER_ID}`);
if (dialogEl) {
const dialogEl = document.querySelector(`#${CONTAINER_ID}`);
const styleEl = document.querySelector(`#${STYLE_ID}`);
if (dialogEl && styleEl) {
document.body.removeChild(dialogEl);
document.head.removeChild(styleEl);
}
}

show({challengeUrl}: {challengeUrl: AuthsignalChallenge["challengeUrl"]}) {
show({challengeUrl}: {challengeUrl: Challenge["challengeUrl"]}) {
if (!this.popup) {
throw new Error("Popup is not initialized");
}
Expand All @@ -60,7 +99,7 @@ class PopupHandler {
iframe.setAttribute("height", "600");
iframe.setAttribute("frameborder", "0");

const dialogContent = document.querySelector(`#${DIALOG_CONTENT_ID}`);
const dialogContent = document.querySelector(`#${CONTENT_ID}`);
if (dialogContent) {
dialogContent.appendChild(iframe);
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export {AuthsignalClient, authsignalClient} from "./Authsignal";
export {authsignalBrowser, AuthsignalBrowser} from "./AuthsignalBrowser";
export * from "./types";
15 changes: 10 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
export type AuthsignalChallenge = {
export type Challenge = {
challengeUrl: string;
mode?: "popup" | "redirect";
};

export type AnnoymousId = {
export type Mfa = {
url: string;
};

export type AnonymousId = {
idCookie: string;
generated: boolean;
};
Expand Down Expand Up @@ -38,17 +43,17 @@ export type AuthsignalOptions = {
* Cookie domain that will be used to identify
* users. If not set, location.hostname will be used
*/
cookie_domain?: string;
cookieDomain?: string;
/**
* Tracking host (where API calls will be sent). If not set,
* we'd try to do the best to "guess" it. Last resort is t.authsignal.com.
*
* Though this parameter is not required, it's highly recommended to set is explicitly
*/
tracking_host?: string;
trackingHost?: string;

/**
* Name of id cookie. __eventn_id by default
*/
cookie_name?: string;
cookieName?: string;
};

0 comments on commit e332c20

Please sign in to comment.