diff --git a/apps/web/src/app/auth/settings/change-password.component.ts b/apps/web/src/app/auth/settings/change-password.component.ts index 1f962af670d..96f57e96bd8 100644 --- a/apps/web/src/app/auth/settings/change-password.component.ts +++ b/apps/web/src/app/auth/settings/change-password.component.ts @@ -230,6 +230,10 @@ export class ChangePasswordComponent algorithm: "argon2id", parameters: { memory: 256 * 1024, iterations: 3, parallelism: 4 }, }); + await this.opaqueService.login(this.email, this.masterPassword, { + algorithm: "argon2id", + parameters: { memory: 256 * 1024, iterations: 3, parallelism: 4 }, + }); await this.formPromise; this.toastService.showToast({ diff --git a/libs/common/src/auth/opaque/default-opaque-api.service.ts b/libs/common/src/auth/opaque/default-opaque-api.service.ts index 8d2a0401232..5448fcf5bc1 100644 --- a/libs/common/src/auth/opaque/default-opaque-api.service.ts +++ b/libs/common/src/auth/opaque/default-opaque-api.service.ts @@ -3,6 +3,9 @@ import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { LoginFinishRequest } from "./models/login-finish.request"; +import { LoginStartRequest } from "./models/login-start.request"; +import { LoginStartResponse } from "./models/login-start.response"; import { RegistrationFinishRequest } from "./models/registration-finish.request"; import { RegistrationFinishResponse } from "./models/registration-finish.response"; import { RegistrationStartRequest } from "./models/registration-start.request"; @@ -43,10 +46,29 @@ export class DefaultOpaqueApiService implements OpaqueApiService { return new RegistrationFinishResponse(response); } - loginStart(): any { - throw new Error("Method not implemented"); + async loginStart(request: LoginStartRequest): Promise { + const env = await firstValueFrom(this.environmentService.environment$); + const response = await this.apiService.send( + "POST", + `/opaque/start-login`, + request, + true, + true, + env.getApiUrl(), + ); + return new LoginStartResponse(response); } - loginFinish(): any { - throw new Error("Method not implemented"); + + async loginFinish(request: LoginFinishRequest): Promise { + const env = await firstValueFrom(this.environmentService.environment$); + const response = await this.apiService.send( + "POST", + `/opaque/finish-login`, + request, + true, + true, + env.getApiUrl(), + ); + return response.success; } } diff --git a/libs/common/src/auth/opaque/default-opaque.service.ts b/libs/common/src/auth/opaque/default-opaque.service.ts index d2c4c067dd9..e0be57cebfc 100644 --- a/libs/common/src/auth/opaque/default-opaque.service.ts +++ b/libs/common/src/auth/opaque/default-opaque.service.ts @@ -8,6 +8,8 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { UserKey } from "../../types/key"; import { CipherConfiguration, KsfConfig } from "./models/cipher-configuration"; +import { LoginFinishRequest } from "./models/login-finish.request"; +import { LoginStartRequest } from "./models/login-start.request"; import { RegistrationFinishRequest } from "./models/registration-finish.request"; import { RegistrationStartRequest } from "./models/registration-start.request"; import { OpaqueApiService } from "./opaque-api.service"; @@ -60,7 +62,32 @@ export class DefaultOpaqueService implements OpaqueService { ); } - async login(masterPassword: string, ksfConfig: KsfConfig): Promise { - throw new Error("Method not implemented."); + async login(email: string, masterPassword: string, ksfConfig: KsfConfig): Promise { + const config = new CipherConfiguration(ksfConfig); + const cryptoClient = (await firstValueFrom(this.sdkService.client$)).crypto(); + + const loginStart = cryptoClient.opaque_login_start(masterPassword, config.toSdkConfig()); + const loginStartResponse = await this.opaqueApiService.loginStart( + new LoginStartRequest(email, Utils.fromBufferToB64(loginStart.credential_request)), + ); + + const loginFinish = cryptoClient.opaque_login_finish( + masterPassword, + config.toSdkConfig(), + Utils.fromB64ToArray(loginStartResponse.credentialResponse), + loginStart.state, + ); + + const success = await this.opaqueApiService.loginFinish( + new LoginFinishRequest( + loginStartResponse.sessionId, + Utils.fromBufferToB64(loginFinish.credential_finalization), + ), + ); + if (!success) { + throw new Error("Login failed"); + } + + return loginFinish.export_key; } } diff --git a/libs/common/src/auth/opaque/models/login-start.response.ts b/libs/common/src/auth/opaque/models/login-start.response.ts index 10ef2a80513..ed3868c0572 100644 --- a/libs/common/src/auth/opaque/models/login-start.response.ts +++ b/libs/common/src/auth/opaque/models/login-start.response.ts @@ -1,7 +1,9 @@ +import { OpaqueSessionId } from "@bitwarden/common/types/guid"; + import { BaseResponse } from "../../../models/response/base.response"; export class LoginStartResponse extends BaseResponse { - sessionId: string; + sessionId: OpaqueSessionId; credentialResponse: string; constructor(response: any) { diff --git a/libs/common/src/auth/opaque/opaque-api.service.ts b/libs/common/src/auth/opaque/opaque-api.service.ts index ee153db3353..e694f5ad053 100644 --- a/libs/common/src/auth/opaque/opaque-api.service.ts +++ b/libs/common/src/auth/opaque/opaque-api.service.ts @@ -1,3 +1,6 @@ +import { LoginFinishRequest } from "./models/login-finish.request"; +import { LoginStartRequest } from "./models/login-start.request"; +import { LoginStartResponse } from "./models/login-start.response"; import { RegistrationFinishRequest } from "./models/registration-finish.request"; import { RegistrationFinishResponse } from "./models/registration-finish.response"; import { RegistrationStartRequest } from "./models/registration-start.request"; @@ -8,6 +11,6 @@ export abstract class OpaqueApiService { abstract registrationFinish( request: RegistrationFinishRequest, ): Promise; - abstract loginStart(): any; - abstract loginFinish(): any; + abstract loginStart(request: LoginStartRequest): Promise; + abstract loginFinish(request: LoginFinishRequest): Promise; } diff --git a/libs/common/src/auth/opaque/opaque.service.ts b/libs/common/src/auth/opaque/opaque.service.ts index 13fc88ff3f8..ae964e9650e 100644 --- a/libs/common/src/auth/opaque/opaque.service.ts +++ b/libs/common/src/auth/opaque/opaque.service.ts @@ -13,5 +13,5 @@ export abstract class OpaqueService { * in combination with the rotateable keyset returned from the token endpoint. * @returns The ExportKey obtained during the Opaque login flow. */ - abstract login(masterPassword: string, ksfConfig: KsfConfig): Promise; + abstract login(email: string, masterPassword: string, ksfConfig: KsfConfig): Promise; }