1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-23 11:56:00 +01:00

[SSO] New user provision flow (#173)

* Initial commit of new user sso flow

* Adjusted stateSplit conditional per review
This commit is contained in:
Vincent Salucci 2020-10-13 15:21:03 -05:00 committed by GitHub
parent 595215a9da
commit d84d6da7f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 11 deletions

View File

@ -1,4 +1,7 @@
import { Router } from '@angular/router';
import {
ActivatedRoute,
Router
} from '@angular/router';
import { ApiService } from '../../abstractions/api.service';
import { CryptoService } from '../../abstractions/crypto.service';
@ -24,6 +27,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
syncLoading: boolean = true;
showPassword: boolean = false;
hint: string = '';
identifier: string = null;
onSuccessfulChangePassword: () => Promise<any>;
successRoute = 'vault';
@ -31,7 +35,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
constructor(i18nService: I18nService, cryptoService: CryptoService, messagingService: MessagingService,
userService: UserService, passwordGenerationService: PasswordGenerationService,
platformUtilsService: PlatformUtilsService, policyService: PolicyService, private router: Router,
private apiService: ApiService, private syncService: SyncService) {
private apiService: ApiService, private syncService: SyncService, private route: ActivatedRoute) {
super(i18nService, cryptoService, messagingService, userService, passwordGenerationService,
platformUtilsService, policyService);
}
@ -39,6 +43,17 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
async ngOnInit() {
await this.syncService.fullSync(true);
this.syncLoading = false;
const queryParamsSub = this.route.queryParams.subscribe(async (qParams) => {
if (qParams.identifier != null) {
this.identifier = qParams.identifier;
}
if (queryParamsSub != null) {
queryParamsSub.unsubscribe();
}
});
super.ngOnInit();
}
@ -57,6 +72,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
request.masterPasswordHint = this.hint;
request.kdf = this.kdf;
request.kdfIterations = this.kdfIterations;
request.orgIdentifier = this.identifier;
const keys = await this.cryptoService.makeKeyPair(encKey[0]);
request.keys = new KeysRequest(keys[0], keys[1].encryptedString);

View File

@ -52,7 +52,7 @@ export class SsoComponent {
await this.storageService.remove(ConstantsService.ssoCodeVerifierKey);
await this.storageService.remove(ConstantsService.ssoStateKey);
if (qParams.code != null && codeVerifier != null && state != null && state === qParams.state) {
await this.logIn(qParams.code, codeVerifier);
await this.logIn(qParams.code, codeVerifier, this.getOrgIdentiferFromState(state));
}
} else if (qParams.clientId != null && qParams.redirectUri != null && qParams.state != null &&
qParams.codeChallenge != null) {
@ -109,10 +109,14 @@ export class SsoComponent {
if (returnUri) {
state += `_returnUri='${returnUri}'`;
}
await this.storageService.save(ConstantsService.ssoStateKey, state);
}
// Add Organization Identifier to state
state += `_identifier=${this.identifier}`;
// Save state (regardless of new or existing)
await this.storageService.save(ConstantsService.ssoStateKey, state);
let authorizeUrl = this.apiService.identityBaseUrl + '/connect/authorize?' +
'client_id=' + this.clientId + '&redirect_uri=' + encodeURIComponent(this.redirectUri) + '&' +
'response_type=code&scope=api offline_access&' +
@ -128,7 +132,7 @@ export class SsoComponent {
return authorizeUrl;
}
private async logIn(code: string, codeVerifier: string) {
private async logIn(code: string, codeVerifier: string, orgIdFromState: string) {
this.loggingIn = true;
try {
this.formPromise = this.authService.logInSso(code, codeVerifier, this.redirectUri);
@ -140,7 +144,7 @@ export class SsoComponent {
} else {
this.router.navigate([this.twoFactorRoute], {
queryParams: {
resetMasterPassword: response.resetMasterPassword,
identifier: orgIdFromState,
},
});
}
@ -149,7 +153,11 @@ export class SsoComponent {
if (this.onSuccessfulLoginChangePasswordNavigate != null) {
this.onSuccessfulLoginChangePasswordNavigate();
} else {
this.router.navigate([this.changePasswordRoute]);
this.router.navigate([this.changePasswordRoute], {
queryParams: {
identifier: orgIdFromState,
},
});
}
} else {
const disableFavicon = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
@ -167,4 +175,13 @@ export class SsoComponent {
} catch { }
this.loggingIn = false;
}
private getOrgIdentiferFromState(state: string): string {
if (!state) {
return null;
}
const stateSplit = state.split('_identifier=');
return stateSplit.length > 1 ? stateSplit[1] : null;
}
}

View File

@ -3,7 +3,10 @@ import {
OnInit,
} from '@angular/core';
import { Router } from '@angular/router';
import {
ActivatedRoute,
Router,
} from '@angular/router';
import { DeviceType } from '../../enums/deviceType';
import { TwoFactorProviderType } from '../../enums/twoFactorProviderType';
@ -40,6 +43,7 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
twoFactorEmail: string = null;
formPromise: Promise<any>;
emailPromise: Promise<any>;
identifier: string = null;
onSuccessfulLogin: () => Promise<any>;
onSuccessfulLoginNavigate: () => Promise<any>;
@ -50,7 +54,7 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
protected i18nService: I18nService, protected apiService: ApiService,
protected platformUtilsService: PlatformUtilsService, protected win: Window,
protected environmentService: EnvironmentService, protected stateService: StateService,
protected storageService: StorageService) {
protected storageService: StorageService, protected route: ActivatedRoute) {
this.u2fSupported = this.platformUtilsService.supportsU2f(win);
}
@ -61,6 +65,16 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
return;
}
const queryParamsSub = this.route.queryParams.subscribe(async (qParams) => {
if (qParams.identifier != null) {
this.identifier = qParams.identifier;
}
if (queryParamsSub != null) {
queryParamsSub.unsubscribe();
}
});
if (this.authService.authingWithSso()) {
this.successRoute = 'lock';
}
@ -191,7 +205,11 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
if (response.resetMasterPassword) {
this.successRoute = 'set-password';
}
this.router.navigate([this.successRoute]);
this.router.navigate([this.successRoute], {
queryParams: {
identifier: this.identifier,
},
});
}
} catch {
if (this.selectedProviderType === TwoFactorProviderType.U2f && this.u2f != null) {

View File

@ -9,4 +9,5 @@ export class SetPasswordRequest {
keys: KeysRequest;
kdf: KdfType;
kdfIterations: number;
orgIdentifier: string;
}