mirror of
https://github.com/bitwarden/jslib
synced 2025-12-17 16:53:20 +00:00
[Tech debt] Refactor authService and remove LogInHelper (#588)
* Use different strategy classes for different types of login * General refactor and cleanup of auth logic * Create subclasses for different types of login credentials * Create subclasses for different types of tokenRequests * Create TwoFactorService, move code out of authService * refactor base CLI commands to use new interface
This commit is contained in:
@@ -1,9 +1,18 @@
|
||||
import { TwoFactorProviderType } from "../../enums/twoFactorProviderType";
|
||||
|
||||
import { Utils } from "../../misc/utils";
|
||||
|
||||
export class AuthResult {
|
||||
twoFactor: boolean = false;
|
||||
captchaSiteKey: string = "";
|
||||
resetMasterPassword: boolean = false;
|
||||
forcePasswordReset: boolean = false;
|
||||
twoFactorProviders: Map<TwoFactorProviderType, { [key: string]: string }> = null;
|
||||
|
||||
get requiresCaptcha() {
|
||||
return !Utils.isNullOrWhitespace(this.captchaSiteKey);
|
||||
}
|
||||
|
||||
get requiresTwoFactor() {
|
||||
return this.twoFactorProviders != null;
|
||||
}
|
||||
}
|
||||
|
||||
24
common/src/models/domain/logInCredentials.ts
Normal file
24
common/src/models/domain/logInCredentials.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { TokenRequestTwoFactor } from "../request/identityToken/tokenRequest";
|
||||
|
||||
export class PasswordLogInCredentials {
|
||||
constructor(
|
||||
public email: string,
|
||||
public masterPassword: string,
|
||||
public captchaToken?: string,
|
||||
public twoFactor?: TokenRequestTwoFactor
|
||||
) {}
|
||||
}
|
||||
|
||||
export class SsoLogInCredentials {
|
||||
constructor(
|
||||
public code: string,
|
||||
public codeVerifier: string,
|
||||
public redirectUrl: string,
|
||||
public orgId: string,
|
||||
public twoFactor?: TokenRequestTwoFactor
|
||||
) {}
|
||||
}
|
||||
|
||||
export class ApiLogInCredentials {
|
||||
constructor(public clientId: string, public clientSecret: string) {}
|
||||
}
|
||||
24
common/src/models/request/identityToken/apiTokenRequest.ts
Normal file
24
common/src/models/request/identityToken/apiTokenRequest.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { TokenRequest, TokenRequestTwoFactor } from "./tokenRequest";
|
||||
|
||||
import { DeviceRequest } from "../deviceRequest";
|
||||
|
||||
export class ApiTokenRequest extends TokenRequest {
|
||||
constructor(
|
||||
public clientId: string,
|
||||
public clientSecret: string,
|
||||
protected twoFactor: TokenRequestTwoFactor,
|
||||
device?: DeviceRequest
|
||||
) {
|
||||
super(twoFactor, device);
|
||||
}
|
||||
|
||||
toIdentityToken() {
|
||||
const obj = super.toIdentityToken(this.clientId);
|
||||
|
||||
obj.scope = this.clientId.startsWith("organization") ? "api.organization" : "api";
|
||||
obj.grant_type = "client_credentials";
|
||||
obj.client_secret = this.clientSecret;
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { TokenRequest, TokenRequestTwoFactor } from "./tokenRequest";
|
||||
|
||||
import { CaptchaProtectedRequest } from "../captchaProtectedRequest";
|
||||
import { DeviceRequest } from "../deviceRequest";
|
||||
|
||||
import { Utils } from "../../../misc/utils";
|
||||
|
||||
export class PasswordTokenRequest extends TokenRequest implements CaptchaProtectedRequest {
|
||||
constructor(
|
||||
public email: string,
|
||||
public masterPasswordHash: string,
|
||||
public captchaResponse: string,
|
||||
protected twoFactor: TokenRequestTwoFactor,
|
||||
device?: DeviceRequest
|
||||
) {
|
||||
super(twoFactor, device);
|
||||
}
|
||||
|
||||
toIdentityToken(clientId: string) {
|
||||
const obj = super.toIdentityToken(clientId);
|
||||
|
||||
obj.grant_type = "password";
|
||||
obj.username = this.email;
|
||||
obj.password = this.masterPasswordHash;
|
||||
|
||||
if (this.captchaResponse != null) {
|
||||
obj.captchaResponse = this.captchaResponse;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
alterIdentityTokenHeaders(headers: Headers) {
|
||||
headers.set("Auth-Email", Utils.fromUtf8ToUrlB64(this.email));
|
||||
}
|
||||
}
|
||||
26
common/src/models/request/identityToken/ssoTokenRequest.ts
Normal file
26
common/src/models/request/identityToken/ssoTokenRequest.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { TokenRequest, TokenRequestTwoFactor } from "./tokenRequest";
|
||||
|
||||
import { DeviceRequest } from "../deviceRequest";
|
||||
|
||||
export class SsoTokenRequest extends TokenRequest {
|
||||
constructor(
|
||||
public code: string,
|
||||
public codeVerifier: string,
|
||||
public redirectUri: string,
|
||||
protected twoFactor: TokenRequestTwoFactor,
|
||||
device?: DeviceRequest
|
||||
) {
|
||||
super(twoFactor, device);
|
||||
}
|
||||
|
||||
toIdentityToken(clientId: string) {
|
||||
const obj = super.toIdentityToken(clientId);
|
||||
|
||||
obj.grant_type = "authorization_code";
|
||||
obj.code = this.code;
|
||||
obj.code_verifier = this.codeVerifier;
|
||||
obj.redirect_uri = this.redirectUri;
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
48
common/src/models/request/identityToken/tokenRequest.ts
Normal file
48
common/src/models/request/identityToken/tokenRequest.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { TwoFactorProviderType } from "../../../enums/twoFactorProviderType";
|
||||
|
||||
import { DeviceRequest } from "../deviceRequest";
|
||||
|
||||
export interface TokenRequestTwoFactor {
|
||||
provider: TwoFactorProviderType;
|
||||
token: string;
|
||||
remember: boolean;
|
||||
}
|
||||
|
||||
export abstract class TokenRequest {
|
||||
protected device?: DeviceRequest;
|
||||
|
||||
constructor(protected twoFactor: TokenRequestTwoFactor, device?: DeviceRequest) {
|
||||
this.device = device != null ? device : null;
|
||||
}
|
||||
|
||||
alterIdentityTokenHeaders(headers: Headers) {
|
||||
// Implemented in subclass if required
|
||||
}
|
||||
|
||||
setTwoFactor(twoFactor: TokenRequestTwoFactor) {
|
||||
this.twoFactor = twoFactor;
|
||||
}
|
||||
|
||||
protected toIdentityToken(clientId: string) {
|
||||
const obj: any = {
|
||||
scope: "api offline_access",
|
||||
client_id: clientId,
|
||||
};
|
||||
|
||||
if (this.device) {
|
||||
obj.deviceType = this.device.type;
|
||||
obj.deviceIdentifier = this.device.identifier;
|
||||
obj.deviceName = this.device.name;
|
||||
// no push tokens for browser apps yet
|
||||
// obj.devicePushToken = this.device.pushToken;
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import { TwoFactorProviderType } from "../../enums/twoFactorProviderType";
|
||||
|
||||
import { CaptchaProtectedRequest } from "./captchaProtectedRequest";
|
||||
import { DeviceRequest } from "./deviceRequest";
|
||||
|
||||
import { Utils } from "../../misc/utils";
|
||||
|
||||
export class TokenRequest implements CaptchaProtectedRequest {
|
||||
email: string;
|
||||
masterPasswordHash: string;
|
||||
code: string;
|
||||
codeVerifier: string;
|
||||
redirectUri: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
device?: DeviceRequest;
|
||||
|
||||
constructor(
|
||||
credentials: string[],
|
||||
codes: string[],
|
||||
clientIdClientSecret: string[],
|
||||
public provider: TwoFactorProviderType,
|
||||
public token: string,
|
||||
public remember: boolean,
|
||||
public captchaResponse: string,
|
||||
device?: DeviceRequest
|
||||
) {
|
||||
if (credentials != null && credentials.length > 1) {
|
||||
this.email = credentials[0];
|
||||
this.masterPasswordHash = credentials[1];
|
||||
} else if (codes != null && codes.length > 2) {
|
||||
this.code = codes[0];
|
||||
this.codeVerifier = codes[1];
|
||||
this.redirectUri = codes[2];
|
||||
} else if (clientIdClientSecret != null && clientIdClientSecret.length > 1) {
|
||||
this.clientId = clientIdClientSecret[0];
|
||||
this.clientSecret = clientIdClientSecret[1];
|
||||
}
|
||||
this.device = device != null ? device : null;
|
||||
}
|
||||
|
||||
toIdentityToken(clientId: string) {
|
||||
const obj: any = {
|
||||
scope: "api offline_access",
|
||||
client_id: clientId,
|
||||
};
|
||||
|
||||
if (this.clientSecret != null) {
|
||||
obj.scope = clientId.startsWith("organization") ? "api.organization" : "api";
|
||||
obj.grant_type = "client_credentials";
|
||||
obj.client_secret = this.clientSecret;
|
||||
} else if (this.masterPasswordHash != null && this.email != null) {
|
||||
obj.grant_type = "password";
|
||||
obj.username = this.email;
|
||||
obj.password = this.masterPasswordHash;
|
||||
} else if (this.code != null && this.codeVerifier != null && this.redirectUri != null) {
|
||||
obj.grant_type = "authorization_code";
|
||||
obj.code = this.code;
|
||||
obj.code_verifier = this.codeVerifier;
|
||||
obj.redirect_uri = this.redirectUri;
|
||||
} else {
|
||||
throw new Error("must provide credentials or codes");
|
||||
}
|
||||
|
||||
if (this.device) {
|
||||
obj.deviceType = this.device.type;
|
||||
obj.deviceIdentifier = this.device.identifier;
|
||||
obj.deviceName = this.device.name;
|
||||
// no push tokens for browser apps yet
|
||||
// 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.captchaResponse != null) {
|
||||
obj.captchaResponse = this.captchaResponse;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
alterIdentityTokenHeaders(headers: Headers) {
|
||||
if (this.clientSecret == null && this.masterPasswordHash != null && this.email != null) {
|
||||
headers.set("Auth-Email", Utils.fromUtf8ToUrlB64(this.email));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user