1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

Merge branch 'master' into feature/org-admin-refresh

This commit is contained in:
Shane Melton
2022-11-17 15:02:50 -08:00
34 changed files with 353 additions and 86 deletions

View File

@@ -281,12 +281,12 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
return (
this.authService.authingWithPassword() ||
this.authService.authingWithSso() ||
this.authService.authingWithApiKey() ||
this.authService.authingWithUserApiKey() ||
this.authService.authingWithPasswordless()
);
}
get needsLock(): boolean {
return this.authService.authingWithSso() || this.authService.authingWithApiKey();
return this.authService.authingWithSso() || this.authService.authingWithUserApiKey();
}
}

View File

@@ -12,13 +12,13 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { TokenService } from "@bitwarden/common/abstractions/token.service";
import { TwoFactorService } from "@bitwarden/common/abstractions/twoFactor.service";
import { ApiLogInStrategy } from "@bitwarden/common/misc/logInStrategies/apiLogin.strategy";
import { UserApiLogInStrategy } from "@bitwarden/common/misc/logInStrategies/user-api-login.strategy";
import { Utils } from "@bitwarden/common/misc/utils";
import { ApiLogInCredentials } from "@bitwarden/common/models/domain/log-in-credentials";
import { UserApiLogInCredentials } from "@bitwarden/common/models/domain/log-in-credentials";
import { identityTokenResponseFactory } from "./logIn.strategy.spec";
describe("ApiLogInStrategy", () => {
describe("UserApiLogInStrategy", () => {
let cryptoService: SubstituteOf<CryptoService>;
let apiService: SubstituteOf<ApiService>;
let tokenService: SubstituteOf<TokenService>;
@@ -31,8 +31,8 @@ describe("ApiLogInStrategy", () => {
let stateService: SubstituteOf<StateService>;
let twoFactorService: SubstituteOf<TwoFactorService>;
let apiLogInStrategy: ApiLogInStrategy;
let credentials: ApiLogInCredentials;
let apiLogInStrategy: UserApiLogInStrategy;
let credentials: UserApiLogInCredentials;
const deviceId = Utils.newGuid();
const keyConnectorUrl = "KEY_CONNECTOR_URL";
@@ -55,7 +55,7 @@ describe("ApiLogInStrategy", () => {
appIdService.getAppId().resolves(deviceId);
tokenService.getTwoFactorToken().resolves(null);
apiLogInStrategy = new ApiLogInStrategy(
apiLogInStrategy = new UserApiLogInStrategy(
cryptoService,
apiService,
tokenService,
@@ -69,7 +69,7 @@ describe("ApiLogInStrategy", () => {
keyConnectorService
);
credentials = new ApiLogInCredentials(apiClientId, apiClientSecret);
credentials = new UserApiLogInCredentials(apiClientId, apiClientSecret);
});
it("sends api key credentials to the server", async () => {

View File

@@ -24,9 +24,9 @@ import { EmergencyAccessUpdateRequest } from "../models/request/emergency-access
import { EventRequest } from "../models/request/event.request";
import { GroupRequest } from "../models/request/group.request";
import { IapCheckRequest } from "../models/request/iap-check.request";
import { ApiTokenRequest } from "../models/request/identity-token/api-token.request";
import { PasswordTokenRequest } from "../models/request/identity-token/password-token.request";
import { SsoTokenRequest } from "../models/request/identity-token/sso-token.request";
import { UserApiTokenRequest } from "../models/request/identity-token/user-api-token.request";
import { ImportCiphersRequest } from "../models/request/import-ciphers.request";
import { ImportOrganizationCiphersRequest } from "../models/request/import-organization-ciphers.request";
import { KdfRequest } from "../models/request/kdf.request";
@@ -175,7 +175,7 @@ export abstract class ApiService {
) => Promise<any>;
postIdentityToken: (
request: PasswordTokenRequest | SsoTokenRequest | ApiTokenRequest
request: PasswordTokenRequest | SsoTokenRequest | UserApiTokenRequest
) => Promise<IdentityTokenResponse | IdentityTwoFactorResponse | IdentityCaptchaResponse>;
refreshIdentityToken: () => Promise<any>;

View File

@@ -3,7 +3,7 @@ import { Observable } from "rxjs";
import { AuthenticationStatus } from "../enums/authenticationStatus";
import { AuthResult } from "../models/domain/auth-result";
import {
ApiLogInCredentials,
UserApiLogInCredentials,
PasswordLogInCredentials,
SsoLogInCredentials,
PasswordlessLogInCredentials,
@@ -20,7 +20,7 @@ export abstract class AuthService {
logIn: (
credentials:
| ApiLogInCredentials
| UserApiLogInCredentials
| PasswordLogInCredentials
| SsoLogInCredentials
| PasswordlessLogInCredentials
@@ -31,7 +31,7 @@ export abstract class AuthService {
) => Promise<AuthResult>;
logOut: (callback: () => void) => void;
makePreloginKey: (masterPassword: string, email: string) => Promise<SymmetricCryptoKey>;
authingWithApiKey: () => boolean;
authingWithUserApiKey: () => boolean;
authingWithSso: () => boolean;
authingWithPassword: () => boolean;
authingWithPasswordless: () => boolean;

View File

@@ -12,6 +12,10 @@ export abstract class FolderService {
clearCache: () => Promise<void>;
encrypt: (model: FolderView, key?: SymmetricCryptoKey) => Promise<Folder>;
get: (id: string) => Promise<Folder>;
/**
* @deprecated Only use in CLI!
*/
getFromState: (id: string) => Promise<Folder>;
/**
* @deprecated Only use in CLI!
*/

View File

@@ -1,6 +1,6 @@
export enum AuthenticationType {
Password = 0,
Sso = 1,
Api = 2,
UserApi = 2,
Passwordless = 3,
}

View File

@@ -11,23 +11,23 @@ import { TwoFactorProviderType } from "../../enums/twoFactorProviderType";
import { Account, AccountProfile, AccountTokens } from "../../models/domain/account";
import { AuthResult } from "../../models/domain/auth-result";
import {
ApiLogInCredentials,
UserApiLogInCredentials,
PasswordLogInCredentials,
SsoLogInCredentials,
PasswordlessLogInCredentials,
} from "../../models/domain/log-in-credentials";
import { DeviceRequest } from "../../models/request/device.request";
import { ApiTokenRequest } from "../../models/request/identity-token/api-token.request";
import { PasswordTokenRequest } from "../../models/request/identity-token/password-token.request";
import { SsoTokenRequest } from "../../models/request/identity-token/sso-token.request";
import { TokenTwoFactorRequest } from "../../models/request/identity-token/token-two-factor.request";
import { UserApiTokenRequest } from "../../models/request/identity-token/user-api-token.request";
import { KeysRequest } from "../../models/request/keys.request";
import { IdentityCaptchaResponse } from "../../models/response/identity-captcha.response";
import { IdentityTokenResponse } from "../../models/response/identity-token.response";
import { IdentityTwoFactorResponse } from "../../models/response/identity-two-factor.response";
export abstract class LogInStrategy {
protected abstract tokenRequest: ApiTokenRequest | PasswordTokenRequest | SsoTokenRequest;
protected abstract tokenRequest: UserApiTokenRequest | PasswordTokenRequest | SsoTokenRequest;
protected captchaBypassToken: string = null;
constructor(
@@ -44,7 +44,7 @@ export abstract class LogInStrategy {
abstract logIn(
credentials:
| ApiLogInCredentials
| UserApiLogInCredentials
| PasswordLogInCredentials
| SsoLogInCredentials
| PasswordlessLogInCredentials

View File

@@ -9,14 +9,14 @@ import { PlatformUtilsService } from "../../abstractions/platformUtils.service";
import { StateService } from "../../abstractions/state.service";
import { TokenService } from "../../abstractions/token.service";
import { TwoFactorService } from "../../abstractions/twoFactor.service";
import { ApiLogInCredentials } from "../../models/domain/log-in-credentials";
import { ApiTokenRequest } from "../../models/request/identity-token/api-token.request";
import { UserApiLogInCredentials } from "../../models/domain/log-in-credentials";
import { UserApiTokenRequest } from "../../models/request/identity-token/user-api-token.request";
import { IdentityTokenResponse } from "../../models/response/identity-token.response";
import { LogInStrategy } from "./logIn.strategy";
export class ApiLogInStrategy extends LogInStrategy {
tokenRequest: ApiTokenRequest;
export class UserApiLogInStrategy extends LogInStrategy {
tokenRequest: UserApiTokenRequest;
constructor(
cryptoService: CryptoService,
@@ -51,8 +51,8 @@ export class ApiLogInStrategy extends LogInStrategy {
}
}
async logIn(credentials: ApiLogInCredentials) {
this.tokenRequest = new ApiTokenRequest(
async logIn(credentials: UserApiLogInCredentials) {
this.tokenRequest = new UserApiTokenRequest(
credentials.clientId,
credentials.clientSecret,
await this.buildTwoFactor(),

View File

@@ -26,8 +26,8 @@ export class SsoLogInCredentials {
) {}
}
export class ApiLogInCredentials {
readonly type = AuthenticationType.Api;
export class UserApiLogInCredentials {
readonly type = AuthenticationType.UserApi;
constructor(public clientId: string, public clientSecret: string) {}
}

View File

@@ -42,10 +42,12 @@ export abstract class TokenRequest {
obj.authRequest = this.passwordlessAuthRequest;
}
if (this.twoFactor.token && this.twoFactor.provider != null) {
obj.twoFactorToken = this.twoFactor.token;
obj.twoFactorProvider = this.twoFactor.provider;
obj.twoFactorRemember = this.twoFactor.remember ? "1" : "0";
if (this.twoFactor) {
if (this.twoFactor.token && this.twoFactor.provider != null) {
obj.twoFactorToken = this.twoFactor.token;
obj.twoFactorProvider = this.twoFactor.provider;
obj.twoFactorRemember = this.twoFactor.remember ? "1" : "0";
}
}
return obj;

View File

@@ -3,7 +3,7 @@ import { DeviceRequest } from "../device.request";
import { TokenTwoFactorRequest } from "./token-two-factor.request";
import { TokenRequest } from "./token.request";
export class ApiTokenRequest extends TokenRequest {
export class UserApiTokenRequest extends TokenRequest {
constructor(
public clientId: string,
public clientSecret: string,

View File

@@ -31,10 +31,10 @@ import { EmergencyAccessUpdateRequest } from "../models/request/emergency-access
import { EventRequest } from "../models/request/event.request";
import { GroupRequest } from "../models/request/group.request";
import { IapCheckRequest } from "../models/request/iap-check.request";
import { ApiTokenRequest } from "../models/request/identity-token/api-token.request";
import { PasswordTokenRequest } from "../models/request/identity-token/password-token.request";
import { SsoTokenRequest } from "../models/request/identity-token/sso-token.request";
import { TokenTwoFactorRequest } from "../models/request/identity-token/token-two-factor.request";
import { UserApiTokenRequest } from "../models/request/identity-token/user-api-token.request";
import { ImportCiphersRequest } from "../models/request/import-ciphers.request";
import { ImportOrganizationCiphersRequest } from "../models/request/import-organization-ciphers.request";
import { KdfRequest } from "../models/request/kdf.request";
@@ -206,7 +206,7 @@ export class ApiService implements ApiServiceAbstraction {
// Auth APIs
async postIdentityToken(
request: ApiTokenRequest | PasswordTokenRequest | SsoTokenRequest
request: UserApiTokenRequest | PasswordTokenRequest | SsoTokenRequest
): Promise<IdentityTokenResponse | IdentityTwoFactorResponse | IdentityCaptchaResponse> {
const headers = new Headers({
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
@@ -219,7 +219,7 @@ export class ApiService implements ApiServiceAbstraction {
request.alterIdentityTokenHeaders(headers);
const identityToken =
request instanceof ApiTokenRequest
request instanceof UserApiTokenRequest
? request.toIdentityToken()
: request.toIdentityToken(this.platformUtilsService.getClientType());
@@ -2271,8 +2271,7 @@ export class ApiService implements ApiServiceAbstraction {
const appId = await this.appIdService.getAppId();
const deviceRequest = new DeviceRequest(appId, this.platformUtilsService);
const tokenRequest = new ApiTokenRequest(
const tokenRequest = new UserApiTokenRequest(
clientId,
clientSecret,
new TokenTwoFactorRequest(),

View File

@@ -17,13 +17,13 @@ import { AuthenticationStatus } from "../enums/authenticationStatus";
import { AuthenticationType } from "../enums/authenticationType";
import { KdfType } from "../enums/kdfType";
import { KeySuffixOptions } from "../enums/keySuffixOptions";
import { ApiLogInStrategy } from "../misc/logInStrategies/apiLogin.strategy";
import { PasswordLogInStrategy } from "../misc/logInStrategies/passwordLogin.strategy";
import { PasswordlessLogInStrategy } from "../misc/logInStrategies/passwordlessLogin.strategy";
import { SsoLogInStrategy } from "../misc/logInStrategies/ssoLogin.strategy";
import { UserApiLogInStrategy } from "../misc/logInStrategies/user-api-login.strategy";
import { AuthResult } from "../models/domain/auth-result";
import {
ApiLogInCredentials,
UserApiLogInCredentials,
PasswordLogInCredentials,
SsoLogInCredentials,
PasswordlessLogInCredentials,
@@ -67,7 +67,7 @@ export class AuthService implements AuthServiceAbstraction {
}
private logInStrategy:
| ApiLogInStrategy
| UserApiLogInStrategy
| PasswordLogInStrategy
| SsoLogInStrategy
| PasswordlessLogInStrategy;
@@ -92,7 +92,7 @@ export class AuthService implements AuthServiceAbstraction {
async logIn(
credentials:
| ApiLogInCredentials
| UserApiLogInCredentials
| PasswordLogInCredentials
| SsoLogInCredentials
| PasswordlessLogInCredentials
@@ -100,7 +100,7 @@ export class AuthService implements AuthServiceAbstraction {
this.clearState();
let strategy:
| ApiLogInStrategy
| UserApiLogInStrategy
| PasswordLogInStrategy
| SsoLogInStrategy
| PasswordlessLogInStrategy;
@@ -134,8 +134,8 @@ export class AuthService implements AuthServiceAbstraction {
this.keyConnectorService
);
break;
case AuthenticationType.Api:
strategy = new ApiLogInStrategy(
case AuthenticationType.UserApi:
strategy = new UserApiLogInStrategy(
this.cryptoService,
this.apiService,
this.tokenService,
@@ -203,8 +203,8 @@ export class AuthService implements AuthServiceAbstraction {
this.messagingService.send("loggedOut");
}
authingWithApiKey(): boolean {
return this.logInStrategy instanceof ApiLogInStrategy;
authingWithUserApiKey(): boolean {
return this.logInStrategy instanceof UserApiLogInStrategy;
}
authingWithSso(): boolean {
@@ -272,7 +272,7 @@ export class AuthService implements AuthServiceAbstraction {
private saveState(
strategy:
| ApiLogInStrategy
| UserApiLogInStrategy
| PasswordLogInStrategy
| SsoLogInStrategy
| PasswordlessLogInStrategy

View File

@@ -64,6 +64,20 @@ export class FolderService implements InternalFolderServiceAbstraction {
return folders.find((folder) => folder.id === id);
}
/**
* @deprecated For the CLI only
* @param id id of the folder
*/
async getFromState(id: string): Promise<Folder> {
const foldersMap = await this.stateService.getEncryptedFolders();
const folder = foldersMap[id];
if (folder == null) {
return null;
}
return new Folder(folder);
}
/**
* @deprecated Only use in CLI!
*/

View File

@@ -0,0 +1,79 @@
import { Component, HostBinding, Input } from "@angular/core";
import { Utils } from "@bitwarden/common/misc/utils";
enum CharacterType {
Letter,
Emoji,
Special,
Number,
}
@Component({
selector: "bit-color-password",
template: `<div
*ngFor="let character of passwordArray; index as i"
[class]="getCharacterClass(character)"
>
<span>{{ character }}</span>
<span *ngIf="showCount" class="tw-whitespace-nowrap tw-text-xs tw-leading-5 tw-text-main">{{
i + 1
}}</span>
</div>`,
})
export class ColorPasswordComponent {
@Input() private password: string = null;
@Input() showCount = false;
characterStyles: Record<CharacterType, string[]> = {
[CharacterType.Emoji]: [],
[CharacterType.Letter]: ["tw-text-main"],
[CharacterType.Special]: ["tw-text-danger"],
[CharacterType.Number]: ["tw-text-primary-500"],
};
@HostBinding("class")
get classList() {
return ["tw-min-w-0", "tw-whitespace-pre-wrap", "tw-break-all"];
}
get passwordArray() {
// Convert to an array to handle cases that strings have special characters, i.e.: emoji.
return Array.from(this.password);
}
getCharacterClass(character: string) {
const charType = this.getCharacterType(character);
const charClass = this.characterStyles[charType].concat("tw-inline-flex");
if (this.showCount) {
return charClass.concat([
"tw-inline-flex",
"tw-flex-col",
"tw-items-center",
"tw-w-7",
"tw-py-1",
"odd:tw-bg-secondary-100",
]);
}
return charClass;
}
private getCharacterType(character: string): CharacterType {
if (character.match(Utils.regexpEmojiPresentation)) {
return CharacterType.Emoji;
}
if (character.match(/\d/)) {
return CharacterType.Number;
}
const specials = ["&", "<", ">", " "];
if (specials.includes(character) || character.match(/[^\w ]/)) {
return CharacterType.Special;
}
return CharacterType.Letter;
}
}

View File

@@ -0,0 +1,11 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { ColorPasswordComponent } from "./color-password.component";
@NgModule({
imports: [CommonModule],
exports: [ColorPasswordComponent],
declarations: [ColorPasswordComponent],
})
export class ColorPasswordModule {}

View File

@@ -0,0 +1,52 @@
import { Meta, Story } from "@storybook/angular";
import { ColorPasswordComponent } from "./color-password.component";
const examplePassword = "Wq$Jk😀7jDX#rS5Sdi!z";
export default {
title: "Component Library/Color Password",
component: ColorPasswordComponent,
args: {
password: examplePassword,
showCount: false,
},
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/file/6fvTDa3zfvgWdizLQ7nSTP/Numbered-Password",
},
},
} as Meta;
const Template: Story<ColorPasswordComponent> = (args: ColorPasswordComponent) => ({
props: args,
template: `
<bit-color-password class="tw-text-base" [password]="password" [showCount]="showCount"></bit-color-password>
`,
});
const WrappedTemplate: Story<ColorPasswordComponent> = (args: ColorPasswordComponent) => ({
props: args,
template: `
<div class="tw-max-w-32">
<bit-color-password class="tw-text-base" [password]="password" [showCount]="showCount"></bit-color-password>
</div>
`,
});
export const ColorPassword = Template.bind({});
export const WrappedColorPassword = WrappedTemplate.bind({});
export const ColorPasswordCount = Template.bind({});
ColorPasswordCount.args = {
password: examplePassword,
showCount: true,
};
export const WrappedColorPasswordCount = WrappedTemplate.bind({});
WrappedColorPasswordCount.args = {
password: examplePassword,
showCount: true,
};

View File

@@ -0,0 +1 @@
export * from "./color-password.module";

View File

@@ -1,10 +1,12 @@
import { DialogModule, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { DIALOG_DATA, DialogModule, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject } from "@angular/core";
import { Meta, moduleMetadata, Story } from "@storybook/angular";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { ButtonModule } from "../button";
import { IconButtonModule } from "../icon-button";
import { SharedModule } from "../shared";
import { I18nMockService } from "../utils/i18n-mock.service";
import { DialogService } from "./dialog.service";
@@ -35,7 +37,7 @@ class StoryDialogComponent {
@Component({
selector: "story-dialog-content",
template: `
<bit-dialog [dialogSize]="large">
<bit-dialog dialogSize="large">
<span bitDialogTitle>Dialog Title</span>
<span bitDialogContent>
Dialog body text goes here.
@@ -68,7 +70,7 @@ export default {
DialogTitleContainerDirective,
StoryDialogContentComponent,
],
imports: [ButtonModule, DialogModule],
imports: [SharedModule, ButtonModule, DialogModule, IconButtonModule],
providers: [
DialogService,
{

View File

@@ -14,4 +14,5 @@ export * from "./multi-select";
export * from "./tabs";
export * from "./table";
export * from "./toggle-group";
export * from "./color-password";
export * from "./utils/i18n-mock.service";

View File

@@ -34,7 +34,7 @@
role="tabpanel"
*ngFor="let tab of tabs; let i = index"
[id]="getTabContentId(i)"
[attr.tabindex]="selectedIndex === i ? 0 : -1"
[attr.tabindex]="tab.contentTabIndex"
[attr.labeledby]="getTabLabelId(i)"
[active]="tab.isActive"
[content]="tab.content"

View File

@@ -20,9 +20,18 @@ import { TabLabelDirective } from "./tab-label.directive";
})
export class TabComponent implements OnInit {
@Input() disabled = false;
@Input("label") textLabel = "";
/**
* Optional tabIndex for the tabPanel that contains this tab's content.
*
* If the tabpanel does not contain any focusable elements or the first element with content is not focusable,
* this should be set to 0 to include it in the tab sequence of the page.
*
* @remarks See note 4 of https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel/
*/
@Input() contentTabIndex: number | undefined;
@ViewChild(TemplateRef, { static: true }) implicitContent: TemplateRef<unknown>;
@ContentChild(TabLabelDirective) templateLabel: TabLabelDirective;

View File

@@ -3,6 +3,9 @@ import { Component } from "@angular/core";
import { RouterModule } from "@angular/router";
import { Meta, moduleMetadata, Story } from "@storybook/angular";
import { ButtonModule } from "../button";
import { FormFieldModule } from "../form-field";
import { TabGroupComponent } from "./tab-group/tab-group.component";
import { TabsModule } from "./tabs.module";
@@ -44,6 +47,8 @@ export default {
imports: [
CommonModule,
TabsModule,
ButtonModule,
FormFieldModule,
RouterModule.forRoot(
[
{ path: "", redirectTo: "active", pathMatch: "full" },
@@ -125,3 +130,32 @@ const PreserveContentTabGroupTemplate: Story<TabGroupComponent> = (args: any) =>
});
export const PreserveContentTabs = PreserveContentTabGroupTemplate.bind({});
const KeyboardNavTabGroupTemplate: Story<TabGroupComponent> = (args: any) => ({
props: args,
template: `
<bit-tab-group label="Keyboard Navigation Tabs" class="tw-text-main">
<bit-tab label="Form Tab">
<p>
You can navigate through all tab labels, form inputs, and the button that is outside the tab group via
the keyboard.
</p>
<bit-form-field>
<bit-label>First Input</bit-label>
<input type="text" bitInput />
</bit-form-field>
<bit-form-field>
<bit-label>Second Input</bit-label>
<input type="text" bitInput />
</bit-form-field>
</bit-tab>
<bit-tab label="No Focusable Content Tab" [contentTabIndex]="0">
<p>This tab has no focusable content, but the panel should still be focusable</p>
</bit-tab>
</bit-tab-group>
<button bitButton buttonType="primary" class="tw-mt-5">External Button</button>
`,
});
export const KeyboardNavigation = KeyboardNavTabGroupTemplate.bind({});

View File

@@ -1,6 +1,6 @@
import { Utils } from "@bitwarden/common/misc/utils";
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key";
import { NodeCryptoFunctionService } from "@bitwarden/node/services/nodeCryptoFunction.service";
import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service";
const RsaPublicKey =
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP" +

View File

@@ -21,7 +21,7 @@ import { NodeUtils } from "@bitwarden/common/misc/nodeUtils";
import { Utils } from "@bitwarden/common/misc/utils";
import { AuthResult } from "@bitwarden/common/models/domain/auth-result";
import {
ApiLogInCredentials,
UserApiLogInCredentials,
PasswordLogInCredentials,
SsoLogInCredentials,
} from "@bitwarden/common/models/domain/log-in-credentials";
@@ -160,7 +160,12 @@ export class LoginCommand {
let response: AuthResult = null;
if (clientId != null && clientSecret != null) {
response = await this.authService.logIn(new ApiLogInCredentials(clientId, clientSecret));
if (!clientId.startsWith("user")) {
return Response.error("Invalid API Key; Organization API Key currently not supported");
}
response = await this.authService.logIn(
new UserApiLogInCredentials(clientId, clientSecret)
);
} else if (ssoCode != null && ssoCodeVerifier != null) {
response = await this.authService.logIn(
new SsoLogInCredentials(