Submodule jslib contains modified content diff --git a/jslib/angular/src/components/register.component.ts b/jslib/angular/src/components/register.component.ts index 53ec3c8..7b49db1 100644 --- a/jslib/angular/src/components/register.component.ts +++ b/jslib/angular/src/components/register.component.ts @@ -24,7 +24,7 @@ export class RegisterComponent { formPromise: Promise; masterPasswordScore: number; referenceData: ReferenceEventRequest; - showTerms = true; + showTerms = false; acceptPolicies: boolean = false; protected successRoute = 'login'; @@ -35,7 +35,7 @@ export class RegisterComponent { protected apiService: ApiService, protected stateService: StateService, protected platformUtilsService: PlatformUtilsService, protected passwordGenerationService: PasswordGenerationService) { - this.showTerms = !platformUtilsService.isSelfHost(); + this.showTerms = false; } get masterPasswordScoreWidth() { @@ -69,6 +69,12 @@ export class RegisterComponent { } async submit() { + if (typeof crypto.subtle === 'undefined') { + this.platformUtilsService.showToast('error', "This browser requires HTTPS to use the web vault", + "Check the Vaultwarden wiki for details on how to enable it"); + return; + } + if (!this.acceptPolicies && this.showTerms) { this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'), this.i18nService.t('acceptPoliciesError')); diff --git a/jslib/angular/src/components/sso.component.ts b/jslib/angular/src/components/sso.component.ts index d4512a1..ad57f69 100644 --- a/jslib/angular/src/components/sso.component.ts +++ b/jslib/angular/src/components/sso.component.ts @@ -19,6 +19,8 @@ import { Utils } from 'jslib-common/misc/utils'; import { AuthResult } from 'jslib-common/models/domain/authResult'; +import { switchMap } from 'rxjs/operators'; + @Directive() export class SsoComponent { identifier: string; @@ -48,13 +50,19 @@ export class SsoComponent { async ngOnInit() { const queryParamsSub = this.route.queryParams.subscribe(async qParams => { - if (qParams.code != null && qParams.state != null) { + // I have no idea why the qParams is empty here - I've hacked in an alternative very messily, but it works. + const workingParams = (new URL(window.location.href)).searchParams; + const workingSwap = { + code: workingParams.get('code'), + state: workingParams.get('state'), + }; + if (workingSwap.code != null && workingSwap.state != null) { const codeVerifier = await this.storageService.get(ConstantsService.ssoCodeVerifierKey); const state = await this.storageService.get(ConstantsService.ssoStateKey); await this.storageService.remove(ConstantsService.ssoCodeVerifierKey); await this.storageService.remove(ConstantsService.ssoStateKey); - if (qParams.code != null && codeVerifier != null && state != null && this.checkState(state, qParams.state)) { - await this.logIn(qParams.code, codeVerifier, this.getOrgIdentiferFromState(qParams.state)); + if (workingSwap.code != null && codeVerifier != null && state != null && this.checkState(state, workingSwap.state)) { + await this.logIn(workingSwap.code, codeVerifier, this.getOrgIdentiferFromState(workingSwap.state)); } } else if (qParams.clientId != null && qParams.redirectUri != null && qParams.state != null && qParams.codeChallenge != null) { @@ -122,7 +130,7 @@ export class SsoComponent { let authorizeUrl = this.apiService.identityBaseUrl + '/connect/authorize?' + 'client_id=' + this.clientId + '&redirect_uri=' + encodeURIComponent(this.redirectUri) + '&' + 'response_type=code&scope=api offline_access&' + - 'state=' + state + '&code_challenge=' + codeChallenge + '&' + + 'state=' + encodeURIComponent(state) + '&code_challenge=' + codeChallenge + '&' + 'code_challenge_method=S256&response_mode=query&' + 'domain_hint=' + encodeURIComponent(this.identifier); @@ -137,7 +145,7 @@ export class SsoComponent { private async logIn(code: string, codeVerifier: string, orgIdFromState: string) { this.loggingIn = true; try { - this.formPromise = this.authService.logInSso(code, codeVerifier, this.redirectUri); + this.formPromise = this.authService.logInSso(code, codeVerifier, this.redirectUri, orgIdFromState); const response = await this.formPromise; if (response.twoFactor) { if (this.onSuccessfulLoginTwoFactorNavigate != null) { diff --git a/jslib/common/src/abstractions/api.service.ts b/jslib/common/src/abstractions/api.service.ts index 67131df..ce498ff 100644 --- a/jslib/common/src/abstractions/api.service.ts +++ b/jslib/common/src/abstractions/api.service.ts @@ -33,6 +33,7 @@ import { KeysRequest } from '../models/request/keysRequest'; import { OrganizationCreateRequest } from '../models/request/organizationCreateRequest'; import { OrganizationImportRequest } from '../models/request/organizationImportRequest'; import { OrganizationKeysRequest } from '../models/request/organizationKeysRequest'; +import { OrganizationSsoUpdateRequest } from '../models/request/organizationSsoUpdateRequest'; import { OrganizationTaxInfoUpdateRequest } from '../models/request/organizationTaxInfoUpdateRequest'; import { OrganizationUpdateRequest } from '../models/request/organizationUpdateRequest'; import { OrganizationUpgradeRequest } from '../models/request/organizationUpgradeRequest'; @@ -122,6 +123,7 @@ import { SendAccessResponse } from '../models/response/sendAccessResponse'; import { SendFileDownloadDataResponse } from '../models/response/sendFileDownloadDataResponse'; import { SendFileUploadDataResponse } from '../models/response/sendFileUploadDataResponse'; import { SendResponse } from '../models/response/sendResponse'; +import { SsoConfigResponse } from '../models/response/ssoConfigResponse'; import { SubscriptionResponse } from '../models/response/subscriptionResponse'; import { SyncResponse } from '../models/response/syncResponse'; import { TaxInfoResponse } from '../models/response/taxInfoResponse'; @@ -360,6 +362,8 @@ export abstract class ApiService { getOrganizationTaxInfo: (id: string) => Promise; postOrganization: (request: OrganizationCreateRequest) => Promise; putOrganization: (id: string, request: OrganizationUpdateRequest) => Promise; + getSsoConfig: (id: string) => Promise; + putOrganizationSso: (id: string, request: OrganizationSsoUpdateRequest) => Promise; putOrganizationTaxInfo: (id: string, request: OrganizationTaxInfoUpdateRequest) => Promise; postLeaveOrganization: (id: string) => Promise; postOrganizationLicense: (data: FormData) => Promise; diff --git a/jslib/common/src/abstractions/auth.service.ts b/jslib/common/src/abstractions/auth.service.ts index ac7ef04..5b1b774 100644 --- a/jslib/common/src/abstractions/auth.service.ts +++ b/jslib/common/src/abstractions/auth.service.ts @@ -15,7 +15,7 @@ export abstract class AuthService { selectedTwoFactorProviderType: TwoFactorProviderType; logIn: (email: string, masterPassword: string) => Promise; - logInSso: (code: string, codeVerifier: string, redirectUrl: string) => Promise; + logInSso: (code: string, codeVerifier: string, redirectUrl: string, orgIdentifier: string) => Promise; logInApiKey: (clientId: string, clientSecret: string) => Promise; logInTwoFactor: (twoFactorProvider: TwoFactorProviderType, twoFactorToken: string, remember?: boolean) => Promise; diff --git a/jslib/common/src/models/request/organizationSsoUpdateRequest.ts b/jslib/common/src/models/request/organizationSsoUpdateRequest.ts new file mode 100644 index 0000000..7075aec --- /dev/null +++ b/jslib/common/src/models/request/organizationSsoUpdateRequest.ts @@ -0,0 +1,8 @@ +export class OrganizationSsoUpdateRequest { + useSso: boolean; + callbackPath: string; + signedOutCallbackPath: string; + authority: string; + clientId: string; + clientSecret: string; +} diff --git a/jslib/common/src/models/request/tokenRequest.ts b/jslib/common/src/models/request/tokenRequest.ts index 7578012..964364f 100644 --- a/jslib/common/src/models/request/tokenRequest.ts +++ b/jslib/common/src/models/request/tokenRequest.ts @@ -14,9 +14,10 @@ export class TokenRequest { provider: TwoFactorProviderType; remember: boolean; device?: DeviceRequest; + orgIdentifier?: string; constructor(credentials: string[], codes: string[], clientIdClientSecret: string[], provider: TwoFactorProviderType, - token: string, remember: boolean, device?: DeviceRequest) { + token: string, remember: boolean, device?: DeviceRequest, orgIdentifier?: string) { if (credentials != null && credentials.length > 1) { this.email = credentials[0]; this.masterPasswordHash = credentials[1]; @@ -28,6 +29,9 @@ export class TokenRequest { this.clientId = clientIdClientSecret[0]; this.clientSecret = clientIdClientSecret[1]; } + if (orgIdentifier && orgIdentifier !== '') { + this.orgIdentifier = orgIdentifier; + } this.token = token; this.provider = provider; this.remember = remember; @@ -53,6 +57,7 @@ export class TokenRequest { obj.code = this.code; obj.code_verifier = this.codeVerifier; obj.redirect_uri = this.redirectUri; + obj.org_identifier = this.orgIdentifier; } else { throw new Error('must provide credentials or codes'); } diff --git a/jslib/common/src/services/api.service.ts b/jslib/common/src/services/api.service.ts index 51c1c14..b615672 100644 --- a/jslib/common/src/services/api.service.ts +++ b/jslib/common/src/services/api.service.ts @@ -37,6 +37,7 @@ import { KeysRequest } from '../models/request/keysRequest'; import { OrganizationCreateRequest } from '../models/request/organizationCreateRequest'; import { OrganizationImportRequest } from '../models/request/organizationImportRequest'; import { OrganizationKeysRequest } from '../models/request/organizationKeysRequest'; +import { OrganizationSsoUpdateRequest } from '../models/request/organizationSsoUpdateRequest'; import { OrganizationTaxInfoUpdateRequest } from '../models/request/organizationTaxInfoUpdateRequest'; import { OrganizationUpdateRequest } from '../models/request/organizationUpdateRequest'; import { OrganizationUpgradeRequest } from '../models/request/organizationUpgradeRequest'; @@ -128,6 +129,7 @@ import { SendAccessResponse } from '../models/response/sendAccessResponse'; import { SendFileDownloadDataResponse } from '../models/response/sendFileDownloadDataResponse'; import { SendFileUploadDataResponse } from '../models/response/sendFileUploadDataResponse'; import { SendResponse } from '../models/response/sendResponse'; +import { SsoConfigResponse } from '../models/response/ssoConfigResponse'; import { SubscriptionResponse } from '../models/response/subscriptionResponse'; import { SyncResponse } from '../models/response/syncResponse'; import { TaxInfoResponse } from '../models/response/taxInfoResponse'; @@ -1158,6 +1160,16 @@ export class ApiService implements ApiServiceAbstraction { return new OrganizationResponse(r); } + async getSsoConfig(id: string): Promise { + const r = await this.send('GET', '/organizations/' + id + '/sso', null, true, true); + return new SsoConfigResponse(r); + } + + async putOrganizationSso(id: string, request: OrganizationSsoUpdateRequest): Promise { + const r = await this.send('PUT', '/organizations/' + id + '/sso', request, true, false); + return new SsoConfigResponse(r); + } + async putOrganizationTaxInfo(id: string, request: OrganizationTaxInfoUpdateRequest): Promise { return this.send('PUT', '/organizations/' + id + '/tax', request, true, false); } diff --git a/jslib/common/src/services/auth.service.ts b/jslib/common/src/services/auth.service.ts index 6536a94..6f4899c 100644 --- a/jslib/common/src/services/auth.service.ts +++ b/jslib/common/src/services/auth.service.ts @@ -130,10 +130,10 @@ export class AuthService implements AuthServiceAbstraction { key, null, null, null); } - async logInSso(code: string, codeVerifier: string, redirectUrl: string): Promise { + async logInSso(code: string, codeVerifier: string, redirectUrl: string, orgIdentifier: string): Promise { this.selectedTwoFactorProviderType = null; return await this.logInHelper(null, null, null, code, codeVerifier, redirectUrl, null, null, - null, null, null, null); + null, null, null, null, orgIdentifier); } async logInApiKey(clientId: string, clientSecret: string): Promise { @@ -272,7 +272,7 @@ export class AuthService implements AuthServiceAbstraction { private async logInHelper(email: string, hashedPassword: string, localHashedPassword: string, code: string, codeVerifier: string, redirectUrl: string, clientId: string, clientSecret: string, key: SymmetricCryptoKey, - twoFactorProvider?: TwoFactorProviderType, twoFactorToken?: string, remember?: boolean): Promise { + twoFactorProvider?: TwoFactorProviderType, twoFactorToken?: string, remember?: boolean, orgIdentifier?: string): Promise { const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(email); const appId = await this.appIdService.getAppId(); const deviceRequest = new DeviceRequest(appId, this.platformUtilsService); @@ -300,13 +300,13 @@ export class AuthService implements AuthServiceAbstraction { let request: TokenRequest; if (twoFactorToken != null && twoFactorProvider != null) { request = new TokenRequest(emailPassword, codeCodeVerifier, clientIdClientSecret, twoFactorProvider, - twoFactorToken, remember, deviceRequest); + twoFactorToken, remember, deviceRequest, orgIdentifier); } else if (storedTwoFactorToken != null) { request = new TokenRequest(emailPassword, codeCodeVerifier, clientIdClientSecret, TwoFactorProviderType.Remember, - storedTwoFactorToken, false, deviceRequest); + storedTwoFactorToken, false, deviceRequest, orgIdentifier); } else { request = new TokenRequest(emailPassword, codeCodeVerifier, clientIdClientSecret, null, - null, false, deviceRequest); + null, false, deviceRequest, orgIdentifier); } const response = await this.apiService.postIdentityToken(request); diff --git a/src/404.html b/src/404.html index eba36375..cb8883ec 100644 --- a/src/404.html +++ b/src/404.html @@ -41,10 +41,10 @@

You can return to the web vault, check our status page - or contact us.

+ or contact us.

diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index bee8416f..dad32467 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -33,6 +33,7 @@ import { AccountComponent as OrgAccountComponent } from './organizations/setting import { OrganizationBillingComponent } from './organizations/settings/organization-billing.component'; import { OrganizationSubscriptionComponent } from './organizations/settings/organization-subscription.component'; import { SettingsComponent as OrgSettingsComponent } from './organizations/settings/settings.component'; +import { SsoComponent as OrgSsoComponent } from './organizations/settings/sso.component'; import { TwoFactorSetupComponent as OrgTwoFactorSetupComponent, } from './organizations/settings/two-factor-setup.component'; @@ -412,6 +413,7 @@ const routes: Routes = [ children: [ { path: '', pathMatch: 'full', redirectTo: 'account' }, { path: 'account', component: OrgAccountComponent, data: { titleId: 'myOrganization' } }, + { path: 'sso', component: OrgSsoComponent, data: { titleId: 'sso' } }, { path: 'two-factor', component: OrgTwoFactorSetupComponent, data: { titleId: 'twoStepLogin' } }, { path: 'billing', diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 2922cf09..8f2be1ad 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -146,6 +146,10 @@ export class AppComponent implements OnDestroy, OnInit { } break; case 'showToast': + if (typeof message.text === "string" && typeof crypto.subtle === 'undefined') { + message.title="This browser requires HTTPS to use the web vault"; + message.text="Check the Vaultwarden wiki for details on how to enable it"; + } this.showToast(message); break; case 'setFullWidth': diff --git a/src/app/app.module.ts b/src/app/app.module.ts index bafc22a0..938db7ee 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -67,6 +67,7 @@ import { DownloadLicenseComponent } from './organizations/settings/download-lice import { OrganizationBillingComponent } from './organizations/settings/organization-billing.component'; import { OrganizationSubscriptionComponent } from './organizations/settings/organization-subscription.component'; import { SettingsComponent as OrgSettingComponent } from './organizations/settings/settings.component'; +import { SsoComponent as OrgSsoComponent } from './organizations/settings/sso.component'; import { TwoFactorSetupComponent as OrgTwoFactorSetupComponent, } from './organizations/settings/two-factor-setup.component'; @@ -347,6 +348,7 @@ registerLocaleData(localeZhTw, 'zh-TW'); NavbarComponent, OptionsComponent, OrgAccountComponent, + OrgSsoComponent, OrgAddEditComponent, OrganizationBillingComponent, OrganizationPlansComponent, diff --git a/src/app/layouts/footer.component.html b/src/app/layouts/footer.component.html index b001b9e3..c1bd2ac8 100644 --- a/src/app/layouts/footer.component.html +++ b/src/app/layouts/footer.component.html @@ -1,7 +1,7 @@