1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-07 20:24:01 +00:00

PM-20532 - WIP on send token stuff

This commit is contained in:
Jared Snider
2025-05-28 18:30:20 -04:00
parent 9c23e0e529
commit 60842c0490
3 changed files with 53 additions and 13 deletions

View File

@@ -1,6 +1,6 @@
import { SendHashedPassword } from "../../../key-management/sends/send-password.service";
import { SendAccessToken } from "../models/send-access-token";
import { SendTokenRetrievalError } from "../services/send-token.service";
import { TryGetSendAccessTokenError } from "../services/send-token.service";
export type SendAccessCredentialsType = "password" | "email-otp";
@@ -31,9 +31,20 @@ export abstract class SendTokenService {
// TODO: define return types.
// TODO: consider converting to observable.
/**
* Attempts to retrieve a SendAccessToken for the given sendId.
* If the access token is found in session storage and is not expired, then it returns the token.
* If the access token is expired, then it returns a SendTokenRetrievalError expired error.
* If an access token is not found in storage, then it attempts to retrieve it from the server (will succeed for sends that don't require any credentials to view).
* If the access token is successfully retrieved from the server, then it stores the token in session storage and returns it.
* If an access token cannot be granted b/c the send requires credentials, then it returns a SendTokenRetrievalError indicating which credentials are required.
* Any submissions of credentials will be handled by the getSendAccessTokenWithCredentials method.
* @param sendId The ID of the send to retrieve the access token for.
* @returns A promise that resolves to a SendAccessToken if found and valid, or a SendTokenRetrievalError if not.
*/
abstract tryGetSendAccessToken: (
sendId: string,
) => Promise<SendAccessToken | SendTokenRetrievalError>;
) => Promise<SendAccessToken | TryGetSendAccessTokenError>;
abstract getSendAccessTokenWithCredentials: (
sendId: string,

View File

@@ -8,6 +8,9 @@ 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 class SendTokenApiService implements SendTokenApiServiceAbstraction {
constructor(
private environmentService: EnvironmentService,

View File

@@ -33,7 +33,9 @@ export const SEND_ACCESS_TOKEN_DICT = KeyDefinition.record<SendAccessToken, stri
},
);
export type SendTokenRetrievalError = "expired" | SendTokenApiRetrievalError;
// TODO: add different error types for each method.
export type TryGetSendAccessTokenError = "expired" | SendTokenApiRetrievalError;
// export type GetSendAccessTokenWithCredsError = <subset of SendTokenApiRetrievalError>;
export class SendTokenService implements SendTokenServiceAbstraction {
private sendAccessTokenDictGlobalState: GlobalState<Record<string, SendAccessToken>> | undefined;
@@ -50,9 +52,13 @@ export class SendTokenService implements SendTokenServiceAbstraction {
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.
async tryGetSendAccessToken(
sendId: string,
): Promise<SendAccessToken | TryGetSendAccessTokenError> {
// Validate the sendId is a non-empty string.
this.validateSendId(sendId);
// Check in storage for the access token for the given sendId.
const sendAccessTokenFromStorage = await this.getSendAccessTokenFromStorage(sendId);
if (sendAccessTokenFromStorage != null) {
@@ -60,7 +66,7 @@ export class SendTokenService implements SendTokenServiceAbstraction {
if (sendAccessTokenFromStorage.isExpired()) {
return "expired";
} else {
// If it is not expired, we return
// If it is not expired, we return it
return sendAccessTokenFromStorage;
}
}
@@ -68,7 +74,6 @@ export class SendTokenService implements SendTokenServiceAbstraction {
// 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) {
@@ -82,13 +87,28 @@ export class SendTokenService implements SendTokenServiceAbstraction {
async getSendAccessTokenWithCredentials(
sendId: string,
sendCredentials: SendAccessCredentials | undefined,
sendCredentials: SendAccessCredentials,
): Promise<void> {
// TODO: check in storage for the access token and if it is expired.
// If it is expired, we will need to request a new token from the server.
// If it is not expired, we will return the token from storage.
// const request = new SendAccessTokenRequest(sendId, sendCredentials);
// const result = await this.sendTokenApiService.requestSendAccessToken(request);
// Validate the sendId
this.validateSendId(sendId);
// Validate the credentials
if (sendCredentials == null) {
throw new Error("sendCredentials must be provided.");
}
// Request the access token from the server using the provided credentials.
const request = new SendAccessTokenRequest(sendId, sendCredentials);
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;
}
// Handle errors from the API service.
// return result;
}
async hashPassword(password: string, keyMaterialUrlB64: string): Promise<SendHashedPassword> {
@@ -118,4 +138,10 @@ export class SendTokenService implements SendTokenServiceAbstraction {
});
}
}
private validateSendId(sendId: string): void {
if (sendId == null || sendId.trim() === "") {
throw new Error("sendId must be provided.");
}
}
}