1
0
mirror of https://github.com/bitwarden/directory-connector synced 2025-12-05 23:53:21 +00:00

[Tech debt] Refactor authService (#213)

* Add OrganizationLogInStrategy

* Use noop TwoFactorService
This commit is contained in:
Thomas Rittson
2022-02-08 13:38:46 +10:00
committed by GitHub
parent 910bfb945d
commit 1146c8f5bf
9 changed files with 158 additions and 97 deletions

2
jslib

Submodule jslib updated: e0cc754d6f...6b8508579f

View File

@@ -12,8 +12,8 @@ import { StateService } from "../../abstractions/state.service";
import { ModalService } from "jslib-angular/services/modal.service"; import { ModalService } from "jslib-angular/services/modal.service";
import { HtmlStorageLocation } from "jslib-common/enums/htmlStorageLocation";
import { Utils } from "jslib-common/misc/utils"; import { Utils } from "jslib-common/misc/utils";
import { ApiLogInCredentials } from "jslib-common/models/domain/logInCredentials";
@Component({ @Component({
selector: "app-apiKey", selector: "app-apiKey",
@@ -76,7 +76,9 @@ export class ApiKeyComponent {
} }
try { try {
this.formPromise = this.authService.logInApiKey(this.clientId, this.clientSecret); this.formPromise = this.authService.logIn(
new ApiLogInCredentials(this.clientId, this.clientSecret)
);
await this.formPromise; await this.formPromise;
const organizationId = await this.stateService.getEntityId(); const organizationId = await this.stateService.getEntityId();
await this.stateService.setOrganizationId(organizationId); await this.stateService.setOrganizationId(organizationId);

View File

@@ -11,6 +11,7 @@ import { LaunchGuardService } from "./launch-guard.service";
import { I18nService } from "../../services/i18n.service"; import { I18nService } from "../../services/i18n.service";
import { SyncService } from "../../services/sync.service"; import { SyncService } from "../../services/sync.service";
import { NoopTwoFactorService } from "../../services/noop/noopTwoFactor.service";
import { JslibServicesModule } from "jslib-angular/services/jslib-services.module"; import { JslibServicesModule } from "jslib-angular/services/jslib-services.module";
@@ -34,11 +35,11 @@ import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "jslib-c
import { StateMigrationService as StateMigrationServiceAbstraction } from "jslib-common/abstractions/stateMigration.service"; import { StateMigrationService as StateMigrationServiceAbstraction } from "jslib-common/abstractions/stateMigration.service";
import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service"; import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service";
import { TokenService as TokenServiceAbstraction } from "jslib-common/abstractions/token.service"; import { TokenService as TokenServiceAbstraction } from "jslib-common/abstractions/token.service";
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service"; import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service";
import { StateService as StateServiceAbstraction } from "../../abstractions/state.service"; import { StateService as StateServiceAbstraction } from "../../abstractions/state.service";
import { ApiService, refreshToken } from "../../services/api.service"; import { refreshToken } from "../../services/api.service";
import { AuthService } from "../../services/auth.service"; import { AuthService } from "../../services/auth.service";
import { StateService } from "../../services/state.service"; import { StateService } from "../../services/state.service";
import { StateMigrationService } from "../../services/stateMigration.service"; import { StateMigrationService } from "../../services/stateMigration.service";
@@ -60,7 +61,6 @@ function refreshTokenCallback(injector: Injector) {
export function initFactory( export function initFactory(
environmentService: EnvironmentServiceAbstraction, environmentService: EnvironmentServiceAbstraction,
i18nService: I18nService, i18nService: I18nService,
authService: AuthService,
platformUtilsService: PlatformUtilsServiceAbstraction, platformUtilsService: PlatformUtilsServiceAbstraction,
stateService: StateServiceAbstraction, stateService: StateServiceAbstraction,
cryptoService: CryptoServiceAbstraction cryptoService: CryptoServiceAbstraction
@@ -69,7 +69,6 @@ export function initFactory(
await stateService.init(); await stateService.init();
await environmentService.setUrlsFromStorage(); await environmentService.setUrlsFromStorage();
await i18nService.init(); await i18nService.init();
authService.init();
const htmlEl = window.document.documentElement; const htmlEl = window.document.documentElement;
htmlEl.classList.add("os_" + platformUtilsService.getDeviceString()); htmlEl.classList.add("os_" + platformUtilsService.getDeviceString());
htmlEl.classList.add("locale_" + i18nService.translationLocale); htmlEl.classList.add("locale_" + i18nService.translationLocale);
@@ -103,7 +102,6 @@ export function initFactory(
deps: [ deps: [
EnvironmentServiceAbstraction, EnvironmentServiceAbstraction,
I18nServiceAbstraction, I18nServiceAbstraction,
AuthServiceAbstraction,
PlatformUtilsServiceAbstraction, PlatformUtilsServiceAbstraction,
StateServiceAbstraction, StateServiceAbstraction,
CryptoServiceAbstraction, CryptoServiceAbstraction,
@@ -170,15 +168,13 @@ export function initFactory(
ApiServiceAbstraction, ApiServiceAbstraction,
TokenServiceAbstraction, TokenServiceAbstraction,
AppIdServiceAbstraction, AppIdServiceAbstraction,
I18nServiceAbstraction,
PlatformUtilsServiceAbstraction, PlatformUtilsServiceAbstraction,
MessagingServiceAbstraction, MessagingServiceAbstraction,
VaultTimeoutServiceAbstraction,
LogServiceAbstraction, LogServiceAbstraction,
CryptoFunctionServiceAbstraction,
EnvironmentServiceAbstraction,
KeyConnectorServiceAbstraction, KeyConnectorServiceAbstraction,
EnvironmentServiceAbstraction,
StateServiceAbstraction, StateServiceAbstraction,
TwoFactorServiceAbstraction,
], ],
}, },
{ {
@@ -232,6 +228,10 @@ export function initFactory(
StateMigrationServiceAbstraction, StateMigrationServiceAbstraction,
], ],
}, },
{
provide: TwoFactorServiceAbstraction,
useClass: NoopTwoFactorService,
},
], ],
}) })
export class ServicesModule {} export class ServicesModule {}

View File

@@ -8,6 +8,7 @@ import { AuthService } from "./services/auth.service";
import { I18nService } from "./services/i18n.service"; import { I18nService } from "./services/i18n.service";
import { KeytarSecureStorageService } from "./services/keytarSecureStorage.service"; import { KeytarSecureStorageService } from "./services/keytarSecureStorage.service";
import { LowdbStorageService } from "./services/lowdbStorage.service"; import { LowdbStorageService } from "./services/lowdbStorage.service";
import { NoopTwoFactorService } from "./services/noop/noopTwoFactor.service";
import { StateService } from "./services/state.service"; import { StateService } from "./services/state.service";
import { StateMigrationService } from "./services/stateMigration.service"; import { StateMigrationService } from "./services/stateMigration.service";
import { SyncService } from "./services/sync.service"; import { SyncService } from "./services/sync.service";
@@ -37,15 +38,16 @@ import { SettingsService } from "jslib-common/services/settings.service";
import { TokenService } from "jslib-common/services/token.service"; import { TokenService } from "jslib-common/services/token.service";
import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service"; import { StorageService as StorageServiceAbstraction } from "jslib-common/abstractions/storage.service";
import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service";
import { Program } from "./program"; import { Program } from "./program";
import { Account } from "./models/account"; import { Account } from "./models/account";
import { GlobalStateFactory } from "jslib-common/factories/globalStateFactory";
import { StateFactory } from "jslib-common/factories/stateFactory"; import { StateFactory } from "jslib-common/factories/stateFactory";
import { GlobalState } from "jslib-common/models/domain/globalState"; import { GlobalState } from "jslib-common/models/domain/globalState";
import { ApiLogInCredentials } from "jslib-common/models/domain/logInCredentials";
// tslint:disable-next-line // tslint:disable-next-line
const packageJson = require("./package.json"); const packageJson = require("./package.json");
@@ -83,6 +85,7 @@ export class Main {
stateMigrationService: StateMigrationService; stateMigrationService: StateMigrationService;
organizationService: OrganizationService; organizationService: OrganizationService;
providerService: ProviderService; providerService: ProviderService;
twoFactorService: TwoFactorServiceAbstraction;
constructor() { constructor() {
const applicationName = "Bitwarden Directory Connector"; const applicationName = "Bitwarden Directory Connector";
@@ -160,7 +163,8 @@ export class Main {
" (" + " (" +
this.platformUtilsService.getDeviceString().toUpperCase() + this.platformUtilsService.getDeviceString().toUpperCase() +
")", ")",
(clientId, clientSecret) => this.authService.logInApiKey(clientId, clientSecret) (clientId, clientSecret) =>
this.authService.logIn(new ApiLogInCredentials(clientId, clientSecret))
); );
this.containerService = new ContainerService(this.cryptoService); this.containerService = new ContainerService(this.cryptoService);
@@ -172,23 +176,24 @@ export class Main {
this.apiService, this.apiService,
this.tokenService, this.tokenService,
this.logService, this.logService,
this.organizationService this.organizationService,
this.cryptoFunctionService
); );
this.twoFactorService = new NoopTwoFactorService();
this.authService = new AuthService( this.authService = new AuthService(
this.cryptoService, this.cryptoService,
this.apiService, this.apiService,
this.tokenService, this.tokenService,
this.appIdService, this.appIdService,
this.i18nService,
this.platformUtilsService, this.platformUtilsService,
this.messagingService, this.messagingService,
null,
this.logService, this.logService,
this.cryptoFunctionService,
this.environmentService,
this.keyConnectorService, this.keyConnectorService,
this.stateService this.environmentService,
this.stateService,
this.twoFactorService
); );
this.syncService = new SyncService( this.syncService = new SyncService(
@@ -281,7 +286,6 @@ export class Main {
// }); // });
const locale = await this.stateService.getLocale(); const locale = await this.stateService.getLocale();
await this.i18nService.init(locale); await this.i18nService.init(locale);
this.authService.init();
const installedVersion = await this.stateService.getInstalledVersion(); const installedVersion = await this.stateService.getInstalledVersion();
const currentVersion = await this.platformUtilsService.getApplicationVersion(); const currentVersion = await this.platformUtilsService.getApplicationVersion();

View File

@@ -0,0 +1,65 @@
import { LogInStrategy } from "jslib-common/misc/logInStrategies/logIn.strategy";
import { ApiTokenRequest } from "jslib-common/models/request/identityToken/apiTokenRequest";
import { IdentityTokenResponse } from "jslib-common/models/response/identityTokenResponse";
import { AccountKeys, AccountProfile, AccountTokens } from "jslib-common/models/domain/account";
import { AuthResult } from "jslib-common/models/domain/authResult";
import { ApiLogInCredentials } from "jslib-common/models/domain/logInCredentials";
import { Account, DirectoryConfigurations, DirectorySettings } from "src/models/account";
export class OrganizationLogInStrategy extends LogInStrategy {
tokenRequest: ApiTokenRequest;
async logIn(credentials: ApiLogInCredentials) {
this.tokenRequest = new ApiTokenRequest(
credentials.clientId,
credentials.clientSecret,
await this.buildTwoFactor(),
await this.buildDeviceRequest()
);
return this.startLogIn();
}
protected async processTokenResponse(response: IdentityTokenResponse): Promise<AuthResult> {
await this.saveAccountInformation(response);
return new AuthResult();
}
protected async saveAccountInformation(tokenResponse: IdentityTokenResponse) {
const clientId = this.tokenRequest.clientId;
const entityId = clientId.split("organization.")[1];
const clientSecret = this.tokenRequest.clientSecret;
await this.stateService.addAccount(
new Account({
profile: {
...new AccountProfile(),
...{
userId: entityId,
apiKeyClientId: clientId,
entityId: entityId,
},
},
tokens: {
...new AccountTokens(),
...{
accessToken: tokenResponse.accessToken,
refreshToken: tokenResponse.refreshToken,
},
},
keys: {
...new AccountKeys(),
...{
apiKeyClientSecret: clientSecret,
},
},
directorySettings: new DirectorySettings(),
directoryConfigurations: new DirectoryConfigurations(),
})
);
}
}

View File

@@ -106,6 +106,7 @@ export class Program extends BaseProgram {
this.main.stateService, this.main.stateService,
this.main.cryptoService, this.main.cryptoService,
this.main.policyService, this.main.policyService,
this.main.twoFactorService,
"connector" "connector"
); );

View File

@@ -4,6 +4,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
import { TokenService } from "jslib-common/abstractions/token.service"; import { TokenService } from "jslib-common/abstractions/token.service";
import { StateService } from "../abstractions/state.service"; import { StateService } from "../abstractions/state.service";
import { ApiLogInCredentials } from "jslib-common/models/domain/logInCredentials";
import { ApiService as ApiServiceBase } from "jslib-common/services/api.service"; import { ApiService as ApiServiceBase } from "jslib-common/services/api.service";
export async function refreshToken(stateService: StateService, authService: AuthService) { export async function refreshToken(stateService: StateService, authService: AuthService) {
@@ -11,7 +12,7 @@ export async function refreshToken(stateService: StateService, authService: Auth
const clientId = await stateService.getApiKeyClientId(); const clientId = await stateService.getApiKeyClientId();
const clientSecret = await stateService.getApiKeyClientSecret(); const clientSecret = await stateService.getApiKeyClientSecret();
if (clientId != null && clientSecret != null) { if (clientId != null && clientSecret != null) {
await authService.logInApiKey(clientId, clientSecret); await authService.logIn(new ApiLogInCredentials(clientId, clientSecret));
} }
} catch (e) { } catch (e) {
return Promise.reject(e); return Promise.reject(e);

View File

@@ -1,28 +1,22 @@
import { OrganizationLogInStrategy } from "../misc/logInStrategies/organizationLogIn.strategy";
import { ApiService } from "jslib-common/abstractions/api.service"; import { ApiService } from "jslib-common/abstractions/api.service";
import { AppIdService } from "jslib-common/abstractions/appId.service"; 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 { 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 { TokenService } from "jslib-common/abstractions/token.service"; import { TokenService } from "jslib-common/abstractions/token.service";
import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
import { StateService } from "../abstractions/state.service";
import { AuthService as AuthServiceBase } from "jslib-common/services/auth.service"; import { AuthService as AuthServiceBase } from "jslib-common/services/auth.service";
import { Account, DirectoryConfigurations, DirectorySettings } from "src/models/account";
import { AccountKeys, AccountProfile, AccountTokens } from "jslib-common/models/domain/account";
import { AuthResult } from "jslib-common/models/domain/authResult"; import { AuthResult } from "jslib-common/models/domain/authResult";
import { ApiLogInCredentials } from "jslib-common/models/domain/logInCredentials";
import { DeviceRequest } from "jslib-common/models/request/deviceRequest"; import { StateService } from "../abstractions/state.service";
import { TokenRequest } from "jslib-common/models/request/tokenRequest";
import { IdentityTokenResponse } from "jslib-common/models/response/identityTokenResponse";
export class AuthService extends AuthServiceBase { export class AuthService extends AuthServiceBase {
constructor( constructor(
@@ -30,90 +24,42 @@ export class AuthService extends AuthServiceBase {
apiService: ApiService, apiService: ApiService,
tokenService: TokenService, tokenService: TokenService,
appIdService: AppIdService, appIdService: AppIdService,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService, platformUtilsService: PlatformUtilsService,
messagingService: MessagingService, messagingService: MessagingService,
vaultTimeoutService: VaultTimeoutService,
logService: LogService, logService: LogService,
cryptoFunctionService: CryptoFunctionService,
environmentService: EnvironmentService,
keyConnectorService: KeyConnectorService, keyConnectorService: KeyConnectorService,
stateService: StateService environmentService: EnvironmentService,
stateService: StateService,
twoFactorService: TwoFactorService
) { ) {
super( super(
cryptoService, cryptoService,
apiService, apiService,
tokenService, tokenService,
appIdService, appIdService,
i18nService,
platformUtilsService, platformUtilsService,
messagingService, messagingService,
vaultTimeoutService,
logService, logService,
cryptoFunctionService,
keyConnectorService, keyConnectorService,
environmentService, environmentService,
stateService, stateService,
false twoFactorService
); );
} }
async logInApiKey(clientId: string, clientSecret: string): Promise<AuthResult> { async logIn(credentials: ApiLogInCredentials): Promise<AuthResult> {
this.selectedTwoFactorProviderType = null; const strategy = new OrganizationLogInStrategy(
if (clientId.startsWith("organization")) { this.cryptoService,
return await this.organizationLogInHelper(clientId, clientSecret); this.apiService,
} this.tokenService,
return await super.logInApiKey(clientId, clientSecret); this.appIdService,
} this.platformUtilsService,
this.messagingService,
private async organizationLogInHelper(clientId: string, clientSecret: string) { this.logService,
const appId = await this.appIdService.getAppId(); this.stateService,
const entityId = clientId.split("organization.")[1]; this.twoFactorService
const deviceRequest = new DeviceRequest(appId, this.platformUtilsService);
const request = new TokenRequest(
null,
null,
[clientId, clientSecret],
null,
null,
false,
null,
deviceRequest
); );
const response = await this.apiService.postIdentityToken(request); return strategy.logIn(credentials);
const result = new AuthResult();
result.twoFactor = !(response as any).accessToken;
const tokenResponse = response as IdentityTokenResponse;
result.resetMasterPassword = tokenResponse.resetMasterPassword;
await this.stateService.addAccount(
new Account({
profile: {
...new AccountProfile(),
...{
userId: entityId,
apiKeyClientId: clientId,
entityId: entityId,
},
},
tokens: {
...new AccountTokens(),
...{
accessToken: tokenResponse.accessToken,
refreshToken: tokenResponse.refreshToken,
},
},
keys: {
...new AccountKeys(),
...{
apiKeyClientSecret: clientSecret,
},
},
directorySettings: new DirectorySettings(),
directoryConfigurations: new DirectoryConfigurations(),
})
);
return result;
} }
} }

View File

@@ -0,0 +1,42 @@
import {
TwoFactorProviderDetails,
TwoFactorService,
} from "jslib-common/abstractions/twoFactor.service";
import { TwoFactorProviderType } from "jslib-common/enums/twoFactorProviderType";
import { IdentityTwoFactorResponse } from "jslib-common/models/response/identityTwoFactorResponse";
export class NoopTwoFactorService implements TwoFactorService {
init() {
// Noop
}
getSupportedProviders(win: Window): TwoFactorProviderDetails[] {
return null;
}
getDefaultProvider(webAuthnSupported: boolean): TwoFactorProviderType {
return null;
}
setSelectedProvider(type: TwoFactorProviderType) {
// Noop
}
clearSelectedProvider() {
// Noop
}
setProviders(response: IdentityTwoFactorResponse) {
// Noop
}
clearProviders() {
// Noop
}
getProviders(): Map<TwoFactorProviderType, { [key: string]: string }> {
return null;
}
}