diff --git a/libs/common/src/auth/send-access/abstractions/send-token.service.ts b/libs/common/src/auth/send-access/abstractions/send-token.service.ts index d74a39b7a22..dd815c1a1d4 100644 --- a/libs/common/src/auth/send-access/abstractions/send-token.service.ts +++ b/libs/common/src/auth/send-access/abstractions/send-token.service.ts @@ -1,6 +1,9 @@ import { SendHashedPassword } from "../../../key-management/sends/send-password.service"; import { SendAccessToken } from "../models/send-access-token"; -import { TryGetSendAccessTokenError } from "../services/send-token.service"; +import { + GetSendAcccessTokenError, + TryGetSendAccessTokenError, +} from "../services/send-token.service"; export type SendAccessCredentialsType = "password" | "email-otp"; @@ -15,21 +18,7 @@ export type SendEmailOtpCredentials = { }; export type SendAccessCredentials = SendPasswordCredentials | SendEmailOtpCredentials; -// TODO: add JSdocs export abstract class SendTokenService { - // SendAccessTokens need to be stored in session storage once retrieved. - // All SendAccessTokens are scoped to a specific send id so all getting and setting should accept a send id. - - // TODO: should this abstraction have separate methods for requesting an access token from the server - // and for getting the access token from storage? - // One method that does both is ideal. - // We will need to extend inputs to include the send id and the credentials. - // We will also need to store the send access token with it's expires_in value so we know if it's expired - // so that we don't hand out an expired token to make a request. - - // Returned error types should be discriminated union with a type that can be conditioned off for logic. - - // TODO: define return types. // TODO: consider converting to observable. /** * Attempts to retrieve a SendAccessToken for the given sendId. @@ -46,10 +35,18 @@ export abstract class SendTokenService { sendId: string, ) => Promise; - abstract getSendAccessTokenWithCredentials: ( + /** + * Retrieves a SendAccessToken for the given sendId using the provided credentials. + * If the access token is successfully retrieved from the server, it stores the token in session storage and returns it. + * If the access token cannot be granted due to invalid credentials, it returns a GetSendAcccessTokenError. + * @param sendId The ID of the send to retrieve the access token for. + * @param sendAccessCredentials The credentials to use for accessing the send. + * @returns A promise that resolves to a SendAccessToken if found and valid, or a GetSendAcccessTokenError if not. + */ + abstract getSendAccessToken: ( sendId: string, sendAccessCredentials: SendAccessCredentials, - ) => Promise; + ) => Promise; /** * Hashes a password for send access. diff --git a/libs/common/src/auth/send-access/services/send-token-api.service.ts b/libs/common/src/auth/send-access/services/send-token-api.service.ts index 89d31d0a50a..e1ab19cd659 100644 --- a/libs/common/src/auth/send-access/services/send-token-api.service.ts +++ b/libs/common/src/auth/send-access/services/send-token-api.service.ts @@ -6,10 +6,12 @@ import { SendAccessTokenRequest } from "../../models/request/identity-token/send import { SendTokenApiService as SendTokenApiServiceAbstraction } from "../abstractions/send-token-api.service"; import { SendAccessToken } from "../models/send-access-token"; -export type SendTokenApiRetrievalError = "password-required" | "otp-required" | "unknown-error"; - -// Consider adding types for submission with credentials: -// | "invalid-password" +export type SendTokenApiRetrievalError = + | "password-required" + | "otp-required" + | "invalid-password" + | "invalid-otp" + | "unknown-error"; export class SendTokenApiService implements SendTokenApiServiceAbstraction { constructor( diff --git a/libs/common/src/auth/send-access/services/send-token.service.ts b/libs/common/src/auth/send-access/services/send-token.service.ts index c4d594ccf81..eed30b6a5eb 100644 --- a/libs/common/src/auth/send-access/services/send-token.service.ts +++ b/libs/common/src/auth/send-access/services/send-token.service.ts @@ -22,7 +22,6 @@ import { SendTokenApiRetrievalError, SendTokenApiService } from "./send-token-ap // TODO: add JSDocs // TODO: add tests for this service. - export const SEND_ACCESS_TOKEN_DICT = KeyDefinition.record( SEND_ACCESS_DISK, "accessTokenDict", @@ -33,9 +32,29 @@ export const SEND_ACCESS_TOKEN_DICT = KeyDefinition.record; +type CredentialsRequiredApiError = Extract< + SendTokenApiRetrievalError, + "password-required" | "otp-required" | "unknown-error" +>; + +function isCredentialsRequiredApiError( + error: SendTokenApiRetrievalError, +): error is CredentialsRequiredApiError { + return error === "password-required" || error === "otp-required" || error === "unknown-error"; +} + +export type TryGetSendAccessTokenError = "expired" | CredentialsRequiredApiError; + +export type GetSendAcccessTokenError = Extract< + SendTokenApiRetrievalError, + "invalid-password" | "invalid-otp" | "unknown-error" +>; + +function isGetSendAccessTokenError( + error: SendTokenApiRetrievalError, +): error is GetSendAcccessTokenError { + return error === "invalid-password" || error === "invalid-otp" || error === "unknown-error"; +} export class SendTokenService implements SendTokenServiceAbstraction { private sendAccessTokenDictGlobalState: GlobalState> | undefined; @@ -82,13 +101,20 @@ export class SendTokenService implements SendTokenServiceAbstraction { return result; } - return result; + if (isCredentialsRequiredApiError(result)) { + // If we get an expected API error, we return it. + // Typically, this will be a "password-required" or "otp-required" error to communicate that the send requires credentials to access. + return result; + } + + // If we get an unexpected error, we throw. + throw new Error(`Unexpected and unhandled API error retrieving send access token: ${result}`); } - async getSendAccessTokenWithCredentials( + async getSendAccessToken( sendId: string, sendCredentials: SendAccessCredentials, - ): Promise { + ): Promise { // Validate the sendId this.validateSendId(sendId); @@ -104,11 +130,17 @@ export class SendTokenService implements SendTokenServiceAbstraction { if (result instanceof SendAccessToken) { // If we get a token back, we need to store it in the global state. await this.setSendAccessTokenInStorage(sendId, result); - return; + return result; } - // Handle errors from the API service. - // return result; + if (isGetSendAccessTokenError(result)) { + // If we get an expected API error, we return it. + // Typically, this will be due to an invalid credentials error + return result; + } + + // If we get an unexpected error, we throw. + throw new Error(`Unexpected and unhandled API error retrieving send access token: ${result}`); } async hashPassword(password: string, keyMaterialUrlB64: string): Promise {