1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-06 09:20:43 +01:00

[AuthService refactor] Don't clear state if 2FA is invalid (#690)

* Don't clear state if 2FA is invalid

* Add session timeout to 2FA

* Clear internal authService state if unhandled error
This commit is contained in:
Thomas Rittson 2022-02-24 13:08:43 +10:00 committed by GitHub
parent fdaa4f9fa5
commit 1cbc119ad8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 4 deletions

View File

@ -119,6 +119,7 @@ import { ValidationService } from "./validation.service";
EnvironmentServiceAbstraction, EnvironmentServiceAbstraction,
StateServiceAbstraction, StateServiceAbstraction,
TwoFactorServiceAbstraction, TwoFactorServiceAbstraction,
I18nServiceAbstraction,
], ],
}, },
{ {

View File

@ -3,6 +3,7 @@ import { AppIdService } from "../abstractions/appId.service";
import { AuthService as AuthServiceAbstraction } from "../abstractions/auth.service"; import { AuthService as AuthServiceAbstraction } from "../abstractions/auth.service";
import { CryptoService } from "../abstractions/crypto.service"; import { CryptoService } from "../abstractions/crypto.service";
import { EnvironmentService } from "../abstractions/environment.service"; import { EnvironmentService } from "../abstractions/environment.service";
import { I18nService } from "../abstractions/i18n.service";
import { KeyConnectorService } from "../abstractions/keyConnector.service"; import { KeyConnectorService } from "../abstractions/keyConnector.service";
import { LogService } from "../abstractions/log.service"; import { LogService } from "../abstractions/log.service";
import { MessagingService } from "../abstractions/messaging.service"; import { MessagingService } from "../abstractions/messaging.service";
@ -24,6 +25,9 @@ import {
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey"; import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequest"; import { TokenRequestTwoFactor } from "../models/request/identityToken/tokenRequest";
import { PreloginRequest } from "../models/request/preloginRequest"; import { PreloginRequest } from "../models/request/preloginRequest";
import { ErrorResponse } from "../models/response/errorResponse";
const sessionTimeoutLength = 2 * 60 * 1000; // 2 minutes
export class AuthService implements AuthServiceAbstraction { export class AuthService implements AuthServiceAbstraction {
get email(): string { get email(): string {
@ -37,6 +41,7 @@ export class AuthService implements AuthServiceAbstraction {
} }
private logInStrategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy; private logInStrategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy;
private sessionTimeout: any;
constructor( constructor(
protected cryptoService: CryptoService, protected cryptoService: CryptoService,
@ -49,7 +54,8 @@ export class AuthService implements AuthServiceAbstraction {
protected keyConnectorService: KeyConnectorService, protected keyConnectorService: KeyConnectorService,
protected environmentService: EnvironmentService, protected environmentService: EnvironmentService,
protected stateService: StateService, protected stateService: StateService,
protected twoFactorService: TwoFactorService protected twoFactorService: TwoFactorService,
protected i18nService: I18nService
) {} ) {}
async logIn( async logIn(
@ -110,10 +116,24 @@ export class AuthService implements AuthServiceAbstraction {
} }
async logInTwoFactor(twoFactor: TokenRequestTwoFactor): Promise<AuthResult> { async logInTwoFactor(twoFactor: TokenRequestTwoFactor): Promise<AuthResult> {
if (this.logInStrategy == null) {
throw new Error(this.i18nService.t("sessionTimeout"));
}
try { try {
return await this.logInStrategy.logInTwoFactor(twoFactor); const result = await this.logInStrategy.logInTwoFactor(twoFactor);
} finally {
this.clearState(); // Only clear state if 2FA token has been accepted, otherwise we need to be able to try again
if (!result.requiresTwoFactor) {
this.clearState();
}
return result;
} catch (e) {
// API exceptions are okay, but if there are any unhandled client-side errors then clear state to be safe
if (!(e instanceof ErrorResponse)) {
this.clearState();
}
throw e;
} }
} }
@ -154,9 +174,22 @@ export class AuthService implements AuthServiceAbstraction {
private saveState(strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy) { private saveState(strategy: ApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy) {
this.logInStrategy = strategy; this.logInStrategy = strategy;
this.startSessionTimeout();
} }
private clearState() { private clearState() {
this.logInStrategy = null; this.logInStrategy = null;
this.clearSessionTimeout();
}
private startSessionTimeout() {
this.clearSessionTimeout();
this.sessionTimeout = setTimeout(() => this.clearState(), sessionTimeoutLength);
}
private clearSessionTimeout() {
if (this.sessionTimeout != null) {
clearTimeout(this.sessionTimeout);
}
} }
} }