mirror of
https://github.com/bitwarden/browser
synced 2026-02-26 17:43:22 +00:00
Innovation/opaque grant validator (#13918)
* Add grant validator * Fix 2fa * Set active endpoint
This commit is contained in:
@@ -11,6 +11,7 @@ export class OpaqueTokenRequest extends TokenRequest {
|
||||
constructor(
|
||||
public email: string,
|
||||
protected twoFactor: TokenTwoFactorRequest,
|
||||
public sessionId: string,
|
||||
device?: DeviceRequest,
|
||||
public newDeviceOtp?: string,
|
||||
) {
|
||||
@@ -21,8 +22,9 @@ export class OpaqueTokenRequest extends TokenRequest {
|
||||
const obj = super.toIdentityToken(clientId);
|
||||
|
||||
// TODO: what grant type for OPAQUE?
|
||||
obj.grant_type = "password";
|
||||
obj.grant_type = "opaque-ke";
|
||||
obj.username = this.email;
|
||||
obj.sessionId = this.sessionId;
|
||||
|
||||
if (this.newDeviceOtp) {
|
||||
obj.newDeviceOtp = this.newDeviceOtp;
|
||||
|
||||
@@ -13,6 +13,7 @@ import { LoginStartRequest } from "./models/login-start.request";
|
||||
import { OpaqueCipherConfiguration } from "./models/opaque-cipher-configuration";
|
||||
import { RegistrationFinishRequest } from "./models/registration-finish.request";
|
||||
import { RegistrationStartRequest } from "./models/registration-start.request";
|
||||
import { SetRegistrationActiveRequest } from "./models/set-registration-active.request";
|
||||
import { OpaqueKeyExchangeApiService } from "./opaque-key-exchange-api.service";
|
||||
import { OpaqueKeyExchangeService } from "./opaque-key-exchange.service";
|
||||
|
||||
@@ -74,6 +75,12 @@ export class DefaultOpaqueKeyExchangeService implements OpaqueKeyExchangeService
|
||||
return registrationStartResponse.sessionId;
|
||||
}
|
||||
|
||||
async setRegistrationActive(sessionId: OpaqueSessionId): Promise<void> {
|
||||
await this.opaqueKeyExchangeApiService.setRegistrationActive(
|
||||
new SetRegistrationActiveRequest(sessionId),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: we will likely have to break this apart to return the start / finish requests
|
||||
// so that the opaque login strategy can send both to the identity token endpoint
|
||||
// in separate calls.
|
||||
@@ -81,7 +88,7 @@ export class DefaultOpaqueKeyExchangeService implements OpaqueKeyExchangeService
|
||||
email: string,
|
||||
masterPassword: string,
|
||||
cipherConfig: OpaqueCipherConfiguration,
|
||||
): Promise<Uint8Array> {
|
||||
): Promise<{ sessionId: string; exportKey: Uint8Array }> {
|
||||
if (!email || !masterPassword || !cipherConfig) {
|
||||
throw new Error(
|
||||
`Unable to log in user with missing parameters. email exists: ${!!email}; masterPassword exists: ${!!masterPassword}; cipherConfig exists: ${!!cipherConfig}`,
|
||||
@@ -112,6 +119,6 @@ export class DefaultOpaqueKeyExchangeService implements OpaqueKeyExchangeService
|
||||
throw new Error("Login failed");
|
||||
}
|
||||
|
||||
return loginFinish.export_key;
|
||||
return { sessionId: loginStartResponse.sessionId, exportKey: loginFinish.export_key };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,16 @@ export class OpaqueCipherConfiguration {
|
||||
this.argon2Parameters = ksf;
|
||||
}
|
||||
|
||||
static fromAny(config: any): OpaqueCipherConfiguration {
|
||||
if (
|
||||
config.cipherSuite !==
|
||||
"OPAQUE_3_RISTRETTO255_OPRF_RISTRETTO255_KEGROUP_3DH_KEX_ARGON2ID13_KSF"
|
||||
) {
|
||||
throw new Error("Unsupported cipher suite");
|
||||
}
|
||||
return new OpaqueCipherConfiguration(config.argon2Parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from Bitwarden KDF configs to OPAQUE KSF configs.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { OpaqueSessionId } from "@bitwarden/common/types/guid";
|
||||
|
||||
export class SetRegistrationActiveRequest {
|
||||
constructor(readonly sessionId: OpaqueSessionId) {}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import { RegistrationFinishRequest } from "./models/registration-finish.request"
|
||||
import { RegistrationFinishResponse } from "./models/registration-finish.response";
|
||||
import { RegistrationStartRequest } from "./models/registration-start.request";
|
||||
import { RegistrationStartResponse } from "./models/registration-start.response";
|
||||
import { SetRegistrationActiveRequest } from "./models/set-registration-active.request";
|
||||
|
||||
export class OpaqueKeyExchangeApiService {
|
||||
constructor(
|
||||
@@ -45,13 +46,25 @@ export class OpaqueKeyExchangeApiService {
|
||||
return new RegistrationFinishResponse(response);
|
||||
}
|
||||
|
||||
async setRegistrationActive(request: SetRegistrationActiveRequest): Promise<void> {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
await this.apiService.send(
|
||||
"POST",
|
||||
`/opaque/set-registration-active`,
|
||||
request,
|
||||
true,
|
||||
true,
|
||||
env.getApiUrl(),
|
||||
);
|
||||
}
|
||||
|
||||
async loginStart(request: LoginStartRequest): Promise<LoginStartResponse> {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
const response = await this.apiService.send(
|
||||
"POST",
|
||||
`/opaque/start-login`,
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
env.getApiUrl(),
|
||||
);
|
||||
@@ -64,10 +77,10 @@ export class OpaqueKeyExchangeApiService {
|
||||
"POST",
|
||||
`/opaque/finish-login`,
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
env.getApiUrl(),
|
||||
);
|
||||
return response.success;
|
||||
return response == true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@ export abstract class OpaqueKeyExchangeService {
|
||||
cipherConfiguration: OpaqueCipherConfiguration,
|
||||
): Promise<OpaqueSessionId>;
|
||||
|
||||
/**
|
||||
* Set the registration as the active authentication method for the user.
|
||||
*/
|
||||
abstract setRegistrationActive(sessionId: OpaqueSessionId): Promise<void>;
|
||||
|
||||
/**
|
||||
* Authenticate using the Opaque login method. Returns the export key, which must be used
|
||||
* in combination with the rotateable keyset returned from the token endpoint.
|
||||
@@ -23,5 +28,8 @@ export abstract class OpaqueKeyExchangeService {
|
||||
email: string,
|
||||
masterPassword: string,
|
||||
cipherConfiguration: OpaqueCipherConfiguration,
|
||||
): Promise<Uint8Array>;
|
||||
): Promise<{
|
||||
sessionId: string;
|
||||
exportKey: Uint8Array;
|
||||
}>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user