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:
parent
595215a9da
commit
d84d6da7f7
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -9,4 +9,5 @@ export class SetPasswordRequest {
|
||||
keys: KeysRequest;
|
||||
kdf: KdfType;
|
||||
kdfIterations: number;
|
||||
orgIdentifier: string;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user