1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 08:43:33 +00:00

[PM-11941] Migrate TOTP Generator to use SDK (#12987)

* Refactored totp service to use sdk

Fixed strict typescript issues

* Fixed dependency issues

* Returned object that contains code and period, removed get interval function

* removed dependencies

* Updated to use refactored totp service

* removed sdk service undefined check

* removed undefined as an input from the getCode function

* Made getcode$ an observable

* refactored to use getcodee$

* Filter out emmissions

* updated sdk version

* Fixed readability nit

* log error on overlay if totp response does not return a code

* fix(totpGeneration): [PM-11941] Totp countdown not working on clients

* Used optional chaining if totpresponse returns null or undefined
This commit is contained in:
SmithThe4th
2025-03-06 14:01:07 -05:00
committed by GitHub
parent 1415041fd7
commit e327816bc4
24 changed files with 345 additions and 443 deletions

View File

@@ -2,9 +2,11 @@
// @ts-strict-ignore
import { CommonModule } from "@angular/common";
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { Observable, map, tap } from "rxjs";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { TotpInfo } from "@bitwarden/common/vault/services/totp.service";
import { TypographyModule } from "@bitwarden/components";
@Component({
@@ -17,69 +19,45 @@ export class BitTotpCountdownComponent implements OnInit {
@Input() cipher: CipherView;
@Output() sendCopyCode = new EventEmitter();
totpCode: string;
totpCodeFormatted: string;
totpDash: number;
totpSec: number;
totpLow: boolean;
private totpInterval: any;
/**
* Represents TOTP information including display formatting and timing
*/
totpInfo$: Observable<TotpInfo> | undefined;
constructor(protected totpService: TotpService) {}
async ngOnInit() {
await this.totpUpdateCode();
const interval = this.totpService.getTimeInterval(this.cipher.login.totp);
await this.totpTick(interval);
this.totpInfo$ = this.cipher?.login?.totp
? this.totpService.getCode$(this.cipher.login.totp).pipe(
map((response) => {
const epoch = Math.round(new Date().getTime() / 1000.0);
const mod = epoch % response.period;
this.totpInterval = setInterval(async () => {
await this.totpTick(interval);
}, 1000);
return {
totpCode: response.code,
totpCodeFormatted: this.formatTotpCode(response.code),
totpSec: response.period - mod,
totpDash: +(Math.round(((60 / response.period) * mod + "e+2") as any) + "e-2"),
totpLow: response.period - mod <= 7,
} as TotpInfo;
}),
tap((totpInfo) => {
if (totpInfo.totpCode && totpInfo.totpCode.length > 4) {
this.sendCopyCode.emit({
totpCode: totpInfo.totpCode,
totpCodeFormatted: totpInfo.totpCodeFormatted,
});
}
}),
)
: undefined;
}
private async totpUpdateCode() {
if (this.cipher.login.totp == null) {
this.clearTotp();
return;
}
this.totpCode = await this.totpService.getCode(this.cipher.login.totp);
if (this.totpCode != null) {
if (this.totpCode.length > 4) {
this.totpCodeFormatted = this.formatTotpCode();
this.sendCopyCode.emit({
totpCode: this.totpCode,
totpCodeFormatted: this.totpCodeFormatted,
});
} else {
this.totpCodeFormatted = this.totpCode;
}
} else {
this.totpCodeFormatted = null;
this.sendCopyCode.emit({ totpCode: null, totpCodeFormatted: null });
this.clearTotp();
}
}
private async totpTick(intervalSeconds: number) {
const epoch = Math.round(new Date().getTime() / 1000.0);
const mod = epoch % intervalSeconds;
this.totpSec = intervalSeconds - mod;
this.totpDash = +(Math.round(((60 / intervalSeconds) * mod + "e+2") as any) + "e-2");
this.totpLow = this.totpSec <= 7;
if (mod === 0) {
await this.totpUpdateCode();
}
}
private formatTotpCode(): string {
const half = Math.floor(this.totpCode.length / 2);
return this.totpCode.substring(0, half) + " " + this.totpCode.substring(half);
}
private clearTotp() {
if (this.totpInterval) {
clearInterval(this.totpInterval);
private formatTotpCode(code: string): string {
if (code.length > 4) {
const half = Math.floor(code.length / 2);
return code.substring(0, half) + " " + code.substring(half);
}
return code;
}
}