Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@authsignal/browser",
"version": "1.12.7",
"version": "1.13.0",
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
Expand Down Expand Up @@ -51,5 +51,6 @@
},
"files": [
"dist"
]
],
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
}
11 changes: 7 additions & 4 deletions src/api/types/shared.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {CredentialDeviceType} from "@simplewebauthn/browser";
import {ErrorCode} from "../../types";

export type ApiClientOptions = {
baseUrl: string;
Expand Down Expand Up @@ -26,10 +27,12 @@ export type VerifyResponse = {
};

export type ErrorResponse = {
error: string;
// eslint-disable-next-line @typescript-eslint/ban-types -- This is a valid use case for an empty object
errorCode?: "expired_token" | (string & {});
errorDescription?: string;
/**
* @deprecated Use errorCode and errorDescription instead
*/
error?: string;
errorCode?: ErrorCode;
errorDescription?: string | undefined;
};

export enum VerificationMethod {
Expand Down
29 changes: 19 additions & 10 deletions src/authsignal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class Authsignal {
profilingId = "";
cookieDomain = "";
anonymousIdCookieName = "";

enableLogging = false;
passkey: Passkey;
totp: Totp;
email: Email;
Expand All @@ -51,6 +51,7 @@ export class Authsignal {
baseUrl = DEFAULT_BASE_URL,
tenantId,
onTokenExpired,
enableLogging = false,
}: AuthsignalOptions) {
this.cookieDomain = cookieDomain || getCookieDomain();
this.anonymousIdCookieName = cookieName;
Expand All @@ -75,15 +76,23 @@ export class Authsignal {
});
}

this.passkey = new Passkey({tenantId, baseUrl, anonymousId: this.anonymousId, onTokenExpired});
this.totp = new Totp({tenantId, baseUrl, onTokenExpired});
this.email = new Email({tenantId, baseUrl, onTokenExpired});
this.emailML = new EmailMagicLink({tenantId, baseUrl, onTokenExpired});
this.sms = new Sms({tenantId, baseUrl, onTokenExpired});
this.securityKey = new SecurityKey({tenantId, baseUrl, onTokenExpired});
this.qrCode = new QrCode({tenantId, baseUrl});
this.push = new Push({tenantId, baseUrl, onTokenExpired});
this.whatsapp = new Whatsapp({tenantId, baseUrl, onTokenExpired});
this.enableLogging = enableLogging;

this.passkey = new Passkey({
tenantId,
baseUrl,
anonymousId: this.anonymousId,
onTokenExpired,
enableLogging: this.enableLogging,
});
this.totp = new Totp({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
this.email = new Email({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
this.emailML = new EmailMagicLink({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
this.sms = new Sms({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
this.securityKey = new SecurityKey({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
this.qrCode = new QrCode({tenantId, baseUrl, enableLogging: this.enableLogging});
this.push = new Push({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
this.whatsapp = new Whatsapp({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
}

setToken(token: string) {
Expand Down
11 changes: 7 additions & 4 deletions src/email-magic-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type EmailMagicLinkOptions = {
baseUrl: string;
tenantId: string;
onTokenExpired?: () => void;
enableLogging: boolean;
};

type EnrollParams = {
Expand All @@ -17,8 +18,10 @@ type EnrollParams = {
export class EmailMagicLink {
private api: EmailMagicLinkApiClient;
private cache = TokenCache.shared;
private enableLogging = false;

constructor({baseUrl, tenantId, onTokenExpired}: EmailMagicLinkOptions) {
constructor({baseUrl, tenantId, onTokenExpired, enableLogging}: EmailMagicLinkOptions) {
this.enableLogging = enableLogging;
this.api = new EmailMagicLinkApiClient({baseUrl, tenantId, onTokenExpired});
}

Expand All @@ -29,7 +32,7 @@ export class EmailMagicLink {

const response = await this.api.enroll({token: this.cache.token, email});

return handleApiResponse(response);
return handleApiResponse({response, enableLogging: this.enableLogging});
}

async challenge(): Promise<AuthsignalResponse<ChallengeResponse>> {
Expand All @@ -39,7 +42,7 @@ export class EmailMagicLink {

const response = await this.api.challenge({token: this.cache.token});

return handleApiResponse(response);
return handleApiResponse({response, enableLogging: this.enableLogging});
}

async checkVerificationStatus(): Promise<AuthsignalResponse<VerifyResponse>> {
Expand All @@ -53,6 +56,6 @@ export class EmailMagicLink {
this.cache.token = response.accessToken;
}

return handleApiResponse(response);
return handleApiResponse({response, enableLogging: this.enableLogging});
}
}
11 changes: 7 additions & 4 deletions src/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type EmailOptions = {
baseUrl: string;
tenantId: string;
onTokenExpired?: () => void;
enableLogging: boolean;
};

type EnrollParams = {
Expand All @@ -21,8 +22,10 @@ type VerifyParams = {
export class Email {
private api: EmailApiClient;
private cache = TokenCache.shared;
private enableLogging = false;

constructor({baseUrl, tenantId, onTokenExpired}: EmailOptions) {
constructor({baseUrl, tenantId, onTokenExpired, enableLogging}: EmailOptions) {
this.enableLogging = enableLogging;
this.api = new EmailApiClient({baseUrl, tenantId, onTokenExpired});
}

Expand All @@ -33,7 +36,7 @@ export class Email {

const response = await this.api.enroll({token: this.cache.token, email});

return handleApiResponse(response);
return handleApiResponse({response, enableLogging: this.enableLogging});
}

async challenge(): Promise<AuthsignalResponse<ChallengeResponse>> {
Expand All @@ -43,7 +46,7 @@ export class Email {

const response = await this.api.challenge({token: this.cache.token});

return handleApiResponse(response);
return handleApiResponse({response, enableLogging: this.enableLogging});
}

async verify({code}: VerifyParams): Promise<AuthsignalResponse<VerifyResponse>> {
Expand All @@ -57,6 +60,6 @@ export class Email {
this.cache.token = response.accessToken;
}

return handleApiResponse(response);
return handleApiResponse({response, enableLogging: this.enableLogging});
}
}
36 changes: 30 additions & 6 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,48 @@ export function getCookie(name: string) {
);
}

export function handleErrorResponse(errorResponse: ErrorResponse) {
const error = errorResponse.errorDescription ?? errorResponse.error;
type HandleErrorResponseParams = {
errorResponse: ErrorResponse;
enableLogging: boolean;
};

console.error(error);
export function handleErrorResponse({errorResponse, enableLogging}: HandleErrorResponseParams) {
if (enableLogging) {
console.error(
`[Authsignal] ${errorResponse.errorCode}${
errorResponse.errorDescription ? `: ${errorResponse.errorDescription}` : ""
}`
);
}

const error = errorResponse.errorDescription ?? errorResponse.error;

return {
error,
errorCode: errorResponse.errorCode,
errorDescription: errorResponse.errorDescription,
};
}

export function handleApiResponse<T>(response: ErrorResponse | T) {
type HandleApiResponseParams<T> = {
response: ErrorResponse | T;
enableLogging: boolean;
};

export function handleApiResponse<T>({response, enableLogging}: HandleApiResponseParams<T>) {
if (response && typeof response === "object" && "error" in response) {
const error = response.errorDescription ?? response.error;

console.error(error);
if (enableLogging) {
console.error(
`[Authsignal] ${response.errorCode}${response.errorDescription ? `: ${response.errorDescription}` : ""}`
);
}

return {
error,
errorCode: response.errorCode,
errorDescription: response.errorDescription,
};
} else if (
response &&
Expand All @@ -76,7 +100,7 @@ export function handleApiResponse<T>(response: ErrorResponse | T) {
};
} else {
return {
data: response,
data: response as T,
};
}
}
Expand Down
15 changes: 9 additions & 6 deletions src/passkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type PasskeyOptions = {
tenantId: string;
anonymousId: string;
onTokenExpired?: () => void;
enableLogging: boolean;
};

type SignUpParams = {
Expand Down Expand Up @@ -59,10 +60,12 @@ export class Passkey {
private passkeyLocalStorageKey = "as_user_passkey_map";
private anonymousId: string;
private cache = TokenCache.shared;
private enableLogging = false;

constructor({baseUrl, tenantId, anonymousId, onTokenExpired}: PasskeyOptions) {
constructor({baseUrl, tenantId, anonymousId, onTokenExpired, enableLogging}: PasskeyOptions) {
this.api = new PasskeyApiClient({baseUrl, tenantId, onTokenExpired});
this.anonymousId = anonymousId;
this.enableLogging = enableLogging;
}

async signUp({
Expand Down Expand Up @@ -96,7 +99,7 @@ export class Passkey {
const optionsResponse = await this.api.registrationOptions(optionsInput);

if ("error" in optionsResponse) {
return handleErrorResponse(optionsResponse);
return handleErrorResponse({errorResponse: optionsResponse, enableLogging: this.enableLogging});
}

try {
Expand All @@ -111,7 +114,7 @@ export class Passkey {
});

if ("error" in addAuthenticatorResponse) {
return handleErrorResponse(addAuthenticatorResponse);
return handleErrorResponse({errorResponse: addAuthenticatorResponse, enableLogging: this.enableLogging});
}

if (addAuthenticatorResponse.isVerified) {
Expand Down Expand Up @@ -165,7 +168,7 @@ export class Passkey {
if (challengeResponse && "error" in challengeResponse) {
autofillRequestPending = false;

return handleErrorResponse(challengeResponse);
return handleErrorResponse({errorResponse: challengeResponse, enableLogging: this.enableLogging});
}

const optionsResponse =
Expand All @@ -180,7 +183,7 @@ export class Passkey {
if ("error" in optionsResponse) {
autofillRequestPending = false;

return handleErrorResponse(optionsResponse);
return handleErrorResponse({errorResponse: optionsResponse, enableLogging: this.enableLogging});
}

try {
Expand All @@ -204,7 +207,7 @@ export class Passkey {
if ("error" in verifyResponse) {
autofillRequestPending = false;

return handleErrorResponse(verifyResponse);
return handleErrorResponse({errorResponse: verifyResponse, enableLogging: this.enableLogging});
}

if (verifyResponse.isVerified) {
Expand Down
9 changes: 6 additions & 3 deletions src/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type PushOptions = {
baseUrl: string;
tenantId: string;
onTokenExpired?: () => void;
enableLogging: boolean;
};

type ChallengeParams = {
Expand All @@ -21,8 +22,10 @@ type VerifyParams = {
export class Push {
private api: PushApiClient;
private cache = TokenCache.shared;
private enableLogging = false;

constructor({baseUrl, tenantId, onTokenExpired}: PushOptions) {
constructor({baseUrl, tenantId, onTokenExpired, enableLogging}: PushOptions) {
this.enableLogging = enableLogging;
this.api = new PushApiClient({baseUrl, tenantId, onTokenExpired});
}

Expand All @@ -33,7 +36,7 @@ export class Push {

const response = await this.api.challenge({action, token: this.cache.token});

return handleApiResponse(response);
return handleApiResponse({response, enableLogging: this.enableLogging});
}

async verify({challengeId}: VerifyParams): Promise<AuthsignalResponse<PushVerifyResponse>> {
Expand All @@ -43,6 +46,6 @@ export class Push {

const response = await this.api.verify({challengeId, token: this.cache.token});

return handleApiResponse(response);
return handleApiResponse({response, enableLogging: this.enableLogging});
}
}
17 changes: 14 additions & 3 deletions src/qr-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {WebSocketQrHandler} from "./qr-code/websocket-qr-handler";
type QrCodeOptions = {
baseUrl: string;
tenantId: string;
enableLogging: boolean;
};

export type ChallengeParams = {
Expand All @@ -31,10 +32,12 @@ export class QrCode {
private handler: QrHandler | null = null;
private baseUrl: string;
private tenantId: string;
private enableLogging = false;

constructor({baseUrl, tenantId}: QrCodeOptions) {
constructor({baseUrl, tenantId, enableLogging}: QrCodeOptions) {
this.baseUrl = baseUrl;
this.tenantId = tenantId;
this.enableLogging = enableLogging;
}

async challenge(params: ChallengeParams): Promise<AuthsignalResponse<QrCodeChallengeResponse>> {
Expand All @@ -45,9 +48,17 @@ export class QrCode {
}

if (polling) {
this.handler = new RestQrHandler({baseUrl: this.baseUrl, tenantId: this.tenantId});
this.handler = new RestQrHandler({
baseUrl: this.baseUrl,
tenantId: this.tenantId,
enableLogging: this.enableLogging,
});
} else {
this.handler = new WebSocketQrHandler({baseUrl: this.baseUrl, tenantId: this.tenantId});
this.handler = new WebSocketQrHandler({
baseUrl: this.baseUrl,
tenantId: this.tenantId,
enableLogging: this.enableLogging,
});
}

return this.handler.challenge(challengeParams);
Expand Down
Loading
Loading