1
0
mirror of https://github.com/bitwarden/jslib synced 2025-12-15 15:53:51 +00:00

Refactor TokenRequest to take TwoFactorData

This commit is contained in:
Thomas Rittson
2021-12-17 18:59:49 +10:00
parent 4a83258065
commit 8b7f20479e
6 changed files with 71 additions and 102 deletions

View File

@@ -1,26 +1,16 @@
import { TokenRequest } from "./tokenRequest";
import { TwoFactorProviderType } from "../../../enums/twoFactorProviderType";
import { TokenRequest, TwoFactorData } from "./tokenRequest";
import { DeviceRequest } from "../deviceRequest";
export class ApiTokenRequest extends TokenRequest {
clientId: string;
clientSecret: string;
constructor(
clientId: string,
clientSecret: string,
public provider: TwoFactorProviderType,
public token: string,
public remember: boolean,
public captchaResponse: string,
private clientId: string,
private clientSecret: string,
protected twoFactor: TwoFactorData,
captchaResponse: string,
device?: DeviceRequest
) {
super(provider, token, remember, captchaResponse, device);
this.clientId = clientId;
this.clientSecret = clientSecret;
super(twoFactor, captchaResponse, device);
}
toIdentityToken(clientId: string) {

View File

@@ -1,25 +1,18 @@
import { TokenRequest } from "./tokenRequest";
import { TwoFactorProviderType } from "../../../enums/twoFactorProviderType";
import { TokenRequest, TwoFactorData } from "./tokenRequest";
import { DeviceRequest } from "../deviceRequest";
import { Utils } from "../../../misc/utils";
export class PasswordTokenRequest extends TokenRequest {
email: string;
masterPasswordHash: string;
constructor(
email: string,
masterPasswordHash: string,
public provider: TwoFactorProviderType,
public token: string,
public remember: boolean,
public captchaResponse: string,
private email: string,
private masterPasswordHash: string,
protected twoFactor: TwoFactorData,
captchaResponse: string,
device?: DeviceRequest
) {
super(provider, token, remember, captchaResponse, device);
super(twoFactor, captchaResponse, device);
this.email = email;
this.masterPasswordHash = masterPasswordHash;

View File

@@ -1,25 +1,17 @@
import { TokenRequest } from "./tokenRequest";
import { TwoFactorProviderType } from "../../../enums/twoFactorProviderType";
import { TokenRequest, TwoFactorData } from "./tokenRequest";
import { DeviceRequest } from "../deviceRequest";
export class SsoTokenRequest extends TokenRequest {
code: string;
codeVerifier: string;
redirectUri: string;
constructor(
code: string,
codeVerifier: string,
redirectUri: string,
public provider: TwoFactorProviderType,
public token: string,
public remember: boolean,
public captchaResponse: string,
private code: string,
private codeVerifier: string,
private redirectUri: string,
protected twoFactor: TwoFactorData,
captchaResponse: string,
device?: DeviceRequest
) {
super(provider, token, remember, captchaResponse, device);
super(twoFactor, captchaResponse, device);
this.code = code;
this.codeVerifier = codeVerifier;

View File

@@ -3,13 +3,17 @@ import { TwoFactorProviderType } from "../../../enums/twoFactorProviderType";
import { CaptchaProtectedRequest } from "../captchaProtectedRequest";
import { DeviceRequest } from "../deviceRequest";
export interface TwoFactorData {
provider: TwoFactorProviderType;
token: string;
remember: boolean;
}
export abstract class TokenRequest implements CaptchaProtectedRequest {
device?: DeviceRequest;
protected device?: DeviceRequest;
constructor(
public provider: TwoFactorProviderType,
public token: string,
public remember: boolean,
protected twoFactor: TwoFactorData,
public captchaResponse: string,
device?: DeviceRequest
) {
@@ -30,10 +34,10 @@ export abstract class TokenRequest implements CaptchaProtectedRequest {
// obj.devicePushToken = this.device.pushToken;
}
if (this.token && this.provider != null) {
obj.twoFactorToken = this.token;
obj.twoFactorProvider = this.provider;
obj.twoFactorRemember = this.remember ? "1" : "0";
if (this.twoFactor.token && this.twoFactor.provider != null) {
obj.twoFactorToken = this.twoFactor.token;
obj.twoFactorProvider = this.twoFactor.provider;
obj.twoFactorRemember = this.twoFactor.remember ? "1" : "0";
}
if (this.captchaResponse != null) {

View File

@@ -15,6 +15,7 @@ import { PreloginRequest } from "../models/request/preloginRequest";
import { ApiTokenRequest } from "../models/request/identityToken/apiTokenRequest";
import { PasswordTokenRequest } from "../models/request/identityToken/passwordTokenRequest";
import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest";
import { TwoFactorData } from "../models/request/identityToken/tokenRequest";
import { IdentityTokenResponse } from "../models/response/identityTokenResponse";
import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse";
@@ -386,6 +387,11 @@ export class AuthService implements AuthServiceAbstraction {
return result;
}
private async createDeviceRequest() {
const appId = await this.appIdService.getAppId();
return new DeviceRequest(appId, this.platformUtilsService);
}
private async createTokenRequest(
email: string,
hashedPassword: string,
@@ -399,30 +405,29 @@ export class AuthService implements AuthServiceAbstraction {
remember: boolean,
captchaToken: string
) {
const appId = await this.appIdService.getAppId();
const deviceRequest = await this.createDeviceRequest();
const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(email);
const deviceRequest = new DeviceRequest(appId, this.platformUtilsService);
let effectiveToken = null;
let effectiveProvider = null;
let effectiveRemember = false;
const twoFactor: TwoFactorData = {
token: null,
provider: null,
remember: false,
};
if (twoFactorToken != null && twoFactorProvider != null) {
effectiveToken = twoFactorToken;
effectiveProvider = twoFactorProvider;
effectiveRemember = remember;
twoFactor.token = twoFactorToken;
twoFactor.provider = twoFactorProvider;
twoFactor.remember = remember;
} else if (storedTwoFactorToken != null) {
effectiveToken = storedTwoFactorToken;
effectiveProvider = TwoFactorProviderType.Remember;
twoFactor.token = storedTwoFactorToken;
twoFactor.provider = TwoFactorProviderType.Remember;
}
if (email != null && hashedPassword != null) {
return new PasswordTokenRequest(
email,
hashedPassword,
effectiveProvider,
effectiveToken,
effectiveRemember,
twoFactor,
captchaToken,
deviceRequest
);
@@ -431,22 +436,12 @@ export class AuthService implements AuthServiceAbstraction {
code,
codeVerifier,
redirectUrl,
effectiveProvider,
effectiveToken,
effectiveRemember,
twoFactor,
captchaToken,
deviceRequest
);
} else if (clientId != null && clientSecret != null) {
return new ApiTokenRequest(
clientId,
clientSecret,
effectiveProvider,
effectiveToken,
effectiveRemember,
captchaToken,
deviceRequest
);
return new ApiTokenRequest(clientId, clientSecret, twoFactor, captchaToken, deviceRequest);
} else {
throw new Error("No credentials provided.");
}

View File

@@ -5,14 +5,12 @@ import { AppIdService } from "jslib-common/abstractions/appId.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
import { EnvironmentService } from "jslib-common/abstractions/environment.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { TokenService } from "jslib-common/abstractions/token.service";
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
import { AuthService } from "jslib-common/services/auth.service";
@@ -28,9 +26,6 @@ import { IdentityTokenResponse } from "jslib-common/models/response/identityToke
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
import { HashPurpose } from "jslib-common/enums/hashPurpose";
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
import { SsoTokenRequest } from "jslib-common/models/request/identityToken/ssoTokenRequest";
import { ApiTokenRequest } from "jslib-common/models/request/identityToken/apiTokenRequest";
import { PasswordTokenRequest } from "jslib-common/models/request/identityToken/passwordTokenRequest";
describe("Cipher Service", () => {
let cryptoService: SubstituteOf<CryptoService>;
@@ -201,14 +196,14 @@ describe("Cipher Service", () => {
// Api call:
apiService.received(1).postIdentityToken(
Arg.is((actual) => {
const passwordTokenRequest = actual as PasswordTokenRequest;
const passwordTokenRequest = actual as any; // Need to access private fields
return (
passwordTokenRequest.email === email &&
passwordTokenRequest.masterPasswordHash === hashedPassword &&
actual.device.identifier === deviceId &&
actual.provider == null &&
actual.token == null &&
actual.captchaResponse == null
passwordTokenRequest.device.identifier === deviceId &&
passwordTokenRequest.twoFactor.provider == null &&
passwordTokenRequest.twoFactor.token == null &&
passwordTokenRequest.captchaResponse == null
);
})
);
@@ -341,15 +336,15 @@ describe("Cipher Service", () => {
apiService.received(1).postIdentityToken(
Arg.is((actual) => {
const passwordTokenRequest = actual as PasswordTokenRequest;
const passwordTokenRequest = actual as any;
return (
passwordTokenRequest.email === email &&
passwordTokenRequest.masterPasswordHash === hashedPassword &&
actual.device.identifier === deviceId &&
actual.provider === twoFactorProviderType &&
actual.token === twoFactorToken &&
actual.remember === twoFactorRemember &&
actual.captchaResponse == null
passwordTokenRequest.device.identifier === deviceId &&
passwordTokenRequest.twoFactor.provider == twoFactorProviderType &&
passwordTokenRequest.twoFactor.token == twoFactorToken &&
passwordTokenRequest.twoFactor.remember == twoFactorRemember &&
passwordTokenRequest.captchaResponse == null
);
})
);
@@ -370,14 +365,14 @@ describe("Cipher Service", () => {
// Api call:
apiService.received(1).postIdentityToken(
Arg.is((actual) => {
const ssoTokenRequest = actual as SsoTokenRequest;
const ssoTokenRequest = actual as any;
return (
ssoTokenRequest.code === ssoCode &&
ssoTokenRequest.codeVerifier === ssoCodeVerifier &&
ssoTokenRequest.redirectUri === ssoRedirectUrl &&
actual.device.identifier === deviceId &&
actual.provider == null &&
actual.token == null &&
ssoTokenRequest.device.identifier === deviceId &&
ssoTokenRequest.twoFactor.provider == null &&
ssoTokenRequest.twoFactor.token == null &&
actual.captchaResponse == null
);
})
@@ -491,14 +486,14 @@ describe("Cipher Service", () => {
apiService.received(1).postIdentityToken(
Arg.is((actual) => {
const apiTokenRequest = actual as ApiTokenRequest;
const apiTokenRequest = actual as any;
return (
apiTokenRequest.clientId === apiClientId &&
apiTokenRequest.clientSecret === apiClientSecret &&
actual.device.identifier === deviceId &&
actual.provider == null &&
actual.token == null &&
actual.captchaResponse == null
apiTokenRequest.device.identifier === deviceId &&
apiTokenRequest.twoFactor.provider == null &&
apiTokenRequest.twoFactor.token == null &&
apiTokenRequest.captchaResponse == null
);
})
);