Modifications made to support SSO in Browser

This commit is contained in:
Matt Smith 2020-08-11 15:25:07 -05:00
parent 679c7780e0
commit 1d0b549123
11 changed files with 163 additions and 17 deletions

View File

@ -19,6 +19,9 @@
"login": { "login": {
"message": "Log In" "message": "Log In"
}, },
"enterpriseSingleSignOn": {
"message": "Enterprise Single Sign-On"
},
"cancel": { "cancel": {
"message": "Cancel" "message": "Cancel"
}, },
@ -1297,4 +1300,4 @@
"vaultTimeoutLogOutConfirmationTitle": { "vaultTimeoutLogOutConfirmationTitle": {
"message": "Timeout Action Confirmation" "message": "Timeout Action Confirmation"
} }
} }

View File

@ -4,6 +4,7 @@ import {
ApiService, ApiService,
AppIdService, AppIdService,
AuditService, AuditService,
AuthService,
CipherService, CipherService,
CollectionService, CollectionService,
ConstantsService, ConstantsService,
@ -13,6 +14,7 @@ import {
FolderService, FolderService,
PasswordGenerationService, PasswordGenerationService,
SettingsService, SettingsService,
StateService,
SyncService, SyncService,
TokenService, TokenService,
TotpService, TotpService,
@ -31,6 +33,7 @@ import {
ApiService as ApiServiceAbstraction, ApiService as ApiServiceAbstraction,
AppIdService as AppIdServiceAbstraction, AppIdService as AppIdServiceAbstraction,
AuditService as AuditServiceAbstraction, AuditService as AuditServiceAbstraction,
AuthService as AuthServiceAbstraction,
CipherService as CipherServiceAbstraction, CipherService as CipherServiceAbstraction,
CollectionService as CollectionServiceAbstraction, CollectionService as CollectionServiceAbstraction,
CryptoService as CryptoServiceAbstraction, CryptoService as CryptoServiceAbstraction,
@ -41,6 +44,7 @@ import {
PasswordGenerationService as PasswordGenerationServiceAbstraction, PasswordGenerationService as PasswordGenerationServiceAbstraction,
PlatformUtilsService as PlatformUtilsServiceAbstraction, PlatformUtilsService as PlatformUtilsServiceAbstraction,
SettingsService as SettingsServiceAbstraction, SettingsService as SettingsServiceAbstraction,
StateService as StateServiceAbstraction,
StorageService as StorageServiceAbstraction, StorageService as StorageServiceAbstraction,
SyncService as SyncServiceAbstraction, SyncService as SyncServiceAbstraction,
TokenService as TokenServiceAbstraction, TokenService as TokenServiceAbstraction,
@ -101,9 +105,11 @@ export default class MainBackground {
autofillService: AutofillServiceAbstraction; autofillService: AutofillServiceAbstraction;
containerService: ContainerService; containerService: ContainerService;
auditService: AuditServiceAbstraction; auditService: AuditServiceAbstraction;
authService: AuthServiceAbstraction;
exportService: ExportServiceAbstraction; exportService: ExportServiceAbstraction;
searchService: SearchServiceAbstraction; searchService: SearchServiceAbstraction;
notificationsService: NotificationsServiceAbstraction; notificationsService: NotificationsServiceAbstraction;
stateService: StateServiceAbstraction;
systemService: SystemServiceAbstraction; systemService: SystemServiceAbstraction;
eventService: EventServiceAbstraction; eventService: EventServiceAbstraction;
policyService: PolicyServiceAbstraction; policyService: PolicyServiceAbstraction;
@ -147,6 +153,9 @@ export default class MainBackground {
this.apiService = new ApiService(this.tokenService, this.platformUtilsService, this.apiService = new ApiService(this.tokenService, this.platformUtilsService,
(expired: boolean) => this.logout(expired)); (expired: boolean) => this.logout(expired));
this.userService = new UserService(this.tokenService, this.storageService); this.userService = new UserService(this.tokenService, this.storageService);
this.authService = new AuthService(this.cryptoService, this.apiService, this.userService,
this.tokenService, this.appIdService, this.i18nService, this.platformUtilsService,
this.messagingService, this.vaultTimeoutService, null);
this.settingsService = new SettingsService(this.userService, this.storageService); this.settingsService = new SettingsService(this.userService, this.storageService);
this.cipherService = new CipherService(this.cryptoService, this.userService, this.settingsService, this.cipherService = new CipherService(this.cryptoService, this.userService, this.settingsService,
this.apiService, this.storageService, this.i18nService, () => this.searchService); this.apiService, this.storageService, this.i18nService, () => this.searchService);
@ -155,6 +164,7 @@ export default class MainBackground {
this.collectionService = new CollectionService(this.cryptoService, this.userService, this.storageService, this.collectionService = new CollectionService(this.cryptoService, this.userService, this.storageService,
this.i18nService); this.i18nService);
this.searchService = new SearchService(this.cipherService, this.platformUtilsService); this.searchService = new SearchService(this.cipherService, this.platformUtilsService);
this.stateService = new StateService();
this.policyService = new PolicyService(this.userService, this.storageService); this.policyService = new PolicyService(this.userService, this.storageService);
this.vaultTimeoutService = new VaultTimeoutService(this.cipherService, this.folderService, this.vaultTimeoutService = new VaultTimeoutService(this.cipherService, this.folderService,
this.collectionService, this.cryptoService, this.platformUtilsService, this.storageService, this.collectionService, this.cryptoService, this.platformUtilsService, this.storageService,
@ -206,7 +216,8 @@ export default class MainBackground {
// Background // Background
this.runtimeBackground = new RuntimeBackground(this, this.autofillService, this.cipherService, this.runtimeBackground = new RuntimeBackground(this, this.autofillService, this.cipherService,
this.platformUtilsService as BrowserPlatformUtilsService, this.storageService, this.i18nService, this.platformUtilsService as BrowserPlatformUtilsService, this.storageService, this.i18nService,
this.analytics, this.notificationsService, this.systemService, this.vaultTimeoutService); this.analytics, this.notificationsService, this.systemService, this.vaultTimeoutService, this.syncService,
this.authService, this.stateService, this.environmentService);
this.commandsBackground = new CommandsBackground(this, this.passwordGenerationService, this.commandsBackground = new CommandsBackground(this, this.passwordGenerationService,
this.platformUtilsService, this.analytics, this.vaultTimeoutService); this.platformUtilsService, this.analytics, this.vaultTimeoutService);

View File

@ -4,14 +4,18 @@ import { CipherView } from 'jslib/models/view/cipherView';
import { LoginUriView } from 'jslib/models/view/loginUriView'; import { LoginUriView } from 'jslib/models/view/loginUriView';
import { LoginView } from 'jslib/models/view/loginView'; import { LoginView } from 'jslib/models/view/loginView';
import { ConstantsService } from 'jslib/services/constants.service'; import { AuthResult } from 'jslib/models/domain/authResult';
import { AuthService } from 'jslib/abstractions/auth.service';
import { I18nService } from 'jslib/abstractions/i18n.service'; import { AutofillService } from '../services/abstractions/autofill.service';
import BrowserPlatformUtilsService from '../services/browserPlatformUtils.service';
import { Analytics } from 'jslib/misc';
import { CipherService } from 'jslib/abstractions/cipher.service'; import { CipherService } from 'jslib/abstractions/cipher.service';
import { ConstantsService } from 'jslib/services/constants.service';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { NotificationsService } from 'jslib/abstractions/notifications.service';
import { StateService } from 'jslib/abstractions/state.service';
import { StorageService } from 'jslib/abstractions/storage.service'; import { StorageService } from 'jslib/abstractions/storage.service';
import { SyncService } from 'jslib/abstractions/sync.service';
import { SystemService } from 'jslib/abstractions/system.service'; import { SystemService } from 'jslib/abstractions/system.service';
import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service'; import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service';
@ -19,11 +23,7 @@ import { BrowserApi } from '../browser/browserApi';
import MainBackground from './main.background'; import MainBackground from './main.background';
import { AutofillService } from '../services/abstractions/autofill.service'; import { Analytics } from 'jslib/misc';
import BrowserPlatformUtilsService from '../services/browserPlatformUtils.service';
import { NotificationsService } from 'jslib/abstractions/notifications.service';
import { Utils } from 'jslib/misc/utils'; import { Utils } from 'jslib/misc/utils';
export default class RuntimeBackground { export default class RuntimeBackground {
@ -33,11 +33,19 @@ export default class RuntimeBackground {
private isSafari: boolean; private isSafari: boolean;
private onInstalledReason: string = null; private onInstalledReason: string = null;
formPromise: Promise<AuthResult>;
onSuccessfulLoginNavigate: () => Promise<any>;
onSuccessfulLoginTwoFactorNavigate: () => Promise<any>;
loggingIn = false;
private redirectUri = 'https://localhost:8080/sso-connector.html';
constructor(private main: MainBackground, private autofillService: AutofillService, constructor(private main: MainBackground, private autofillService: AutofillService,
private cipherService: CipherService, private platformUtilsService: BrowserPlatformUtilsService, private cipherService: CipherService, private platformUtilsService: BrowserPlatformUtilsService,
private storageService: StorageService, private i18nService: I18nService, private storageService: StorageService, private i18nService: I18nService,
private analytics: Analytics, private notificationsService: NotificationsService, private analytics: Analytics, private notificationsService: NotificationsService,
private systemService: SystemService, private vaultTimeoutService: VaultTimeoutService) { private systemService: SystemService, private vaultTimeoutService: VaultTimeoutService,
private syncService: SyncService, private authService: AuthService, private stateService: StateService,
private environmentService: EnvironmentService) {
this.isSafari = this.platformUtilsService.isSafari(); this.isSafari = this.platformUtilsService.isSafari();
this.runtime = this.isSafari ? {} : chrome.runtime; this.runtime = this.isSafari ? {} : chrome.runtime;
@ -47,6 +55,52 @@ export default class RuntimeBackground {
this.onInstalledReason = details.reason; this.onInstalledReason = details.reason;
}); });
} }
chrome.runtime.onMessage.addListener(
(request: any) => {
var vaultUrl = environmentService.webVaultUrl;
if(!vaultUrl) {
vaultUrl = 'https://vault.bitwarden.com';
// vaultUrl = 'https://localhost:8080';
}
if(!request.referrer) {
return;
}
if(!vaultUrl.includes(request.referrer)) {
return;
}
if (request.type == "AUTH_RESULT") {
try {
this.logIn(request.code, request.codeVerifier);
}
catch { }
}
});
}
async logIn(code: string, codeVerifier: string) {
this.loggingIn = true;
try {
this.formPromise = this.authService.logInSso(code, codeVerifier, this.redirectUri);
const response = await this.formPromise;
if (response) {
this.syncService.fullSync(true);
this.main.openPopup();
var sidebarName : string = this.platformUtilsService.sidebarViewName();
var sidebarWindows = chrome.extension.getViews({ type: sidebarName });
if(sidebarWindows && sidebarWindows.length > 0) {
sidebarWindows[0].location.reload();
}
}
} catch(error) { console.log(error); }
this.loggingIn = false;
} }
async init() { async init() {

15
src/content/sso.ts Normal file
View File

@ -0,0 +1,15 @@
window.addEventListener("message", function(event) {
if (event.source != window)
return;
console.log(event.source);
if (event.data.type && (event.data.type == "AUTH_RESULT")) {
chrome.runtime.sendMessage({
type: event.data.type,
code: event.data.code,
codeVerifier: event.data.codeVerifier,
referrer: event.source.location.hostname
});
}
}, false)

View File

@ -41,6 +41,18 @@
], ],
"run_at": "document_start" "run_at": "document_start"
}, },
{
"all_frames": false,
"js": [
"content/sso.js"
],
"matches": [
"http://*/*",
"https://*/*",
"file:///*"
],
"run_at": "document_start"
},
{ {
"all_frames": true, "all_frames": true,
"css": [ "css": [

View File

@ -3,6 +3,9 @@
<div class="logo-image"></div> <div class="logo-image"></div>
<p class="lead text-center">{{'loginOrCreateNewAccount' | i18n}}</p> <p class="lead text-center">{{'loginOrCreateNewAccount' | i18n}}</p>
<a class="btn primary block" routerLink="/login"><b>{{'login' | i18n}}</b></a> <a class="btn primary block" routerLink="/login"><b>{{'login' | i18n}}</b></a>
<a (click)="launchSsoBrowser()" class="btn block">
<i class="fa fa-bank" aria-hidden="true"></i> {{'enterpriseSingleSignOn' | i18n}}
</a>
<a class="btn block" routerLink="/register">{{'createAccount' | i18n}}</a> <a class="btn block" routerLink="/register">{{'createAccount' | i18n}}</a>
</div> </div>
</div> </div>

View File

@ -1,7 +1,45 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { CryptoFunctionService } from 'jslib/abstractions/cryptoFunction.service';
import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { PlatformUtilsService } from '../../../jslib/src/abstractions/platformUtils.service';
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
import { Utils } from 'jslib/misc/utils';
@Component({ @Component({
selector: 'app-home', selector: 'app-home',
templateUrl: 'home.component.html', templateUrl: 'home.component.html',
}) })
export class HomeComponent { } export class HomeComponent {
constructor(
protected platformUtilsService: PlatformUtilsService, private passwordGenerationService : PasswordGenerationService,
private cryptoFunctionService: CryptoFunctionService, private environmentService: EnvironmentService) { }
async launchSsoBrowser() {
// Generate necessary sso params
const passwordOptions: any = {
type: 'password',
length: 64,
uppercase: true,
lowercase: true,
numbers: true,
special: false,
};
const state = await this.passwordGenerationService.generatePassword(passwordOptions);
let ssoCodeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions);
const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, 'sha256');
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
const webUrl = 'https://localhost:8080';
const clientId = 'browser';
const ssoRedirectUri = 'https://localhost:8080/sso-connector.html';
// Launch browser
this.platformUtilsService.launchUri(webUrl + '/#/sso?clientId=' + clientId +
'&redirectUri=' + encodeURIComponent(ssoRedirectUri) +
'&state=' + state + '&codeChallenge=' + codeChallenge +
'&codeVerifier=' + ssoCodeVerifier);
}
}

View File

@ -21,6 +21,7 @@ import { AuthService as AuthServiceAbstraction } from 'jslib/abstractions/auth.s
import { CipherService } from 'jslib/abstractions/cipher.service'; import { CipherService } from 'jslib/abstractions/cipher.service';
import { CollectionService } from 'jslib/abstractions/collection.service'; import { CollectionService } from 'jslib/abstractions/collection.service';
import { CryptoService } from 'jslib/abstractions/crypto.service'; import { CryptoService } from 'jslib/abstractions/crypto.service';
import { CryptoFunctionService } from 'jslib/abstractions/cryptoFunction.service'
import { EnvironmentService } from 'jslib/abstractions/environment.service'; import { EnvironmentService } from 'jslib/abstractions/environment.service';
import { EventService } from 'jslib/abstractions/event.service'; import { EventService } from 'jslib/abstractions/event.service';
import { ExportService } from 'jslib/abstractions/export.service'; import { ExportService } from 'jslib/abstractions/export.service';
@ -40,6 +41,7 @@ import { TokenService } from 'jslib/abstractions/token.service';
import { TotpService } from 'jslib/abstractions/totp.service'; import { TotpService } from 'jslib/abstractions/totp.service';
import { UserService } from 'jslib/abstractions/user.service'; import { UserService } from 'jslib/abstractions/user.service';
import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service'; import { VaultTimeoutService } from 'jslib/abstractions/vaultTimeout.service';
import { WebCryptoFunctionService } from 'jslib/services/webCryptoFunction.service';
import { AutofillService } from '../../services/abstractions/autofill.service'; import { AutofillService } from '../../services/abstractions/autofill.service';
import BrowserMessagingService from '../../services/browserMessaging.service'; import BrowserMessagingService from '../../services/browserMessaging.service';
@ -67,9 +69,11 @@ export const authService = new AuthService(getBgService<CryptoService>('cryptoSe
getBgService<ApiService>('apiService')(), getBgService<UserService>('userService')(), getBgService<ApiService>('apiService')(), getBgService<UserService>('userService')(),
getBgService<TokenService>('tokenService')(), getBgService<AppIdService>('appIdService')(), getBgService<TokenService>('tokenService')(), getBgService<AppIdService>('appIdService')(),
getBgService<I18nService>('i18nService')(), getBgService<PlatformUtilsService>('platformUtilsService')(), getBgService<I18nService>('i18nService')(), getBgService<PlatformUtilsService>('platformUtilsService')(),
messagingService, getBgService<VaultTimeoutService>('vaultTimeoutService')()); messagingService, getBgService<VaultTimeoutService>('vaultTimeoutService')(), null);
export const searchService = new PopupSearchService(getBgService<SearchService>('searchService')(), export const searchService = new PopupSearchService(getBgService<SearchService>('searchService')(),
getBgService<CipherService>('cipherService')(), getBgService<PlatformUtilsService>('platformUtilsService')()); getBgService<CipherService>('cipherService')(), getBgService<PlatformUtilsService>('platformUtilsService')());
export const cryptoFunctionService: CryptoFunctionService = new WebCryptoFunctionService(window,
getBgService<PlatformUtilsService>('platformUtilsService')());
export function initFactory(i18nService: I18nService, storageService: StorageService, export function initFactory(i18nService: I18nService, storageService: StorageService,
popupUtilsService: PopupUtilsService): Function { popupUtilsService: PopupUtilsService): Function {
@ -121,6 +125,7 @@ export function initFactory(i18nService: I18nService, storageService: StorageSer
{ provide: AuthServiceAbstraction, useValue: authService }, { provide: AuthServiceAbstraction, useValue: authService },
{ provide: StateServiceAbstraction, useValue: stateService }, { provide: StateServiceAbstraction, useValue: stateService },
{ provide: SearchServiceAbstraction, useValue: searchService }, { provide: SearchServiceAbstraction, useValue: searchService },
{ provide: CryptoFunctionService, useValue: cryptoFunctionService },
{ provide: AuditService, useFactory: getBgService<AuditService>('auditService'), deps: [] }, { provide: AuditService, useFactory: getBgService<AuditService>('auditService'), deps: [] },
{ provide: CipherService, useFactory: getBgService<CipherService>('cipherService'), deps: [] }, { provide: CipherService, useFactory: getBgService<CipherService>('cipherService'), deps: [] },
{ provide: FolderService, useFactory: getBgService<FolderService>('folderService'), deps: [] }, { provide: FolderService, useFactory: getBgService<FolderService>('folderService'), deps: [] },

View File

@ -53,6 +53,10 @@
<key>Script</key> <key>Script</key>
<string>app/content/shortcuts.js</string> <string>app/content/shortcuts.js</string>
</dict> </dict>
<dict>
<key>Script</key>
<string>app/content/sso.js</string>
</dict>
</array> </array>
<key>SFSafariToolbarItem</key> <key>SFSafariToolbarItem</key>
<dict> <dict>

View File

@ -295,7 +295,7 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
return Promise.resolve(false); return Promise.resolve(false);
} }
private sidebarViewName(): string { sidebarViewName(): string {
if ((window as any).chrome.sidebarAction && this.isFirefox()) { if ((window as any).chrome.sidebarAction && this.isFirefox()) {
return 'sidebar'; return 'sidebar';
} else if (this.isOpera() && (typeof opr !== 'undefined') && opr.sidebarAction) { } else if (this.isOpera() && (typeof opr !== 'undefined') && opr.sidebarAction) {

View File

@ -136,6 +136,7 @@ const config = {
'content/autofiller': './src/content/autofiller.ts', 'content/autofiller': './src/content/autofiller.ts',
'content/notificationBar': './src/content/notificationBar.ts', 'content/notificationBar': './src/content/notificationBar.ts',
'content/shortcuts': './src/content/shortcuts.ts', 'content/shortcuts': './src/content/shortcuts.ts',
'content/sso': './src/content/sso.ts',
'notification/bar': './src/notification/bar.js', 'notification/bar': './src/notification/bar.js',
}, },
optimization: { optimization: {