1
0
mirror of https://github.com/bitwarden/jslib synced 2025-12-20 10:13:43 +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 { TokenRequest, TwoFactorData } from "./tokenRequest";
import { TwoFactorProviderType } from "../../../enums/twoFactorProviderType";
import { DeviceRequest } from "../deviceRequest"; import { DeviceRequest } from "../deviceRequest";
export class ApiTokenRequest extends TokenRequest { export class ApiTokenRequest extends TokenRequest {
clientId: string;
clientSecret: string;
constructor( constructor(
clientId: string, private clientId: string,
clientSecret: string, private clientSecret: string,
public provider: TwoFactorProviderType, protected twoFactor: TwoFactorData,
public token: string, captchaResponse: string,
public remember: boolean,
public captchaResponse: string,
device?: DeviceRequest device?: DeviceRequest
) { ) {
super(provider, token, remember, captchaResponse, device); super(twoFactor, captchaResponse, device);
this.clientId = clientId;
this.clientSecret = clientSecret;
} }
toIdentityToken(clientId: string) { toIdentityToken(clientId: string) {

View File

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

View File

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

View File

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

View File

@@ -15,6 +15,7 @@ import { PreloginRequest } from "../models/request/preloginRequest";
import { ApiTokenRequest } from "../models/request/identityToken/apiTokenRequest"; import { ApiTokenRequest } from "../models/request/identityToken/apiTokenRequest";
import { PasswordTokenRequest } from "../models/request/identityToken/passwordTokenRequest"; import { PasswordTokenRequest } from "../models/request/identityToken/passwordTokenRequest";
import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest"; import { SsoTokenRequest } from "../models/request/identityToken/ssoTokenRequest";
import { TwoFactorData } from "../models/request/identityToken/tokenRequest";
import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; import { IdentityTokenResponse } from "../models/response/identityTokenResponse";
import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse"; import { IdentityTwoFactorResponse } from "../models/response/identityTwoFactorResponse";
@@ -386,6 +387,11 @@ export class AuthService implements AuthServiceAbstraction {
return result; return result;
} }
private async createDeviceRequest() {
const appId = await this.appIdService.getAppId();
return new DeviceRequest(appId, this.platformUtilsService);
}
private async createTokenRequest( private async createTokenRequest(
email: string, email: string,
hashedPassword: string, hashedPassword: string,
@@ -399,30 +405,29 @@ export class AuthService implements AuthServiceAbstraction {
remember: boolean, remember: boolean,
captchaToken: string captchaToken: string
) { ) {
const appId = await this.appIdService.getAppId(); const deviceRequest = await this.createDeviceRequest();
const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(email); const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(email);
const deviceRequest = new DeviceRequest(appId, this.platformUtilsService);
let effectiveToken = null; const twoFactor: TwoFactorData = {
let effectiveProvider = null; token: null,
let effectiveRemember = false; provider: null,
remember: false,
};
if (twoFactorToken != null && twoFactorProvider != null) { if (twoFactorToken != null && twoFactorProvider != null) {
effectiveToken = twoFactorToken; twoFactor.token = twoFactorToken;
effectiveProvider = twoFactorProvider; twoFactor.provider = twoFactorProvider;
effectiveRemember = remember; twoFactor.remember = remember;
} else if (storedTwoFactorToken != null) { } else if (storedTwoFactorToken != null) {
effectiveToken = storedTwoFactorToken; twoFactor.token = storedTwoFactorToken;
effectiveProvider = TwoFactorProviderType.Remember; twoFactor.provider = TwoFactorProviderType.Remember;
} }
if (email != null && hashedPassword != null) { if (email != null && hashedPassword != null) {
return new PasswordTokenRequest( return new PasswordTokenRequest(
email, email,
hashedPassword, hashedPassword,
effectiveProvider, twoFactor,
effectiveToken,
effectiveRemember,
captchaToken, captchaToken,
deviceRequest deviceRequest
); );
@@ -431,22 +436,12 @@ export class AuthService implements AuthServiceAbstraction {
code, code,
codeVerifier, codeVerifier,
redirectUrl, redirectUrl,
effectiveProvider, twoFactor,
effectiveToken,
effectiveRemember,
captchaToken, captchaToken,
deviceRequest deviceRequest
); );
} else if (clientId != null && clientSecret != null) { } else if (clientId != null && clientSecret != null) {
return new ApiTokenRequest( return new ApiTokenRequest(clientId, clientSecret, twoFactor, captchaToken, deviceRequest);
clientId,
clientSecret,
effectiveProvider,
effectiveToken,
effectiveRemember,
captchaToken,
deviceRequest
);
} else { } else {
throw new Error("No credentials provided."); 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 { CryptoService } from "jslib-common/abstractions/crypto.service";
import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service"; import { CryptoFunctionService } from "jslib-common/abstractions/cryptoFunction.service";
import { EnvironmentService } from "jslib-common/abstractions/environment.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 { KeyConnectorService } from "jslib-common/abstractions/keyConnector.service";
import { LogService } from "jslib-common/abstractions/log.service"; import { LogService } from "jslib-common/abstractions/log.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service"; import { MessagingService } from "jslib-common/abstractions/messaging.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service"; import { StateService } from "jslib-common/abstractions/state.service";
import { TokenService } from "jslib-common/abstractions/token.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"; 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 { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
import { HashPurpose } from "jslib-common/enums/hashPurpose"; import { HashPurpose } from "jslib-common/enums/hashPurpose";
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType"; 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", () => { describe("Cipher Service", () => {
let cryptoService: SubstituteOf<CryptoService>; let cryptoService: SubstituteOf<CryptoService>;
@@ -201,14 +196,14 @@ describe("Cipher Service", () => {
// Api call: // Api call:
apiService.received(1).postIdentityToken( apiService.received(1).postIdentityToken(
Arg.is((actual) => { Arg.is((actual) => {
const passwordTokenRequest = actual as PasswordTokenRequest; const passwordTokenRequest = actual as any; // Need to access private fields
return ( return (
passwordTokenRequest.email === email && passwordTokenRequest.email === email &&
passwordTokenRequest.masterPasswordHash === hashedPassword && passwordTokenRequest.masterPasswordHash === hashedPassword &&
actual.device.identifier === deviceId && passwordTokenRequest.device.identifier === deviceId &&
actual.provider == null && passwordTokenRequest.twoFactor.provider == null &&
actual.token == null && passwordTokenRequest.twoFactor.token == null &&
actual.captchaResponse == null passwordTokenRequest.captchaResponse == null
); );
}) })
); );
@@ -341,15 +336,15 @@ describe("Cipher Service", () => {
apiService.received(1).postIdentityToken( apiService.received(1).postIdentityToken(
Arg.is((actual) => { Arg.is((actual) => {
const passwordTokenRequest = actual as PasswordTokenRequest; const passwordTokenRequest = actual as any;
return ( return (
passwordTokenRequest.email === email && passwordTokenRequest.email === email &&
passwordTokenRequest.masterPasswordHash === hashedPassword && passwordTokenRequest.masterPasswordHash === hashedPassword &&
actual.device.identifier === deviceId && passwordTokenRequest.device.identifier === deviceId &&
actual.provider === twoFactorProviderType && passwordTokenRequest.twoFactor.provider == twoFactorProviderType &&
actual.token === twoFactorToken && passwordTokenRequest.twoFactor.token == twoFactorToken &&
actual.remember === twoFactorRemember && passwordTokenRequest.twoFactor.remember == twoFactorRemember &&
actual.captchaResponse == null passwordTokenRequest.captchaResponse == null
); );
}) })
); );
@@ -370,14 +365,14 @@ describe("Cipher Service", () => {
// Api call: // Api call:
apiService.received(1).postIdentityToken( apiService.received(1).postIdentityToken(
Arg.is((actual) => { Arg.is((actual) => {
const ssoTokenRequest = actual as SsoTokenRequest; const ssoTokenRequest = actual as any;
return ( return (
ssoTokenRequest.code === ssoCode && ssoTokenRequest.code === ssoCode &&
ssoTokenRequest.codeVerifier === ssoCodeVerifier && ssoTokenRequest.codeVerifier === ssoCodeVerifier &&
ssoTokenRequest.redirectUri === ssoRedirectUrl && ssoTokenRequest.redirectUri === ssoRedirectUrl &&
actual.device.identifier === deviceId && ssoTokenRequest.device.identifier === deviceId &&
actual.provider == null && ssoTokenRequest.twoFactor.provider == null &&
actual.token == null && ssoTokenRequest.twoFactor.token == null &&
actual.captchaResponse == null actual.captchaResponse == null
); );
}) })
@@ -491,14 +486,14 @@ describe("Cipher Service", () => {
apiService.received(1).postIdentityToken( apiService.received(1).postIdentityToken(
Arg.is((actual) => { Arg.is((actual) => {
const apiTokenRequest = actual as ApiTokenRequest; const apiTokenRequest = actual as any;
return ( return (
apiTokenRequest.clientId === apiClientId && apiTokenRequest.clientId === apiClientId &&
apiTokenRequest.clientSecret === apiClientSecret && apiTokenRequest.clientSecret === apiClientSecret &&
actual.device.identifier === deviceId && apiTokenRequest.device.identifier === deviceId &&
actual.provider == null && apiTokenRequest.twoFactor.provider == null &&
actual.token == null && apiTokenRequest.twoFactor.token == null &&
actual.captchaResponse == null apiTokenRequest.captchaResponse == null
); );
}) })
); );