1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-07 04:03:29 +00:00

PM-20532 - SendTokenApiService - improve error handling

This commit is contained in:
Jared Snider
2025-06-05 17:11:31 -04:00
parent 67285507a2
commit b69b585b4f

View File

@@ -7,12 +7,28 @@ import { SendTokenApiService as SendTokenApiServiceAbstraction } from "../abstra
import { SendAccessToken } from "../models/send-access-token";
export type SendTokenApiRetrievalError =
| "password-required"
| "invalid-request"
| "send-id-required"
| "password-hash-required"
| "email-and-otp-required"
| "invalid-password"
| "invalid-grant"
| "invalid-password-hash"
| "invalid-otp"
| "json-parse-error"
| "unknown-error";
const INVALID_REQUEST_ERROR_MAPPING: Record<string, SendTokenApiRetrievalError> = {
"send_id is required.": "send-id-required",
"Password hash is required.": "password-hash-required",
"": "invalid-request", // This is a catch-all for any null/undefined invalid request error descriptions
};
const INVALID_GRANT_ERROR_MAPPING: Record<string, SendTokenApiRetrievalError> = {
"Password hash invalid.": "invalid-password-hash",
"Invalid OTP.": "invalid-otp",
"": "invalid-grant", // This is a catch-all for any null/undefined invalid grant error descriptions
};
export class SendTokenApiService implements SendTokenApiServiceAbstraction {
constructor(
private environmentService: EnvironmentService,
@@ -42,21 +58,45 @@ export class SendTokenApiService implements SendTokenApiServiceAbstraction {
});
const response = await this.apiService.fetch(req);
const responseJson = await response.json();
// TODO: Determine if we need the isJsonResponse check here like API service
let responseJson: any;
try {
responseJson = await response.json();
} catch {
// Only expected for server runtime exceptions or maybe CORS errors
return "json-parse-error";
}
if (response.status === 200) {
const sendAccessToken = SendAccessToken.fromResponseData(responseJson);
return sendAccessToken;
} else if (response.status === 400) {
if (responseJson?.error === "invalid_request") {
if (responseJson?.error_description === "Password is required.") {
return "password-required";
} else if (responseJson?.error_description === "Email and OTP are required.") {
return "email-and-otp-required";
}
}
}
if (response.status === 400 && responseJson?.error) {
return this.mapTokenResponseToError(responseJson.error, responseJson.error_description);
}
// TODO: maybe log this error?
return "unknown-error";
}
private mapTokenResponseToError(
error: string,
errorDescription?: string,
): SendTokenApiRetrievalError {
const errorDescKey = errorDescription ?? "";
switch (error) {
case "invalid_request": {
return INVALID_REQUEST_ERROR_MAPPING[errorDescKey] || "invalid-request";
}
case "invalid_grant": {
return INVALID_GRANT_ERROR_MAPPING[errorDescKey] || "invalid-grant";
}
default:
return "unknown-error";
}
}
}