diff --git a/libs/common/src/auth/send-access/abstractions/send-token-api.service.ts b/libs/common/src/auth/send-access/abstractions/send-token-api.service.ts index 77cfdc5e167..c0ca0e6d7c4 100644 --- a/libs/common/src/auth/send-access/abstractions/send-token-api.service.ts +++ b/libs/common/src/auth/send-access/abstractions/send-token-api.service.ts @@ -1,4 +1,6 @@ import { SendAccessTokenRequest } from "../../models/request/identity-token/send-access-token.request"; +import { SendAccessToken } from "../models/send-access-token"; +import { SendTokenApiRetrievalError } from "../services/send-token-api.service"; /** * Abstract class for the SendTokenApiService. @@ -13,5 +15,7 @@ export abstract class SendTokenApiService { // ExpiredRequiredPassword // these will live at higher level in SendTokenService // ExpiredRequiredEmailOtp - abstract requestSendAccessToken: (request: SendAccessTokenRequest) => Promise; + abstract requestSendAccessToken: ( + request: SendAccessTokenRequest, + ) => Promise; } 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 f728e235f1e..de8ea161523 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,3 +1,6 @@ +import { SendAccessToken } from "../models/send-access-token"; +import { SendTokenRetrievalError } from "../services/send-token.service"; + export type SendAccessCredentialsType = "password" | "email-otp"; export type SendPasswordCredentials = { @@ -26,7 +29,9 @@ export abstract class SendTokenService { // TODO: define return types. // TODO: consider converting to observable. - abstract tryGetSendAccessToken: (sendId: string) => Promise; + abstract tryGetSendAccessToken: ( + sendId: string, + ) => Promise; abstract getSendAccessTokenWithCredentials: ( sendId: string, 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 63dbf9aba41..57276ef90a2 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 @@ -4,6 +4,9 @@ import { ApiService } from "../../../abstractions/api.service"; import { EnvironmentService } from "../../../platform/abstractions/environment.service"; import { SendAccessTokenRequest } from "../../models/request/identity-token/send-access-token.request"; 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"; export class SendTokenApiService implements SendTokenApiServiceAbstraction { constructor( @@ -11,7 +14,9 @@ export class SendTokenApiService implements SendTokenApiServiceAbstraction { private apiService: ApiService, ) {} - async requestSendAccessToken(request: SendAccessTokenRequest): Promise { + async requestSendAccessToken( + request: SendAccessTokenRequest, + ): Promise { const payload = request.toIdentityTokenPayload(); const headers = new Headers({ @@ -31,8 +36,17 @@ export class SendTokenApiService implements SendTokenApiServiceAbstraction { cache: "no-store", }); - await this.apiService.fetch(req); + const response = await this.apiService.fetch(req); + const responseJson = await response.json(); - // TODO: add result processing + if (response.status === 200) { + const sendAccessToken = SendAccessToken.fromJson(responseJson); + return sendAccessToken; + } else if (response.status === 400) { + // TODO: add correct error handling for 400 + return "password-required"; + } + + return "unknown-error"; } } 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 ec90c583e7a..66e4d71b2c3 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 @@ -1,14 +1,20 @@ +import { firstValueFrom } from "rxjs"; import { Jsonify } from "type-fest"; -import { GlobalStateProvider, KeyDefinition, SEND_ACCESS_DISK } from "../../../platform/state"; -// import { SendAccessTokenRequest } from "../../models/request/identity-token/send-access-token.request"; +import { + GlobalState, + GlobalStateProvider, + KeyDefinition, + SEND_ACCESS_DISK, +} from "../../../platform/state"; +import { SendAccessTokenRequest } from "../../models/request/identity-token/send-access-token.request"; import { SendAccessCredentials, SendTokenService as SendTokenServiceAbstraction, } from "../abstractions/send-token.service"; import { SendAccessToken } from "../models/send-access-token"; -import { SendTokenApiService } from "./send-token-api.service"; +import { SendTokenApiRetrievalError, SendTokenApiService } from "./send-token-api.service"; export const SEND_ACCESS_TOKEN_DICT = KeyDefinition.record( SEND_ACCESS_DISK, @@ -20,13 +26,53 @@ export const SEND_ACCESS_TOKEN_DICT = KeyDefinition.record> | undefined; + constructor( private globalStateProvider: GlobalStateProvider, private sendTokenApiService: SendTokenApiService, - ) {} + ) { + this.initializeState(); + } - async getSendAccessToken( + private initializeState(): void { + this.sendAccessTokenDictGlobalState = this.globalStateProvider.get(SEND_ACCESS_TOKEN_DICT); + } + + async tryGetSendAccessToken(sendId: string): Promise { + // TODO: check in storage for the access token and if it is expired. + + const sendAccessTokenFromStorage = await this.getSendAccessTokenFromStorage(sendId); + + if (sendAccessTokenFromStorage != null) { + // If it is expired, we return expired token error. + if (sendAccessTokenFromStorage.isExpired()) { + return "expired"; + } else { + // If it is not expired, we return + return sendAccessTokenFromStorage; + } + } + + // If we don't have a token in storage, we can try to request a new token from the server. + const request = new SendAccessTokenRequest(sendId); + + // try { + const result = await this.sendTokenApiService.requestSendAccessToken(request); + + 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 result; + } + + return result; + } + + async getSendAccessTokenWithCredentials( sendId: string, sendCredentials: SendAccessCredentials | undefined, ): Promise { @@ -36,4 +82,28 @@ export class SendTokenService implements SendTokenServiceAbstraction { // const request = new SendAccessTokenRequest(sendId, sendCredentials); // const result = await this.sendTokenApiService.requestSendAccessToken(request); } + + private async getSendAccessTokenFromStorage( + sendId: string, + ): Promise { + if (this.sendAccessTokenDictGlobalState != null) { + const sendAccessTokenDict = await firstValueFrom(this.sendAccessTokenDictGlobalState.state$); + return sendAccessTokenDict?.[sendId]; + } + return undefined; + } + + private async setSendAccessTokenInStorage( + sendId: string, + sendAccessToken: SendAccessToken, + ): Promise { + if (this.sendAccessTokenDictGlobalState != null) { + await this.sendAccessTokenDictGlobalState.update((sendAccessTokenDict) => { + sendAccessTokenDict ??= {}; // Initialize if undefined + + sendAccessTokenDict[sendId] = sendAccessToken; + return sendAccessTokenDict; + }); + } + } }