mirror of
https://github.com/bitwarden/browser
synced 2026-01-06 10:33:57 +00:00
support for prelogin kdf info
This commit is contained in:
@@ -33,6 +33,7 @@ import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
||||
import { PasswordRequest } from '../models/request/passwordRequest';
|
||||
import { PasswordVerificationRequest } from '../models/request/passwordVerificationRequest';
|
||||
import { PaymentRequest } from '../models/request/paymentRequest';
|
||||
import { PreloginRequest } from '../models/request/preloginRequest';
|
||||
import { RegisterRequest } from '../models/request/registerRequest';
|
||||
import { SeatRequest } from '../models/request/seatRequest';
|
||||
import { StorageRequest } from '../models/request/storageRequest';
|
||||
@@ -77,6 +78,7 @@ import {
|
||||
OrganizationUserDetailsResponse,
|
||||
OrganizationUserUserDetailsResponse,
|
||||
} from '../models/response/organizationUserResponse';
|
||||
import { PreloginResponse } from '../models/response/preloginResponse';
|
||||
import { ProfileResponse } from '../models/response/profileResponse';
|
||||
import { SyncResponse } from '../models/response/syncResponse';
|
||||
import { TwoFactorAuthenticatorResponse } from '../models/response/twoFactorAuthenticatorResponse';
|
||||
@@ -196,6 +198,11 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return new ProfileResponse(r);
|
||||
}
|
||||
|
||||
async postPrelogin(request: PreloginRequest): Promise<PreloginResponse> {
|
||||
const r = await this.send('POST', '/accounts/prelogin', request, false, true);
|
||||
return new PreloginResponse(r);
|
||||
}
|
||||
|
||||
postEmailToken(request: EmailTokenRequest): Promise<any> {
|
||||
return this.send('POST', '/accounts/email-token', request, true, false);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
import { TwoFactorProviderType } from '../enums/twoFactorProviderType';
|
||||
|
||||
import { AuthResult } from '../models/domain/authResult';
|
||||
@@ -5,8 +6,10 @@ import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||
|
||||
import { DeviceRequest } from '../models/request/deviceRequest';
|
||||
import { KeysRequest } from '../models/request/keysRequest';
|
||||
import { PreloginRequest } from '../models/request/preloginRequest';
|
||||
import { TokenRequest } from '../models/request/tokenRequest';
|
||||
|
||||
import { ErrorResponse } from '../models/response/errorResponse';
|
||||
import { IdentityTokenResponse } from '../models/response/identityTokenResponse';
|
||||
import { IdentityTwoFactorResponse } from '../models/response/identityTwoFactorResponse';
|
||||
|
||||
@@ -77,6 +80,8 @@ export class AuthService {
|
||||
selectedTwoFactorProviderType: TwoFactorProviderType = null;
|
||||
|
||||
private key: SymmetricCryptoKey;
|
||||
private kdf: KdfType;
|
||||
private kdfIterations: number;
|
||||
|
||||
constructor(private cryptoService: CryptoService, private apiService: ApiService,
|
||||
private userService: UserService, private tokenService: TokenService,
|
||||
@@ -108,8 +113,7 @@ export class AuthService {
|
||||
|
||||
async logIn(email: string, masterPassword: string): Promise<AuthResult> {
|
||||
this.selectedTwoFactorProviderType = null;
|
||||
email = email.toLowerCase();
|
||||
const key = await this.cryptoService.makeKey(masterPassword, email);
|
||||
const key = await this.makePreloginKey(masterPassword, email);
|
||||
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
|
||||
return await this.logInHelper(email, hashedPassword, key);
|
||||
}
|
||||
@@ -123,8 +127,7 @@ export class AuthService {
|
||||
async logInComplete(email: string, masterPassword: string, twoFactorProvider: TwoFactorProviderType,
|
||||
twoFactorToken: string, remember?: boolean): Promise<AuthResult> {
|
||||
this.selectedTwoFactorProviderType = null;
|
||||
email = email.toLowerCase();
|
||||
const key = await this.cryptoService.makeKey(masterPassword, email);
|
||||
const key = await this.makePreloginKey(masterPassword, email);
|
||||
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
|
||||
return await this.logInHelper(email, hashedPassword, key, twoFactorProvider, twoFactorToken, remember);
|
||||
}
|
||||
@@ -195,6 +198,24 @@ export class AuthService {
|
||||
return providerType;
|
||||
}
|
||||
|
||||
async makePreloginKey(masterPassword: string, email: string): Promise<SymmetricCryptoKey> {
|
||||
email = email.toLowerCase();
|
||||
this.kdf = null;
|
||||
this.kdfIterations = null;
|
||||
try {
|
||||
const preloginResponse = await this.apiService.postPrelogin(new PreloginRequest(email));
|
||||
if (preloginResponse != null) {
|
||||
this.kdf = preloginResponse.kdf;
|
||||
this.kdfIterations = preloginResponse.kdfIterations;
|
||||
}
|
||||
} catch (e) {
|
||||
if (!(e instanceof ErrorResponse) || e.statusCode !== 404) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return this.cryptoService.makeKey(masterPassword, email, this.kdf, this.kdfIterations);
|
||||
}
|
||||
|
||||
private async logInHelper(email: string, hashedPassword: string, key: SymmetricCryptoKey,
|
||||
twoFactorProvider?: TwoFactorProviderType, twoFactorToken?: string, remember?: boolean): Promise<AuthResult> {
|
||||
const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(email);
|
||||
@@ -235,7 +256,8 @@ export class AuthService {
|
||||
}
|
||||
|
||||
await this.tokenService.setTokens(tokenResponse.accessToken, tokenResponse.refreshToken);
|
||||
await this.userService.setUserIdAndEmail(this.tokenService.getUserId(), this.tokenService.getEmail());
|
||||
await this.userService.setInformation(this.tokenService.getUserId(), this.tokenService.getEmail(),
|
||||
this.kdf, this.kdfIterations);
|
||||
if (this.setCryptoKeys) {
|
||||
await this.cryptoService.setKey(key);
|
||||
await this.cryptoService.setKeyHash(hashedPassword);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { EncryptionType } from '../enums/encryptionType';
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
|
||||
import { CipherString } from '../models/domain/cipherString';
|
||||
import { EncryptedObject } from '../models/domain/encryptedObject';
|
||||
@@ -272,8 +273,19 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
await this.setKey(key);
|
||||
}
|
||||
|
||||
async makeKey(password: string, salt: string): Promise<SymmetricCryptoKey> {
|
||||
const key = await this.cryptoFunctionService.pbkdf2(password, salt, 'sha256', 5000);
|
||||
async makeKey(password: string, salt: string, kdf: KdfType, kdfIterations: number):
|
||||
Promise<SymmetricCryptoKey> {
|
||||
let key: ArrayBuffer = null;
|
||||
if (kdf == null || kdf === KdfType.PBKDF2) {
|
||||
if (kdfIterations == null) {
|
||||
kdfIterations = 5000;
|
||||
} else if (kdfIterations < 5000) {
|
||||
throw new Error('PBKDF2 iteration minimum is 5000.');
|
||||
}
|
||||
key = await this.cryptoFunctionService.pbkdf2(password, salt, 'sha256', kdfIterations);
|
||||
} else {
|
||||
throw new Error('Unknown Kdf.');
|
||||
}
|
||||
return new SymmetricCryptoKey(key);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,28 +5,37 @@ import { UserService as UserServiceAbstraction } from '../abstractions/user.serv
|
||||
import { OrganizationData } from '../models/data/organizationData';
|
||||
import { Organization } from '../models/domain/organization';
|
||||
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
|
||||
const Keys = {
|
||||
userId: 'userId',
|
||||
userEmail: 'userEmail',
|
||||
stamp: 'securityStamp',
|
||||
kdf: 'kdf',
|
||||
kdfIterations: 'kdfIterations',
|
||||
organizationsPrefix: 'organizations_',
|
||||
};
|
||||
|
||||
export class UserService implements UserServiceAbstraction {
|
||||
userId: string;
|
||||
email: string;
|
||||
stamp: string;
|
||||
private userId: string;
|
||||
private email: string;
|
||||
private stamp: string;
|
||||
private kdf: KdfType;
|
||||
private kdfIterations: number;
|
||||
|
||||
constructor(private tokenService: TokenService, private storageService: StorageService) {
|
||||
}
|
||||
constructor(private tokenService: TokenService, private storageService: StorageService) { }
|
||||
|
||||
setUserIdAndEmail(userId: string, email: string): Promise<any> {
|
||||
setInformation(userId: string, email: string, kdf: KdfType, kdfIterations: number): Promise<any> {
|
||||
this.email = email;
|
||||
this.userId = userId;
|
||||
this.kdf = kdf;
|
||||
this.kdfIterations = kdfIterations;
|
||||
|
||||
return Promise.all([
|
||||
this.storageService.save(Keys.userEmail, email),
|
||||
this.storageService.save(Keys.userId, userId),
|
||||
this.storageService.save(Keys.kdf, kdf),
|
||||
this.storageService.save(Keys.kdfIterations, kdfIterations),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -62,6 +71,24 @@ export class UserService implements UserServiceAbstraction {
|
||||
return this.stamp;
|
||||
}
|
||||
|
||||
async getKdf(): Promise<KdfType> {
|
||||
if (this.kdf != null) {
|
||||
return this.kdf;
|
||||
}
|
||||
|
||||
this.kdf = await this.storageService.get<KdfType>(Keys.kdf);
|
||||
return this.kdf;
|
||||
}
|
||||
|
||||
async getKdfIterations(): Promise<number> {
|
||||
if (this.kdfIterations != null) {
|
||||
return this.kdfIterations;
|
||||
}
|
||||
|
||||
this.kdfIterations = await this.storageService.get<number>(Keys.kdfIterations);
|
||||
return this.kdfIterations;
|
||||
}
|
||||
|
||||
async clear(): Promise<any> {
|
||||
const userId = await this.getUserId();
|
||||
|
||||
@@ -69,10 +96,14 @@ export class UserService implements UserServiceAbstraction {
|
||||
this.storageService.remove(Keys.userId),
|
||||
this.storageService.remove(Keys.userEmail),
|
||||
this.storageService.remove(Keys.stamp),
|
||||
this.storageService.remove(Keys.kdf),
|
||||
this.storageService.remove(Keys.kdfIterations),
|
||||
this.clearOrganizations(userId),
|
||||
]);
|
||||
|
||||
this.userId = this.email = this.stamp = null;
|
||||
this.kdf = null;
|
||||
this.kdfIterations = null;
|
||||
}
|
||||
|
||||
async isAuthenticated(): Promise<boolean> {
|
||||
|
||||
Reference in New Issue
Block a user