Skip to content

Commit b16df14

Browse files
authored
Improve error responses (#116)
* . * .
1 parent b659672 commit b16df14

File tree

17 files changed

+163
-76
lines changed

17 files changed

+163
-76
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@authsignal/browser",
3-
"version": "1.12.7",
3+
"version": "1.13.0",
44
"type": "module",
55
"main": "dist/index.js",
66
"module": "dist/index.js",
@@ -51,5 +51,6 @@
5151
},
5252
"files": [
5353
"dist"
54-
]
54+
],
55+
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
5556
}

src/api/types/shared.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {CredentialDeviceType} from "@simplewebauthn/browser";
2+
import {ErrorCode} from "../../types";
23

34
export type ApiClientOptions = {
45
baseUrl: string;
@@ -26,10 +27,12 @@ export type VerifyResponse = {
2627
};
2728

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

3538
export enum VerificationMethod {

src/authsignal.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class Authsignal {
3434
profilingId = "";
3535
cookieDomain = "";
3636
anonymousIdCookieName = "";
37-
37+
enableLogging = false;
3838
passkey: Passkey;
3939
totp: Totp;
4040
email: Email;
@@ -51,6 +51,7 @@ export class Authsignal {
5151
baseUrl = DEFAULT_BASE_URL,
5252
tenantId,
5353
onTokenExpired,
54+
enableLogging = false,
5455
}: AuthsignalOptions) {
5556
this.cookieDomain = cookieDomain || getCookieDomain();
5657
this.anonymousIdCookieName = cookieName;
@@ -75,15 +76,23 @@ export class Authsignal {
7576
});
7677
}
7778

78-
this.passkey = new Passkey({tenantId, baseUrl, anonymousId: this.anonymousId, onTokenExpired});
79-
this.totp = new Totp({tenantId, baseUrl, onTokenExpired});
80-
this.email = new Email({tenantId, baseUrl, onTokenExpired});
81-
this.emailML = new EmailMagicLink({tenantId, baseUrl, onTokenExpired});
82-
this.sms = new Sms({tenantId, baseUrl, onTokenExpired});
83-
this.securityKey = new SecurityKey({tenantId, baseUrl, onTokenExpired});
84-
this.qrCode = new QrCode({tenantId, baseUrl});
85-
this.push = new Push({tenantId, baseUrl, onTokenExpired});
86-
this.whatsapp = new Whatsapp({tenantId, baseUrl, onTokenExpired});
79+
this.enableLogging = enableLogging;
80+
81+
this.passkey = new Passkey({
82+
tenantId,
83+
baseUrl,
84+
anonymousId: this.anonymousId,
85+
onTokenExpired,
86+
enableLogging: this.enableLogging,
87+
});
88+
this.totp = new Totp({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
89+
this.email = new Email({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
90+
this.emailML = new EmailMagicLink({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
91+
this.sms = new Sms({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
92+
this.securityKey = new SecurityKey({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
93+
this.qrCode = new QrCode({tenantId, baseUrl, enableLogging: this.enableLogging});
94+
this.push = new Push({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
95+
this.whatsapp = new Whatsapp({tenantId, baseUrl, onTokenExpired, enableLogging: this.enableLogging});
8796
}
8897

8998
setToken(token: string) {

src/email-magic-link.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type EmailMagicLinkOptions = {
88
baseUrl: string;
99
tenantId: string;
1010
onTokenExpired?: () => void;
11+
enableLogging: boolean;
1112
};
1213

1314
type EnrollParams = {
@@ -17,8 +18,10 @@ type EnrollParams = {
1718
export class EmailMagicLink {
1819
private api: EmailMagicLinkApiClient;
1920
private cache = TokenCache.shared;
21+
private enableLogging = false;
2022

21-
constructor({baseUrl, tenantId, onTokenExpired}: EmailMagicLinkOptions) {
23+
constructor({baseUrl, tenantId, onTokenExpired, enableLogging}: EmailMagicLinkOptions) {
24+
this.enableLogging = enableLogging;
2225
this.api = new EmailMagicLinkApiClient({baseUrl, tenantId, onTokenExpired});
2326
}
2427

@@ -29,7 +32,7 @@ export class EmailMagicLink {
2932

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

32-
return handleApiResponse(response);
35+
return handleApiResponse({response, enableLogging: this.enableLogging});
3336
}
3437

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

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

42-
return handleApiResponse(response);
45+
return handleApiResponse({response, enableLogging: this.enableLogging});
4346
}
4447

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

56-
return handleApiResponse(response);
59+
return handleApiResponse({response, enableLogging: this.enableLogging});
5760
}
5861
}

src/email.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type EmailOptions = {
88
baseUrl: string;
99
tenantId: string;
1010
onTokenExpired?: () => void;
11+
enableLogging: boolean;
1112
};
1213

1314
type EnrollParams = {
@@ -21,8 +22,10 @@ type VerifyParams = {
2122
export class Email {
2223
private api: EmailApiClient;
2324
private cache = TokenCache.shared;
25+
private enableLogging = false;
2426

25-
constructor({baseUrl, tenantId, onTokenExpired}: EmailOptions) {
27+
constructor({baseUrl, tenantId, onTokenExpired, enableLogging}: EmailOptions) {
28+
this.enableLogging = enableLogging;
2629
this.api = new EmailApiClient({baseUrl, tenantId, onTokenExpired});
2730
}
2831

@@ -33,7 +36,7 @@ export class Email {
3336

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

36-
return handleApiResponse(response);
39+
return handleApiResponse({response, enableLogging: this.enableLogging});
3740
}
3841

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

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

46-
return handleApiResponse(response);
49+
return handleApiResponse({response, enableLogging: this.enableLogging});
4750
}
4851

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

60-
return handleApiResponse(response);
63+
return handleApiResponse({response, enableLogging: this.enableLogging});
6164
}
6265
}

src/helpers.ts

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,48 @@ export function getCookie(name: string) {
4141
);
4242
}
4343

44-
export function handleErrorResponse(errorResponse: ErrorResponse) {
45-
const error = errorResponse.errorDescription ?? errorResponse.error;
44+
type HandleErrorResponseParams = {
45+
errorResponse: ErrorResponse;
46+
enableLogging: boolean;
47+
};
4648

47-
console.error(error);
49+
export function handleErrorResponse({errorResponse, enableLogging}: HandleErrorResponseParams) {
50+
if (enableLogging) {
51+
console.error(
52+
`[Authsignal] ${errorResponse.errorCode}${
53+
errorResponse.errorDescription ? `: ${errorResponse.errorDescription}` : ""
54+
}`
55+
);
56+
}
57+
58+
const error = errorResponse.errorDescription ?? errorResponse.error;
4859

4960
return {
5061
error,
62+
errorCode: errorResponse.errorCode,
63+
errorDescription: errorResponse.errorDescription,
5164
};
5265
}
5366

54-
export function handleApiResponse<T>(response: ErrorResponse | T) {
67+
type HandleApiResponseParams<T> = {
68+
response: ErrorResponse | T;
69+
enableLogging: boolean;
70+
};
71+
72+
export function handleApiResponse<T>({response, enableLogging}: HandleApiResponseParams<T>) {
5573
if (response && typeof response === "object" && "error" in response) {
5674
const error = response.errorDescription ?? response.error;
5775

58-
console.error(error);
76+
if (enableLogging) {
77+
console.error(
78+
`[Authsignal] ${response.errorCode}${response.errorDescription ? `: ${response.errorDescription}` : ""}`
79+
);
80+
}
5981

6082
return {
6183
error,
84+
errorCode: response.errorCode,
85+
errorDescription: response.errorDescription,
6286
};
6387
} else if (
6488
response &&
@@ -76,7 +100,7 @@ export function handleApiResponse<T>(response: ErrorResponse | T) {
76100
};
77101
} else {
78102
return {
79-
data: response,
103+
data: response as T,
80104
};
81105
}
82106
}

src/passkey.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type PasskeyOptions = {
1717
tenantId: string;
1818
anonymousId: string;
1919
onTokenExpired?: () => void;
20+
enableLogging: boolean;
2021
};
2122

2223
type SignUpParams = {
@@ -59,10 +60,12 @@ export class Passkey {
5960
private passkeyLocalStorageKey = "as_user_passkey_map";
6061
private anonymousId: string;
6162
private cache = TokenCache.shared;
63+
private enableLogging = false;
6264

63-
constructor({baseUrl, tenantId, anonymousId, onTokenExpired}: PasskeyOptions) {
65+
constructor({baseUrl, tenantId, anonymousId, onTokenExpired, enableLogging}: PasskeyOptions) {
6466
this.api = new PasskeyApiClient({baseUrl, tenantId, onTokenExpired});
6567
this.anonymousId = anonymousId;
68+
this.enableLogging = enableLogging;
6669
}
6770

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

98101
if ("error" in optionsResponse) {
99-
return handleErrorResponse(optionsResponse);
102+
return handleErrorResponse({errorResponse: optionsResponse, enableLogging: this.enableLogging});
100103
}
101104

102105
try {
@@ -111,7 +114,7 @@ export class Passkey {
111114
});
112115

113116
if ("error" in addAuthenticatorResponse) {
114-
return handleErrorResponse(addAuthenticatorResponse);
117+
return handleErrorResponse({errorResponse: addAuthenticatorResponse, enableLogging: this.enableLogging});
115118
}
116119

117120
if (addAuthenticatorResponse.isVerified) {
@@ -165,7 +168,7 @@ export class Passkey {
165168
if (challengeResponse && "error" in challengeResponse) {
166169
autofillRequestPending = false;
167170

168-
return handleErrorResponse(challengeResponse);
171+
return handleErrorResponse({errorResponse: challengeResponse, enableLogging: this.enableLogging});
169172
}
170173

171174
const optionsResponse =
@@ -180,7 +183,7 @@ export class Passkey {
180183
if ("error" in optionsResponse) {
181184
autofillRequestPending = false;
182185

183-
return handleErrorResponse(optionsResponse);
186+
return handleErrorResponse({errorResponse: optionsResponse, enableLogging: this.enableLogging});
184187
}
185188

186189
try {
@@ -204,7 +207,7 @@ export class Passkey {
204207
if ("error" in verifyResponse) {
205208
autofillRequestPending = false;
206209

207-
return handleErrorResponse(verifyResponse);
210+
return handleErrorResponse({errorResponse: verifyResponse, enableLogging: this.enableLogging});
208211
}
209212

210213
if (verifyResponse.isVerified) {

src/push.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type PushOptions = {
88
baseUrl: string;
99
tenantId: string;
1010
onTokenExpired?: () => void;
11+
enableLogging: boolean;
1112
};
1213

1314
type ChallengeParams = {
@@ -21,8 +22,10 @@ type VerifyParams = {
2122
export class Push {
2223
private api: PushApiClient;
2324
private cache = TokenCache.shared;
25+
private enableLogging = false;
2426

25-
constructor({baseUrl, tenantId, onTokenExpired}: PushOptions) {
27+
constructor({baseUrl, tenantId, onTokenExpired, enableLogging}: PushOptions) {
28+
this.enableLogging = enableLogging;
2629
this.api = new PushApiClient({baseUrl, tenantId, onTokenExpired});
2730
}
2831

@@ -33,7 +36,7 @@ export class Push {
3336

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

36-
return handleApiResponse(response);
39+
return handleApiResponse({response, enableLogging: this.enableLogging});
3740
}
3841

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

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

46-
return handleApiResponse(response);
49+
return handleApiResponse({response, enableLogging: this.enableLogging});
4750
}
4851
}

src/qr-code.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {WebSocketQrHandler} from "./qr-code/websocket-qr-handler";
88
type QrCodeOptions = {
99
baseUrl: string;
1010
tenantId: string;
11+
enableLogging: boolean;
1112
};
1213

1314
export type ChallengeParams = {
@@ -31,10 +32,12 @@ export class QrCode {
3132
private handler: QrHandler | null = null;
3233
private baseUrl: string;
3334
private tenantId: string;
35+
private enableLogging = false;
3436

35-
constructor({baseUrl, tenantId}: QrCodeOptions) {
37+
constructor({baseUrl, tenantId, enableLogging}: QrCodeOptions) {
3638
this.baseUrl = baseUrl;
3739
this.tenantId = tenantId;
40+
this.enableLogging = enableLogging;
3841
}
3942

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

4750
if (polling) {
48-
this.handler = new RestQrHandler({baseUrl: this.baseUrl, tenantId: this.tenantId});
51+
this.handler = new RestQrHandler({
52+
baseUrl: this.baseUrl,
53+
tenantId: this.tenantId,
54+
enableLogging: this.enableLogging,
55+
});
4956
} else {
50-
this.handler = new WebSocketQrHandler({baseUrl: this.baseUrl, tenantId: this.tenantId});
57+
this.handler = new WebSocketQrHandler({
58+
baseUrl: this.baseUrl,
59+
tenantId: this.tenantId,
60+
enableLogging: this.enableLogging,
61+
});
5162
}
5263

5364
return this.handler.challenge(challengeParams);

0 commit comments

Comments
 (0)