Skip to content

Commit

Permalink
[SSO] New user provision flow (#173)
Browse files Browse the repository at this point in the history
* Initial commit of new user sso flow

* Adjusted stateSplit conditional per review
  • Loading branch information
vincentsalucci committed Oct 13, 2020
1 parent 595215a commit d84d6da
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 11 deletions.
20 changes: 18 additions & 2 deletions src/angular/components/set-password.component.ts
@@ -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';
Expand All @@ -24,21 +27,33 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
syncLoading: boolean = true;
showPassword: boolean = false;
hint: string = '';
identifier: string = null;

onSuccessfulChangePassword: () => Promise<any>;
successRoute = 'vault';

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);
}

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();
}

Expand All @@ -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);
Expand Down
29 changes: 23 additions & 6 deletions src/angular/components/sso.component.ts
Expand Up @@ -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) {
Expand Down Expand Up @@ -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&' +
Expand All @@ -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);
Expand All @@ -140,7 +144,7 @@ export class SsoComponent {
} else {
this.router.navigate([this.twoFactorRoute], {
queryParams: {
resetMasterPassword: response.resetMasterPassword,
identifier: orgIdFromState,
},
});
}
Expand All @@ -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);
Expand All @@ -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;
}
}
24 changes: 21 additions & 3 deletions src/angular/components/two-factor.component.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -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>;

Expand All @@ -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);
}

Expand All @@ -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';
}
Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/models/request/setPasswordRequest.ts
Expand Up @@ -9,4 +9,5 @@ export class SetPasswordRequest {
keys: KeysRequest;
kdf: KdfType;
kdfIterations: number;
orgIdentifier: string;
}

0 comments on commit d84d6da

Please sign in to comment.