diff --git a/packages/core/src/libraries/sign-in-experience/index.test.ts b/packages/core/src/libraries/sign-in-experience/index.test.ts index ce0ea0195..8fe45b54e 100644 --- a/packages/core/src/libraries/sign-in-experience/index.test.ts +++ b/packages/core/src/libraries/sign-in-experience/index.test.ts @@ -159,7 +159,7 @@ describe('getFullSignInExperience()', () => { ssoConnectors: [ { id: wellConfiguredSsoConnector.id, - connectorName: wellConfiguredSsoConnector.connectorName, + connectorName: wellConfiguredSsoConnector.providerName, logo: ssoConnectorFactories[wellConfiguredSsoConnector.providerName].logo, darkLogo: ssoConnectorFactories[wellConfiguredSsoConnector.providerName].logoDark, }, @@ -183,7 +183,34 @@ describe('get sso connectors', () => { expect(ssoConnectors).toEqual([ { id: wellConfiguredSsoConnector.id, - connectorName: wellConfiguredSsoConnector.connectorName, + connectorName: wellConfiguredSsoConnector.providerName, + logo: ssoConnectorFactories[wellConfiguredSsoConnector.providerName].logo, + darkLogo: ssoConnectorFactories[wellConfiguredSsoConnector.providerName].logoDark, + }, + ]); + }); + + it('should return displayName if provided', async () => { + getLogtoConnectors.mockResolvedValueOnce(mockSocialConnectors); + findDefaultSignInExperience.mockResolvedValueOnce(mockSignInExperience); + + const displayName = 'Logto Connector'; + + ssoConnectorLibrary.getAvailableSsoConnectors.mockResolvedValueOnce([ + { + ...wellConfiguredSsoConnector, + branding: { + displayName, + }, + }, + ]); + + const { ssoConnectors } = await getFullSignInExperience(); + + expect(ssoConnectors).toEqual([ + { + id: wellConfiguredSsoConnector.id, + connectorName: displayName, logo: ssoConnectorFactories[wellConfiguredSsoConnector.providerName].logo, darkLogo: ssoConnectorFactories[wellConfiguredSsoConnector.providerName].logoDark, }, diff --git a/packages/core/src/libraries/sign-in-experience/index.ts b/packages/core/src/libraries/sign-in-experience/index.ts index beb7eb942..c99587745 100644 --- a/packages/core/src/libraries/sign-in-experience/index.ts +++ b/packages/core/src/libraries/sign-in-experience/index.ts @@ -71,18 +71,17 @@ export const createSignInExperienceLibrary = ( const ssoConnectors = await getAvailableSsoConnectors(); - return ssoConnectors.map( - ({ providerName, connectorName, id, branding }): SsoConnectorMetadata => { - const factory = ssoConnectorFactories[providerName]; + return ssoConnectors.map(({ providerName, id, branding }): SsoConnectorMetadata => { + const factory = ssoConnectorFactories[providerName]; - return { - id, - connectorName, - logo: branding.logo ?? factory.logo, - darkLogo: branding.darkLogo ?? factory.logoDark, - }; - } - ); + return { + id, + // Use the provider name as the connector name if the branding displayName is not provided + connectorName: branding.displayName ?? providerName, + logo: branding.logo ?? factory.logo, + darkLogo: branding.darkLogo ?? factory.logoDark, + }; + }); }; /** diff --git a/packages/core/src/queries/sso-connectors.ts b/packages/core/src/queries/sso-connectors.ts index 9e968fbb6..a5e5d26c5 100644 --- a/packages/core/src/queries/sso-connectors.ts +++ b/packages/core/src/queries/sso-connectors.ts @@ -4,7 +4,8 @@ import { type SsoConnectorKeys, SsoConnectors, } from '@logto/schemas'; -import { type CommonQueryMethods } from 'slonik'; +import { convertToIdentifiers } from '@logto/shared'; +import { sql, type CommonQueryMethods } from 'slonik'; import SchemaQueries from '#src/utils/SchemaQueries.js'; @@ -16,4 +17,13 @@ export default class SsoConnectorQueries extends SchemaQueries< constructor(pool: CommonQueryMethods) { super(pool, SsoConnectors); } + + async findByConnectorName(connectorName: string) { + const { table, fields } = convertToIdentifiers(SsoConnectors); + + return this.pool.maybeOne(sql` + SELECT * FROM ${table} + where ${fields.connectorName}=${connectorName} + `); + } } diff --git a/packages/core/src/routes/sso-connector/index.ts b/packages/core/src/routes/sso-connector/index.ts index d0b07bbc7..9af1977dd 100644 --- a/packages/core/src/routes/sso-connector/index.ts +++ b/packages/core/src/routes/sso-connector/index.ts @@ -16,6 +16,7 @@ import { ssoConnectorCreateGuard, ssoConnectorPatchGuard } from '#src/routes/sso import { ssoConnectorFactories, standardSsoConnectorProviders } from '#src/sso/index.js'; import { isSupportedSsoProvider, isSupportedSsoConnector } from '#src/sso/utils.js'; import { tableToPathname } from '#src/utils/SchemaRouter.js'; +import assertThat from '#src/utils/assert-that.js'; import { type AuthedRouter, type RouterInitArgs } from '../types.js'; @@ -109,6 +110,12 @@ export default function singleSignOnRoutes(...args: Rout // Validate the connector config if it's provided const parsedConfig = config && parseConnectorConfig(providerName, config); + // Validate the connector name is unique + if (connectorName) { + const duplicateConnector = await ssoConnectors.findByConnectorName(connectorName); + assertThat(!duplicateConnector, 'single_sign_on.duplicate_connector_name'); + } + const connectorId = generateStandardShortId(); // Check the connection status of the connector config if it's provided @@ -224,6 +231,12 @@ export default function singleSignOnRoutes(...args: Rout validateConnectorDomains(domains); } + // Validate the connector name is unique + if (rest.connectorName) { + const duplicateConnector = await ssoConnectors.findByConnectorName(rest.connectorName); + assertThat(!duplicateConnector, 'single_sign_on.duplicate_connector_name'); + } + // Validate the connector config if it's provided const parsedConfig = config && parseConnectorConfig(providerName, config); diff --git a/packages/integration-tests/src/constants.ts b/packages/integration-tests/src/constants.ts index 1137ca018..a9bc0c88d 100644 --- a/packages/integration-tests/src/constants.ts +++ b/packages/integration-tests/src/constants.ts @@ -36,6 +36,7 @@ export const newOidcSsoConnectorPayload = { connectorName: 'test-oidc', domains: ['example.io'], // Auto-generated email domain branding: { + displayName: 'test oidc connector', logo: 'https://logto.io/oidc-logo.png', darkLogo: 'https://logto.io/oidc-dark-logo.png', }, diff --git a/packages/integration-tests/src/tests/api/interaction/register-with-identifier/sad-path.test.ts b/packages/integration-tests/src/tests/api/interaction/register-with-identifier/sad-path.test.ts index 66d7a12c6..feb2e12ce 100644 --- a/packages/integration-tests/src/tests/api/interaction/register-with-identifier/sad-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/register-with-identifier/sad-path.test.ts @@ -21,7 +21,12 @@ import { enableAllVerificationCodeSignInMethods, } from '#src/helpers/sign-in-experience.js'; import { generateNewUserProfile } from '#src/helpers/user.js'; -import { generateEmail, generatePassword, generateUsername } from '#src/utils.js'; +import { + generateEmail, + generatePassword, + generateSsoConnectorName, + generateUsername, +} from '#src/utils.js'; describe('Register with identifiers sad path', () => { beforeAll(async () => { @@ -59,6 +64,7 @@ describe('Register with identifiers sad path', () => { await createSsoConnector({ ...newOidcSsoConnectorPayload, + connectorName: generateSsoConnectorName(), domains: ['sso-register-sad-path.io'], }); diff --git a/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/sad-path.test.ts b/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/sad-path.test.ts index 1e14055a0..d5187444e 100644 --- a/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/sad-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/sign-in-with-passcode-identifier/sad-path.test.ts @@ -20,7 +20,12 @@ import { import { expectRejects, readVerificationCode } from '#src/helpers/index.js'; import { enableAllVerificationCodeSignInMethods } from '#src/helpers/sign-in-experience.js'; import { generateNewUser } from '#src/helpers/user.js'; -import { generateEmail, generatePassword, generatePhone } from '#src/utils.js'; +import { + generateEmail, + generatePassword, + generatePhone, + generateSsoConnectorName, +} from '#src/utils.js'; describe('Sign-in flow sad path using verification-code identifiers', () => { beforeAll(async () => { @@ -220,6 +225,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => { await createSsoConnector({ ...newOidcSsoConnectorPayload, + connectorName: generateSsoConnectorName(), domains: ['sso-sad-path.io'], }); diff --git a/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/happy-path.test.ts b/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/happy-path.test.ts index a46bf9665..6f5e2d1ff 100644 --- a/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/happy-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/happy-path.test.ts @@ -21,6 +21,7 @@ import { enableAllVerificationCodeSignInMethods, } from '#src/helpers/sign-in-experience.js'; import { generateNewUser, generateNewUserProfile } from '#src/helpers/user.js'; +import { generateSsoConnectorName } from '#src/utils.js'; describe('Sign-in flow using password identifiers', () => { beforeAll(async () => { @@ -79,7 +80,10 @@ describe('Sign-in flow using password identifiers', () => { const client = await initClient(); // Create a new OIDC SSO connector with email domain 'example.com', it should not block the sign-in flow of email logto.io - await createSsoConnector(newOidcSsoConnectorPayload); + await createSsoConnector({ + ...newOidcSsoConnectorPayload, + connectorName: generateSsoConnectorName(), + }); await client.successSend(putInteraction, { event: InteractionEvent.SignIn, diff --git a/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/sad-path.test.ts b/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/sad-path.test.ts index 6f355197e..ff586de4a 100644 --- a/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/sad-path.test.ts +++ b/packages/integration-tests/src/tests/api/interaction/sign-in-with-password-identifier/sad-path.test.ts @@ -10,7 +10,12 @@ import { clearSsoConnectors } from '#src/helpers/connector.js'; import { expectRejects } from '#src/helpers/index.js'; import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js'; import { generateNewUser } from '#src/helpers/user.js'; -import { generateEmail, generateName, generatePassword } from '#src/utils.js'; +import { + generateEmail, + generateName, + generatePassword, + generateSsoConnectorName, +} from '#src/utils.js'; describe('Sign-in flow sad path using password identifiers', () => { beforeAll(async () => { @@ -191,6 +196,7 @@ describe('Sign-in flow sad path using password identifiers', () => { await createSsoConnector({ ...newOidcSsoConnectorPayload, + connectorName: generateSsoConnectorName(), domains: ['sso-sad-path.io'], }); diff --git a/packages/integration-tests/src/tests/api/sso-connectors.test.ts b/packages/integration-tests/src/tests/api/sso-connectors.test.ts index fba42fb88..59d698b36 100644 --- a/packages/integration-tests/src/tests/api/sso-connectors.test.ts +++ b/packages/integration-tests/src/tests/api/sso-connectors.test.ts @@ -13,6 +13,7 @@ import { deleteSsoConnectorById, patchSsoConnectorById, } from '#src/api/sso-connector.js'; +import { expectRejects } from '#src/helpers/index.js'; describe('sso-connector library', () => { it('should return sso-connector-factories', async () => { @@ -57,6 +58,23 @@ describe('post sso-connectors', () => { ).rejects.toThrow(HTTPError); }); + it('should throw error when connectorName is not unique', async () => { + const { id } = await createSsoConnector({ + providerName: 'OIDC', + connectorName: 'test connector name', + }); + + await expectRejects( + createSsoConnector({ + providerName: 'OIDC', + connectorName: 'test connector name', + }), + { code: 'single_sign_on.duplicate_connector_name', statusCode: 400 } + ); + + await deleteSsoConnectorById(id); + }); + it.each(providerNames)('should create a new sso connector', async (providerName) => { const response = await createSsoConnector({ providerName, @@ -174,6 +192,28 @@ describe('patch sso-connector by id', () => { ); }); + it('should throw error if connector name is not unique', async () => { + const { id } = await createSsoConnector({ + providerName: 'OIDC', + connectorName: 'test connector name', + }); + + const { id: id2 } = await createSsoConnector({ + providerName: 'OIDC', + connectorName: 'test connector name 2', + }); + + await expectRejects( + patchSsoConnectorById(id2, { + connectorName: 'test connector name', + }), + { code: 'single_sign_on.duplicate_connector_name', statusCode: 400 } + ); + + await deleteSsoConnectorById(id); + await deleteSsoConnectorById(id2); + }); + it.each(providerNames)('should patch sso connector without config', async (providerName) => { const { id } = await createSsoConnector({ providerName, diff --git a/packages/integration-tests/src/tests/api/well-known.test.ts b/packages/integration-tests/src/tests/api/well-known.test.ts index 86fca3be6..91c52d8e0 100644 --- a/packages/integration-tests/src/tests/api/well-known.test.ts +++ b/packages/integration-tests/src/tests/api/well-known.test.ts @@ -4,6 +4,7 @@ import { HTTPError } from 'got'; import api, { adminTenantApi, authedAdminApi } from '#src/api/api.js'; import { createSsoConnector, deleteSsoConnectorById } from '#src/api/sso-connector.js'; import { newOidcSsoConnectorPayload } from '#src/constants.js'; +import { generateSsoConnectorName } from '#src/utils.js'; describe('.well-known api', () => { it('should return tenant endpoint URL for any given tenant id', async () => { @@ -57,7 +58,10 @@ describe('.well-known api', () => { describe('sso connectors in sign-in experience', () => { it('should get the sso connectors in sign-in experience', async () => { - const { id, connectorName } = await createSsoConnector(newOidcSsoConnectorPayload); + const { id } = await createSsoConnector({ + ...newOidcSsoConnectorPayload, + connectorName: generateSsoConnectorName(), + }); const signInExperience = await api .get('.well-known/sign-in-exp') @@ -71,7 +75,7 @@ describe('.well-known api', () => { expect(newCreatedConnector).toMatchObject({ id, - connectorName, + connectorName: newOidcSsoConnectorPayload.branding.displayName, logo: newOidcSsoConnectorPayload.branding.logo, darkLogo: newOidcSsoConnectorPayload.branding.darkLogo, }); @@ -82,6 +86,7 @@ describe('.well-known api', () => { it('should filter out the sso connectors with invalid config', async () => { const { id } = await createSsoConnector({ ...newOidcSsoConnectorPayload, + connectorName: generateSsoConnectorName(), config: undefined, }); @@ -99,6 +104,7 @@ describe('.well-known api', () => { it('should filter out the sso connectors with empty domains', async () => { const { id } = await createSsoConnector({ ...newOidcSsoConnectorPayload, + connectorName: generateSsoConnectorName(), domains: [], }); diff --git a/packages/integration-tests/src/utils.ts b/packages/integration-tests/src/utils.ts index fcc2a265e..31c6b8841 100644 --- a/packages/integration-tests/src/utils.ts +++ b/packages/integration-tests/src/utils.ts @@ -17,6 +17,7 @@ export const generateEmail = (domain = 'logto.io') => export const generateScopeName = () => `sc:${crypto.randomUUID()}`; export const generateRoleName = () => `role_${crypto.randomUUID()}`; export const generateDomain = () => `${crypto.randomUUID().toLowerCase().slice(0, 5)}.example.com`; +export const generateSsoConnectorName = () => `sso_${crypto.randomUUID()}`; export const generatePhone = (isE164?: boolean) => { const plus = isE164 ? '+' : ''; diff --git a/packages/phrases/src/locales/de/errors/single-sign-on.ts b/packages/phrases/src/locales/de/errors/single-sign-on.ts index 7e8ba86ba..7ee818d60 100644 --- a/packages/phrases/src/locales/de/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/de/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: 'Es gibt doppelte Domänen.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/en/errors/single-sign-on.ts b/packages/phrases/src/locales/en/errors/single-sign-on.ts index 5049be5bb..b114bfc44 100644 --- a/packages/phrases/src/locales/en/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/en/errors/single-sign-on.ts @@ -3,6 +3,7 @@ const single_sign_on = { duplicated_domains: 'There are duplicate domains.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/es/errors/single-sign-on.ts b/packages/phrases/src/locales/es/errors/single-sign-on.ts index 84fb1c0f8..061a24780 100644 --- a/packages/phrases/src/locales/es/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/es/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: 'Hay dominios duplicados.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/fr/errors/single-sign-on.ts b/packages/phrases/src/locales/fr/errors/single-sign-on.ts index 53d3b2a15..938c49a50 100644 --- a/packages/phrases/src/locales/fr/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/fr/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: 'Il existe des domaines en double.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/it/errors/single-sign-on.ts b/packages/phrases/src/locales/it/errors/single-sign-on.ts index aa7cafec3..6ab47f097 100644 --- a/packages/phrases/src/locales/it/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/it/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: 'Ci sono domini duplicati.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/ja/errors/single-sign-on.ts b/packages/phrases/src/locales/ja/errors/single-sign-on.ts index bfaff0821..f69f8c83e 100644 --- a/packages/phrases/src/locales/ja/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/ja/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: '重複するドメインがあります。', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/ko/errors/single-sign-on.ts b/packages/phrases/src/locales/ko/errors/single-sign-on.ts index ec0f872ed..f9cc83c83 100644 --- a/packages/phrases/src/locales/ko/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/ko/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: '중복된 도메인이 있습니다.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/pl-pl/errors/single-sign-on.ts b/packages/phrases/src/locales/pl-pl/errors/single-sign-on.ts index 3172421bf..cfbb1a76f 100644 --- a/packages/phrases/src/locales/pl-pl/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/pl-pl/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: 'Istnieją zduplikowane domeny.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/pt-br/errors/single-sign-on.ts b/packages/phrases/src/locales/pt-br/errors/single-sign-on.ts index 237ae86fe..6ccdc266a 100644 --- a/packages/phrases/src/locales/pt-br/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/pt-br/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: 'Existem domínios duplicados.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/pt-pt/errors/single-sign-on.ts b/packages/phrases/src/locales/pt-pt/errors/single-sign-on.ts index 14fd5b042..0f9f6307c 100644 --- a/packages/phrases/src/locales/pt-pt/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/pt-pt/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: 'Existem domínios duplicados.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/ru/errors/single-sign-on.ts b/packages/phrases/src/locales/ru/errors/single-sign-on.ts index 33c0c10b7..735f330e9 100644 --- a/packages/phrases/src/locales/ru/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/ru/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: 'Есть дублирующиеся домены.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/tr-tr/errors/single-sign-on.ts b/packages/phrases/src/locales/tr-tr/errors/single-sign-on.ts index 613205245..f29499708 100644 --- a/packages/phrases/src/locales/tr-tr/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/tr-tr/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: 'Yinelenmiş domainler bulunmaktadır.', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/zh-cn/errors/single-sign-on.ts b/packages/phrases/src/locales/zh-cn/errors/single-sign-on.ts index a346b7e5f..220e1717a 100644 --- a/packages/phrases/src/locales/zh-cn/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/zh-cn/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: '存在重复的域名。', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/zh-hk/errors/single-sign-on.ts b/packages/phrases/src/locales/zh-hk/errors/single-sign-on.ts index 35bc05f94..c9a678a01 100644 --- a/packages/phrases/src/locales/zh-hk/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/zh-hk/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: '存在重複的域名。', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/phrases/src/locales/zh-tw/errors/single-sign-on.ts b/packages/phrases/src/locales/zh-tw/errors/single-sign-on.ts index 624e544fe..e24bbc146 100644 --- a/packages/phrases/src/locales/zh-tw/errors/single-sign-on.ts +++ b/packages/phrases/src/locales/zh-tw/errors/single-sign-on.ts @@ -3,6 +3,8 @@ const single_sign_on = { duplicated_domains: '存在重複的域名。', /** UNTRANSLATED */ invalid_domain_format: 'Invalid domain format.', + /** UNTRANSLATED */ + duplicate_connector_name: 'Connector name already exists. Please choose a different name.', }; export default Object.freeze(single_sign_on); diff --git a/packages/schemas/src/foundations/jsonb-types/sso-connector.ts b/packages/schemas/src/foundations/jsonb-types/sso-connector.ts index 86b9df42d..f0839aabb 100644 --- a/packages/schemas/src/foundations/jsonb-types/sso-connector.ts +++ b/packages/schemas/src/foundations/jsonb-types/sso-connector.ts @@ -5,6 +5,7 @@ export const ssoDomainsGuard = z.array(z.string()); export type SsoDomains = z.infer; export const ssoBrandingGuard = z.object({ + displayName: z.string().optional(), logo: z.string().optional(), darkLogo: z.string().optional(), });