mirror of
https://github.com/bitwarden/browser
synced 2026-02-08 20:50:28 +00:00
PM-20532 - WIP on send access token layers.
This commit is contained in:
@@ -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<unknown>;
|
||||
abstract requestSendAccessToken: (
|
||||
request: SendAccessTokenRequest,
|
||||
) => Promise<SendAccessToken | SendTokenApiRetrievalError>;
|
||||
}
|
||||
|
||||
@@ -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<void>;
|
||||
abstract tryGetSendAccessToken: (
|
||||
sendId: string,
|
||||
) => Promise<SendAccessToken | SendTokenRetrievalError>;
|
||||
|
||||
abstract getSendAccessTokenWithCredentials: (
|
||||
sendId: string,
|
||||
|
||||
@@ -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<void> {
|
||||
async requestSendAccessToken(
|
||||
request: SendAccessTokenRequest,
|
||||
): Promise<SendAccessToken | SendTokenApiRetrievalError> {
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<SendAccessToken, string>(
|
||||
SEND_ACCESS_DISK,
|
||||
@@ -20,13 +26,53 @@ export const SEND_ACCESS_TOKEN_DICT = KeyDefinition.record<SendAccessToken, stri
|
||||
},
|
||||
);
|
||||
|
||||
export type SendTokenRetrievalError = "expired" | SendTokenApiRetrievalError;
|
||||
|
||||
export class SendTokenService implements SendTokenServiceAbstraction {
|
||||
private sendAccessTokenDictGlobalState: GlobalState<Record<string, SendAccessToken>> | 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<SendAccessToken | SendTokenRetrievalError> {
|
||||
// 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<void> {
|
||||
@@ -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<SendAccessToken | undefined> {
|
||||
if (this.sendAccessTokenDictGlobalState != null) {
|
||||
const sendAccessTokenDict = await firstValueFrom(this.sendAccessTokenDictGlobalState.state$);
|
||||
return sendAccessTokenDict?.[sendId];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async setSendAccessTokenInStorage(
|
||||
sendId: string,
|
||||
sendAccessToken: SendAccessToken,
|
||||
): Promise<void> {
|
||||
if (this.sendAccessTokenDictGlobalState != null) {
|
||||
await this.sendAccessTokenDictGlobalState.update((sendAccessTokenDict) => {
|
||||
sendAccessTokenDict ??= {}; // Initialize if undefined
|
||||
|
||||
sendAccessTokenDict[sendId] = sendAccessToken;
|
||||
return sendAccessTokenDict;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user