mirror of
https://github.com/bitwarden/directory-connector
synced 2025-12-11 05:43:26 +00:00
sso login from bwdc desktop app (#58)
This commit is contained in:
2
jslib
2
jslib
Submodule jslib updated: 14b01f2e5d...4fec611314
@@ -26,6 +26,9 @@
|
|||||||
<button type="button" class="btn btn-link" (click)="settings()">
|
<button type="button" class="btn btn-link" (click)="settings()">
|
||||||
{{'settings' | i18n}}
|
{{'settings' | i18n}}
|
||||||
</button>
|
</button>
|
||||||
|
<a routerLink="/sso" class="btn btn-outline-secondary btn-block mt-2">
|
||||||
|
<i class="fa fa-bank" aria-hidden="true"></i> Enterprise Single Sign-On
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
32
src/app/accounts/sso.component.html
Normal file
32
src/app/accounts/sso.component.html
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<div class="container-fluid">
|
||||||
|
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8 col-lg-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body" *ngIf="loggingIn">
|
||||||
|
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||||
|
Logging in, please wait...
|
||||||
|
</div>
|
||||||
|
<div class="card-body" *ngIf="!loggingIn">
|
||||||
|
<p>
|
||||||
|
Quickly log in using your organization's single sign-on portal. Please enter your
|
||||||
|
organization's identifier to begin.</p>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="identifier">Organization Identifier</label>
|
||||||
|
<input id="identifier" class="form-control" type="text" name="Identifier"
|
||||||
|
[(ngModel)]="identifier" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary" [disabled]="form.loading">
|
||||||
|
<i class="fa fa-spinner fa-fw fa-spin" [hidden]="!form.loading"></i>
|
||||||
|
<i class="fa fa-sign-in fa-fw" [hidden]="form.loading"></i>
|
||||||
|
{{'logIn' | i18n}}
|
||||||
|
</button>
|
||||||
|
<a routerLink="/" class="btn btn-link">
|
||||||
|
{{'cancel' | i18n}}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
41
src/app/accounts/sso.component.ts
Normal file
41
src/app/accounts/sso.component.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ActivatedRoute,
|
||||||
|
Router,
|
||||||
|
} from '@angular/router';
|
||||||
|
|
||||||
|
import { ApiService } from 'jslib/abstractions/api.service';
|
||||||
|
import { AuthService } from 'jslib/abstractions/auth.service';
|
||||||
|
import { CryptoFunctionService } from 'jslib/abstractions/cryptoFunction.service';
|
||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
|
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
||||||
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
|
import { StateService } from 'jslib/abstractions/state.service';
|
||||||
|
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||||
|
|
||||||
|
import { SsoComponent as BaseSsoComponent } from 'jslib/angular/components/sso.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-sso',
|
||||||
|
templateUrl: 'sso.component.html',
|
||||||
|
})
|
||||||
|
export class SsoComponent extends BaseSsoComponent {
|
||||||
|
constructor(authService: AuthService, router: Router,
|
||||||
|
i18nService: I18nService, route: ActivatedRoute,
|
||||||
|
storageService: StorageService, stateService: StateService,
|
||||||
|
platformUtilsService: PlatformUtilsService, apiService: ApiService,
|
||||||
|
cryptoFunctionService: CryptoFunctionService,
|
||||||
|
passwordGenerationService: PasswordGenerationService) {
|
||||||
|
super(authService, router, i18nService, route, storageService, stateService, platformUtilsService,
|
||||||
|
apiService, cryptoFunctionService, passwordGenerationService);
|
||||||
|
this.successRoute = '/tabs/dashboard';
|
||||||
|
this.redirectUri = 'bwdc://sso-callback';
|
||||||
|
this.clientId = 'connector';
|
||||||
|
}
|
||||||
|
|
||||||
|
async submit() {
|
||||||
|
await super.submit();
|
||||||
|
this.router.navigate(['login']);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import { AuthGuardService } from './services/auth-guard.service';
|
|||||||
import { LaunchGuardService } from './services/launch-guard.service';
|
import { LaunchGuardService } from './services/launch-guard.service';
|
||||||
|
|
||||||
import { LoginComponent } from './accounts/login.component';
|
import { LoginComponent } from './accounts/login.component';
|
||||||
|
import { SsoComponent } from './accounts/sso.component';
|
||||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||||
import { DashboardComponent } from './tabs/dashboard.component';
|
import { DashboardComponent } from './tabs/dashboard.component';
|
||||||
import { MoreComponent } from './tabs/more.component';
|
import { MoreComponent } from './tabs/more.component';
|
||||||
@@ -22,6 +23,7 @@ const routes: Routes = [
|
|||||||
canActivate: [LaunchGuardService],
|
canActivate: [LaunchGuardService],
|
||||||
},
|
},
|
||||||
{ path: '2fa', component: TwoFactorComponent },
|
{ path: '2fa', component: TwoFactorComponent },
|
||||||
|
{ path: 'sso', component: SsoComponent },
|
||||||
{
|
{
|
||||||
path: 'tabs',
|
path: 'tabs',
|
||||||
component: TabsComponent,
|
component: TabsComponent,
|
||||||
|
|||||||
@@ -133,6 +133,9 @@ export class AppComponent implements OnInit {
|
|||||||
properties: { label: message.label },
|
properties: { label: message.label },
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case 'ssoCallback':
|
||||||
|
this.router.navigate(['sso'], { queryParams: { code: message.code, state: message.state } });
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { ModalComponent } from 'jslib/angular/components/modal.component';
|
|||||||
|
|
||||||
import { EnvironmentComponent } from './accounts/environment.component';
|
import { EnvironmentComponent } from './accounts/environment.component';
|
||||||
import { LoginComponent } from './accounts/login.component';
|
import { LoginComponent } from './accounts/login.component';
|
||||||
|
import { SsoComponent } from './accounts/sso.component';
|
||||||
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
|
||||||
import { TwoFactorComponent } from './accounts/two-factor.component';
|
import { TwoFactorComponent } from './accounts/two-factor.component';
|
||||||
import { DashboardComponent } from './tabs/dashboard.component';
|
import { DashboardComponent } from './tabs/dashboard.component';
|
||||||
@@ -72,6 +73,7 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
|
|||||||
MoreComponent,
|
MoreComponent,
|
||||||
SearchCiphersPipe,
|
SearchCiphersPipe,
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
|
SsoComponent,
|
||||||
StopClickDirective,
|
StopClickDirective,
|
||||||
StopPropDirective,
|
StopPropDirective,
|
||||||
TabsComponent,
|
TabsComponent,
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const stateService = new StateService();
|
|||||||
const broadcasterService = new BroadcasterService();
|
const broadcasterService = new BroadcasterService();
|
||||||
const messagingService = new ElectronRendererMessagingService(broadcasterService);
|
const messagingService = new ElectronRendererMessagingService(broadcasterService);
|
||||||
const storageService: StorageServiceAbstraction = new ElectronStorageService(remote.app.getPath('userData'));
|
const storageService: StorageServiceAbstraction = new ElectronStorageService(remote.app.getPath('userData'));
|
||||||
const platformUtilsService = new ElectronPlatformUtilsService(i18nService, messagingService, true, storageService);
|
const platformUtilsService = new ElectronPlatformUtilsService(i18nService, messagingService, false, storageService);
|
||||||
const secureStorageService: StorageServiceAbstraction = new ElectronRendererSecureStorageService();
|
const secureStorageService: StorageServiceAbstraction = new ElectronRendererSecureStorageService();
|
||||||
const cryptoFunctionService: CryptoFunctionServiceAbstraction = new NodeCryptoFunctionService();
|
const cryptoFunctionService: CryptoFunctionServiceAbstraction = new NodeCryptoFunctionService();
|
||||||
const cryptoService = new CryptoService(storageService, secureStorageService, cryptoFunctionService);
|
const cryptoService = new CryptoService(storageService, secureStorageService, cryptoFunctionService);
|
||||||
@@ -140,6 +140,7 @@ export function initFactory(): Function {
|
|||||||
{ provide: ConfigurationService, useValue: configurationService },
|
{ provide: ConfigurationService, useValue: configurationService },
|
||||||
{ provide: SyncService, useValue: syncService },
|
{ provide: SyncService, useValue: syncService },
|
||||||
{ provide: PasswordGenerationServiceAbstraction, useValue: passwordGenerationService },
|
{ provide: PasswordGenerationServiceAbstraction, useValue: passwordGenerationService },
|
||||||
|
{ provide: CryptoFunctionServiceAbstraction, useValue: cryptoFunctionService },
|
||||||
{
|
{
|
||||||
provide: APP_INITIALIZER,
|
provide: APP_INITIALIZER,
|
||||||
useFactory: initFactory,
|
useFactory: initFactory,
|
||||||
|
|||||||
24
src/main.ts
24
src/main.ts
@@ -52,7 +52,8 @@ export class Main {
|
|||||||
this.i18nService = new I18nService('en', './locales/');
|
this.i18nService = new I18nService('en', './locales/');
|
||||||
this.storageService = new ElectronStorageService(app.getPath('userData'));
|
this.storageService = new ElectronStorageService(app.getPath('userData'));
|
||||||
|
|
||||||
this.windowMain = new WindowMain(this.storageService, false, 800, 600);
|
this.windowMain = new WindowMain(this.storageService, false, 800, 600,
|
||||||
|
(arg) => this.processDeepLink(arg));
|
||||||
this.menuMain = new MenuMain(this);
|
this.menuMain = new MenuMain(this);
|
||||||
this.updaterMain = new UpdaterMain(this.i18nService, this.windowMain, 'directory-connector', () => {
|
this.updaterMain = new UpdaterMain(this.i18nService, this.windowMain, 'directory-connector', () => {
|
||||||
this.messagingService.send('checkingForUpdate');
|
this.messagingService.send('checkingForUpdate');
|
||||||
@@ -78,11 +79,32 @@ export class Main {
|
|||||||
this.messagingMain.init();
|
this.messagingMain.init();
|
||||||
await this.updaterMain.init();
|
await this.updaterMain.init();
|
||||||
await this.trayMain.init(this.i18nService.t('bitwardenDirectoryConnector'));
|
await this.trayMain.init(this.i18nService.t('bitwardenDirectoryConnector'));
|
||||||
|
|
||||||
|
if (!app.isDefaultProtocolClient('bwdc')) {
|
||||||
|
app.setAsDefaultProtocolClient('bwdc');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process protocol for macOS
|
||||||
|
app.on('open-url', (event, url) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.processDeepLink([url]);
|
||||||
|
});
|
||||||
}, (e: any) => {
|
}, (e: any) => {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
console.error(e);
|
console.error(e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private processDeepLink(argv: string[]): void {
|
||||||
|
argv.filter((s) => s.indexOf('bwdc://') === 0).forEach((s) => {
|
||||||
|
const url = new URL(s);
|
||||||
|
const code = url.searchParams.get('code');
|
||||||
|
const receivedState = url.searchParams.get('state');
|
||||||
|
if (code != null && receivedState != null) {
|
||||||
|
this.messagingService.send('ssoCallback', { code: code, state: receivedState });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const main = new Main();
|
const main = new Main();
|
||||||
|
|||||||
Reference in New Issue
Block a user