diff --git a/.changeset/upset-results-win.md b/.changeset/upset-results-win.md new file mode 100644 index 00000000000..0b974cadc45 --- /dev/null +++ b/.changeset/upset-results-win.md @@ -0,0 +1,7 @@ +--- +'@clerk/localizations': minor +'@clerk/clerk-js': minor +'@clerk/types': minor +--- + +Introduce experimental step to choose enterprise connection on sign-in/sign-up diff --git a/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap b/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap index 2a06a2e0d56..8db8c3f416e 100644 --- a/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap +++ b/.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap @@ -127,6 +127,8 @@ exports[`Typedoc output > should have a deliberate file structure 1`] = ` "types/sign-in-signal-value.mdx", "types/sign-out.mdx", "types/sign-up-authenticate-with-metamask-params.mdx", + "types/sign-up-enterprise-connection-json.mdx", + "types/sign-up-enterprise-connection-resource.mdx", "types/sign-up-future-resource.mdx", "types/sign-up-resource.mdx", "types/signed-in-session-resource.mdx", diff --git a/packages/clerk-js/bundlewatch.config.json b/packages/clerk-js/bundlewatch.config.json index eb4717c9b6f..86aed3c552c 100644 --- a/packages/clerk-js/bundlewatch.config.json +++ b/packages/clerk-js/bundlewatch.config.json @@ -16,7 +16,7 @@ { "path": "./dist/organizationswitcher*.js", "maxSize": "5KB" }, { "path": "./dist/organizationlist*.js", "maxSize": "5.5KB" }, { "path": "./dist/signin*.js", "maxSize": "18KB" }, - { "path": "./dist/signup*.js", "maxSize": "8.86KB" }, + { "path": "./dist/signup*.js", "maxSize": "9.5KB" }, { "path": "./dist/userbutton*.js", "maxSize": "5KB" }, { "path": "./dist/userprofile*.js", "maxSize": "16KB" }, { "path": "./dist/userverification*.js", "maxSize": "5KB" }, diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index e72b9490851..cb7832dbb90 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -222,6 +222,7 @@ export class SignIn extends BaseResource implements SignInResource { redirectUrl: params.redirectUrl, actionCompleteRedirectUrl: params.actionCompleteRedirectUrl, oidcPrompt: params.oidcPrompt, + enterpriseConnectionId: params.enterpriseConnectionId, } as EnterpriseSSOConfig; break; default: @@ -308,7 +309,8 @@ export class SignIn extends BaseResource implements SignInResource { params: AuthenticateWithRedirectParams, navigateCallback: (url: URL | string) => void, ): Promise => { - const { strategy, redirectUrlComplete, identifier, oidcPrompt, continueSignIn } = params || {}; + const { strategy, redirectUrlComplete, identifier, oidcPrompt, continueSignIn, enterpriseConnectionId } = + params || {}; const actionCompleteRedirectUrl = redirectUrlComplete; const redirectUrl = SignIn.clerk.buildUrlWithAuth(params.redirectUrl); @@ -328,6 +330,7 @@ export class SignIn extends BaseResource implements SignInResource { redirectUrl, actionCompleteRedirectUrl, oidcPrompt, + enterpriseConnectionId, }); } diff --git a/packages/clerk-js/src/core/resources/SignUp.ts b/packages/clerk-js/src/core/resources/SignUp.ts index 02e23aae5f4..fbc697a210f 100644 --- a/packages/clerk-js/src/core/resources/SignUp.ts +++ b/packages/clerk-js/src/core/resources/SignUp.ts @@ -16,6 +16,8 @@ import type { PrepareWeb3WalletVerificationParams, SignUpAuthenticateWithWeb3Params, SignUpCreateParams, + SignUpEnterpriseConnectionJSON, + SignUpEnterpriseConnectionResource, SignUpField, SignUpFutureCreateParams, SignUpFutureEmailCodeVerifyParams, @@ -385,6 +387,7 @@ export class SignUp extends BaseResource implements SignUpResource { emailAddress, legalAccepted, oidcPrompt, + enterpriseConnectionId, } = params; const redirectUrlWithAuthToken = SignUp.clerk.buildUrlWithAuth(redirectUrl); @@ -398,6 +401,7 @@ export class SignUp extends BaseResource implements SignUpResource { emailAddress, legalAccepted, oidcPrompt, + enterpriseConnectionId, }; return continueSignUp && this.id ? this.update(authParams) : this.create(authParams); }; @@ -551,6 +555,17 @@ export class SignUp extends BaseResource implements SignUpResource { return false; } + + __experimental_getEnterpriseConnections = (): Promise => { + return BaseResource._fetch({ + path: `/client/sign_ups/${this.id}/enterprise_connections`, + method: 'GET', + }).then(res => { + const enterpriseConnections = res?.response as unknown as SignUpEnterpriseConnectionJSON[]; + + return enterpriseConnections.map(enterpriseConnection => new SignUpEnterpriseConnection(enterpriseConnection)); + }); + }; } class SignUpFuture implements SignUpFutureResource { @@ -889,3 +904,22 @@ class SignUpFuture implements SignUpFutureResource { }); } } + +class SignUpEnterpriseConnection extends BaseResource implements SignUpEnterpriseConnectionResource { + id!: string; + name!: string; + + constructor(data: SignUpEnterpriseConnectionJSON) { + super(); + this.fromJSON(data); + } + + protected fromJSON(data: SignUpEnterpriseConnectionJSON | null): this { + if (data) { + this.id = data.id; + this.name = data.name; + } + + return this; + } +} diff --git a/packages/clerk-js/src/ui/common/ChooseEnterpriseConnectionCard.tsx b/packages/clerk-js/src/ui/common/ChooseEnterpriseConnectionCard.tsx new file mode 100644 index 00000000000..73f3efb7b3c --- /dev/null +++ b/packages/clerk-js/src/ui/common/ChooseEnterpriseConnectionCard.tsx @@ -0,0 +1,123 @@ +import { useState } from 'react'; + +import type { LocalizationKey } from '@/ui/customizables'; +import { descriptors, Flex, Grid, SimpleButton, Spinner, Text } from '@/ui/customizables'; +import { Card } from '@/ui/elements/Card'; +import { useCardState } from '@/ui/elements/contexts'; +import { Header } from '@/ui/elements/Header'; +import type { InternalTheme, PropsOfComponent } from '@/ui/styledSystem'; + +type ChooseEnterpriseConnectionCardProps = { + title: LocalizationKey; + subtitle: LocalizationKey; + onClick: (id: string) => Promise; + enterpriseConnections: Array<{ id: string; name: string }>; +}; + +/** + * @experimental + */ +export const ChooseEnterpriseConnectionCard = ({ + title, + subtitle, + onClick, + enterpriseConnections, +}: ChooseEnterpriseConnectionCardProps) => { + const card = useCardState(); + + return ( + + + + + + + {card.error} + + + {enterpriseConnections?.map(({ id, name }) => ( + + ))} + + + + + + ); +}; + +type ChooseEnterpriseConnectionButtonProps = Omit, 'onClick'> & { + id: string; + label?: string; + onClick: (id: string) => Promise; +}; + +const ChooseEnterpriseConnectionButton = (props: ChooseEnterpriseConnectionButtonProps): JSX.Element => { + const { label, onClick, ...rest } = props; + const [isLoading, setIsLoading] = useState(false); + + const handleClick = () => { + setIsLoading(true); + void onClick(props.id).catch(() => setIsLoading(false)); + }; + + return ( + [ + { + gap: theme.space.$4, + position: 'relative', + justifyContent: 'flex-start', + }, + (rest as any).sx, + ]} + > + + {isLoading && ( + ({ flex: `0 0 ${theme.space.$4}` })} + > + + + )} + + {label} + + + + ); +}; diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInFactorOne.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInFactorOne.tsx index 3a0634b7477..e238c5d585e 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInFactorOne.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInFactorOne.tsx @@ -12,9 +12,11 @@ import { useAlternativeStrategies } from '../../hooks/useAlternativeStrategies'; import { localizationKeys } from '../../localization'; import { useRouter } from '../../router'; import { AlternativeMethods } from './AlternativeMethods'; +import { hasMultipleEnterpriseConnections } from './shared'; import { SignInFactorOneAlternativePhoneCodeCard } from './SignInFactorOneAlternativePhoneCodeCard'; import { SignInFactorOneEmailCodeCard } from './SignInFactorOneEmailCodeCard'; import { SignInFactorOneEmailLinkCard } from './SignInFactorOneEmailLinkCard'; +import { SignInFactorOneEnterpriseConnections } from './SignInFactorOneEnterpriseConnections'; import { SignInFactorOneForgotPasswordCard } from './SignInFactorOneForgotPasswordCard'; import { SignInFactorOnePasskey } from './SignInFactorOnePasskey'; import { SignInFactorOnePasswordCard } from './SignInFactorOnePasswordCard'; @@ -122,6 +124,15 @@ function SignInFactorOneInternal(): JSX.Element { prevCurrentFactor: prev.currentFactor, })); }; + + /** + * Prompt to choose between a list of enterprise connections as supported first factors + * @experimental + */ + if (hasMultipleEnterpriseConnections(signIn.supportedFirstFactors)) { + return ; + } + if (showAllStrategies || showForgotPasswordStrategies) { const canGoBack = factorHasLocalStrategy(currentFactor); diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInFactorOneEnterpriseConnections.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInFactorOneEnterpriseConnections.tsx new file mode 100644 index 00000000000..e5256e3a385 --- /dev/null +++ b/packages/clerk-js/src/ui/components/SignIn/SignInFactorOneEnterpriseConnections.tsx @@ -0,0 +1,82 @@ +import { useClerk } from '@clerk/shared/react/index'; +import type { ComponentType } from 'react'; + +import { buildSSOCallbackURL, withRedirect } from '@/ui/common'; +import { ChooseEnterpriseConnectionCard } from '@/ui/common/ChooseEnterpriseConnectionCard'; +import { useCoreSignIn, useEnvironment, useSignInContext } from '@/ui/contexts'; +import { Flow, localizationKeys } from '@/ui/customizables'; +import { withCardStateProvider } from '@/ui/elements/contexts'; +import type { AvailableComponentProps } from '@/ui/types'; + +import { hasMultipleEnterpriseConnections } from './shared'; + +/** + * @experimental + */ +const SignInFactorOneEnterpriseConnectionsInternal = () => { + const ctx = useSignInContext(); + const { displayConfig } = useEnvironment(); + + const clerk = useClerk(); + const signIn = clerk.client.signIn; + + if (!hasMultipleEnterpriseConnections(signIn.supportedFirstFactors)) { + // This should not happen due to the HOC guard, but provides type safety + return null; + } + + const enterpriseConnections = signIn.supportedFirstFactors.map(ff => ({ + id: ff.enterpriseConnectionId, + name: ff.enterpriseConnectionName, + })); + + const handleEnterpriseSSO = (enterpriseConnectionId: string) => { + const redirectUrl = buildSSOCallbackURL(ctx, displayConfig.signInUrl); + const redirectUrlComplete = ctx.afterSignInUrl || '/'; + + return signIn.authenticateWithRedirect({ + strategy: 'enterprise_sso', + redirectUrl, + redirectUrlComplete, + oidcPrompt: ctx.oidcPrompt, + continueSignIn: true, + enterpriseConnectionId, + }); + }; + + return ( + + + + ); +}; + +const withEnterpriseConnectionsGuard =

(Component: ComponentType

) => { + const displayName = Component.displayName || Component.name || 'Component'; + Component.displayName = displayName; + + const HOC = (props: P) => { + const signIn = useCoreSignIn(); + const signInCtx = useSignInContext(); + + return withRedirect( + Component, + () => !hasMultipleEnterpriseConnections(signIn.supportedFirstFactors), + ({ clerk }) => signInCtx.signInUrl || clerk.buildSignInUrl(), + 'There are no enterprise connections available to sign-in. Clerk is redirecting to the `signInUrl` instead.', + )(props); + }; + + HOC.displayName = `withEnterpriseConnectionsGuard(${displayName})`; + + return HOC; +}; + +export const SignInFactorOneEnterpriseConnections = withCardStateProvider( + withEnterpriseConnectionsGuard(SignInFactorOneEnterpriseConnectionsInternal), +); diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx index a46ec20057e..6b0dd0cc8a9 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx @@ -39,7 +39,7 @@ import { useLoadingStatus } from '../../hooks'; import { useSupportEmail } from '../../hooks/useSupportEmail'; import { useRouter } from '../../router'; import { handleCombinedFlowTransfer } from './handleCombinedFlowTransfer'; -import { useHandleAuthenticateWithPasskey } from './shared'; +import { hasMultipleEnterpriseConnections, useHandleAuthenticateWithPasskey } from './shared'; import { SignInAlternativePhoneCodePhoneNumberCard } from './SignInAlternativePhoneCodePhoneNumberCard'; import { SignInSocialButtons } from './SignInSocialButtons'; import { @@ -224,12 +224,13 @@ function SignInStartInternal(): JSX.Element { }) .then(res => { switch (res.status) { - case 'needs_first_factor': - if (hasOnlyEnterpriseSSOFirstFactors(res)) { - return authenticateWithEnterpriseSSO(); + case 'needs_first_factor': { + if (!hasOnlyEnterpriseSSOFirstFactors(res) || hasMultipleEnterpriseConnections(res.supportedFirstFactors)) { + return navigate('factor-one'); } - return navigate('factor-one'); + return authenticateWithEnterpriseSSO(); + } case 'needs_second_factor': return navigate('factor-two'); case 'complete': @@ -253,7 +254,7 @@ function SignInStartInternal(): JSX.Element { // Keep the card in loading state during SSO redirect to prevent UI flicker // This is necessary because there's a brief delay between initiating the SSO flow // and the actual redirect to the external Identity Provider - const isRedirectingToSSOProvider = hasOnlyEnterpriseSSOFirstFactors(signIn); + const isRedirectingToSSOProvider = !!hasOnlyEnterpriseSSOFirstFactors(signIn); if (isRedirectingToSSOProvider) { return; } @@ -381,12 +382,13 @@ function SignInStartInternal(): JSX.Element { await authenticateWithEnterpriseSSO(); } break; - case 'needs_first_factor': - if (hasOnlyEnterpriseSSOFirstFactors(res)) { - await authenticateWithEnterpriseSSO(); - break; + case 'needs_first_factor': { + if (!hasOnlyEnterpriseSSOFirstFactors(res) || hasMultipleEnterpriseConnections(res.supportedFirstFactors)) { + return navigate('factor-one'); } - return navigate('factor-one'); + + return authenticateWithEnterpriseSSO(); + } case 'needs_second_factor': return navigate('factor-two'); case 'complete': diff --git a/packages/clerk-js/src/ui/components/SignIn/shared.ts b/packages/clerk-js/src/ui/components/SignIn/shared.ts index f14c04c91b4..41a5828d9b5 100644 --- a/packages/clerk-js/src/ui/components/SignIn/shared.ts +++ b/packages/clerk-js/src/ui/components/SignIn/shared.ts @@ -1,5 +1,6 @@ import { isClerkRuntimeError, isUserLockedError } from '@clerk/shared/error'; import { useClerk } from '@clerk/shared/react'; +import type { EnterpriseSSOFactor, SignInFirstFactor } from '@clerk/types'; import { useCallback, useEffect } from 'react'; import { useCardState } from '@/ui/elements/contexts'; @@ -62,4 +63,26 @@ function useHandleAuthenticateWithPasskey(onSecondFactor: () => Promise }, []); } -export { useHandleAuthenticateWithPasskey }; +/** + * Type guard that checks if all factors in the array are enterprise SSO factors + * with both `enterpriseConnectionId` and `enterpriseConnectionName` properties. + * This is used to determine if the user should be presented with a choice + * between multiple enterprise connections. + * @experimental + */ +function hasMultipleEnterpriseConnections( + factors: SignInFirstFactor[] | null, +): factors is Array { + if (!factors?.length) { + return false; + } + + return factors.every( + factor => + factor.strategy === 'enterprise_sso' && + 'enterpriseConnectionId' in factor && + 'enterpriseConnectionName' in factor, + ); +} + +export { hasMultipleEnterpriseConnections, useHandleAuthenticateWithPasskey }; diff --git a/packages/clerk-js/src/ui/components/SignUp/SignUpEnterpriseConnections.tsx b/packages/clerk-js/src/ui/components/SignUp/SignUpEnterpriseConnections.tsx new file mode 100644 index 00000000000..b2eea839ebb --- /dev/null +++ b/packages/clerk-js/src/ui/components/SignUp/SignUpEnterpriseConnections.tsx @@ -0,0 +1,58 @@ +import { useClerk } from '@clerk/shared/react/index'; + +import { withRedirectToAfterSignUp } from '@/ui/common'; +import { ChooseEnterpriseConnectionCard } from '@/ui/common/ChooseEnterpriseConnectionCard'; +import { useSignUpContext } from '@/ui/contexts'; +import { Flow, localizationKeys } from '@/ui/customizables'; +import { withCardStateProvider } from '@/ui/elements/contexts'; +import { LoadingCard } from '@/ui/elements/LoadingCard'; +import { useFetch } from '@/ui/hooks'; + +const SignUpEnterpriseConnectionsInternal = () => { + const clerk = useClerk(); + const ctx = useSignUpContext(); + + const signUp = clerk.client.signUp; + const { data: enterpriseConnections, isLoading } = useFetch(signUp?.__experimental_getEnterpriseConnections, { + signUpId: signUp.id, + }); + + const handleEnterpriseSSO = (enterpriseConnectionId: string) => { + const redirectUrl = ctx.ssoCallbackUrl; + const redirectUrlComplete = ctx.afterSignUpUrl || '/'; + + return signUp.authenticateWithRedirect({ + strategy: 'enterprise_sso', + redirectUrl, + redirectUrlComplete, + continueSignUp: true, + enterpriseConnectionId, + }); + }; + + if (!isLoading && !enterpriseConnections?.length) { + return null; + } + + return ( + + {enterpriseConnections?.length ? ( + + ) : isLoading ? ( + + ) : null} + + ); +}; + +/** + * @experimental + */ +export const SignUpEnterpriseConnections = withRedirectToAfterSignUp( + withCardStateProvider(SignUpEnterpriseConnectionsInternal), +); diff --git a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx index ffc1e0c2dd9..a178468d6b2 100644 --- a/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/SignUpStart.tsx @@ -3,6 +3,7 @@ import { useClerk } from '@clerk/shared/react'; import type { PhoneCodeChannel, PhoneCodeChannelData, SignUpResource } from '@clerk/types'; import React from 'react'; +import { isClerkAPIResponseError } from '@/index.headless'; import { Card } from '@/ui/elements/Card'; import { useCardState, withCardStateProvider } from '@/ui/elements/contexts'; import { Header } from '@/ui/elements/Header'; @@ -355,7 +356,19 @@ function SignUpStartInternal(): JSX.Element { oidcPrompt, }), ) - .catch(err => handleError(err, fieldsToSubmit, card.setError)) + .catch(err => { + /** + * @experimental + */ + if ( + isClerkAPIResponseError(err) && + err.errors?.[0]?.code === 'enterprise_connection_id_is_required_with_multiple_connections' + ) { + return navigate('./enterprise-connections'); + } + + return handleError(err, fieldsToSubmit, card.setError); + }) .finally(() => card.setIdle()); }; diff --git a/packages/clerk-js/src/ui/components/SignUp/index.tsx b/packages/clerk-js/src/ui/components/SignUp/index.tsx index a46993739fd..e5167b6743e 100644 --- a/packages/clerk-js/src/ui/components/SignUp/index.tsx +++ b/packages/clerk-js/src/ui/components/SignUp/index.tsx @@ -10,6 +10,7 @@ import { SignUpContext, useSignUpContext, withCoreSessionSwitchGuard } from '../ import { Flow } from '../../customizables'; import { Route, Switch, VIRTUAL_ROUTER_BASE_PATH } from '../../router'; import { SignUpContinue } from './SignUpContinue'; +import { SignUpEnterpriseConnections } from './SignUpEnterpriseConnections'; import { SignUpSSOCallback } from './SignUpSSOCallback'; import { SignUpStart } from './SignUpStart'; import { SignUpVerifyEmail } from './SignUpVerifyEmail'; @@ -82,6 +83,9 @@ function SignUpRoutes(): JSX.Element { + + + diff --git a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts index a48d622df94..206bc658c73 100644 --- a/packages/clerk-js/src/ui/customizables/elementDescriptors.ts +++ b/packages/clerk-js/src/ui/customizables/elementDescriptors.ts @@ -505,6 +505,10 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([ 'subscriptionDetailsDetailRow', 'subscriptionDetailsDetailRowLabel', 'subscriptionDetailsDetailRowValue', + + 'enterpriseConnectionsRoot', + 'enterpriseConnectionButton', + 'enterpriseConnectionButtonText', ] as const).map(camelize) as (keyof ElementsConfig)[]; type TargettableClassname = `${typeof CLASS_PREFIX}${K}`; diff --git a/packages/clerk-js/src/ui/elements/contexts/index.tsx b/packages/clerk-js/src/ui/elements/contexts/index.tsx index 40e4e82262e..aa6c5227d48 100644 --- a/packages/clerk-js/src/ui/elements/contexts/index.tsx +++ b/packages/clerk-js/src/ui/elements/contexts/index.tsx @@ -123,7 +123,8 @@ export type FlowMetadata = { | 'popover' | 'complete' | 'accountSwitcher' - | 'chooseOrganization'; + | 'chooseOrganization' + | 'enterpriseConnections'; }; const [FlowMetadataCtx, useFlowMetadata] = createContextAndHook('FlowMetadata'); diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index c44053ebee9..4b7089a8f7a 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -714,6 +714,10 @@ export const enUS: LocalizationResource = { subtitle: 'To continue, please enter the verification code generated by your authenticator app', title: 'Two-step verification', }, + enterpriseConnections: { + subtitle: 'Select the enterprise account with which you wish to continue.', + title: 'Choose your enterprise account', + }, }, signInEnterPasswordTitle: 'Enter your password', signUp: { @@ -802,6 +806,10 @@ export const enUS: LocalizationResource = { title: 'Create your account', titleCombined: 'Create your account', }, + enterpriseConnections: { + subtitle: 'Select the enterprise account with which you wish to continue.', + title: 'Choose your enterprise account', + }, }, socialButtonsBlockButton: 'Continue with {{provider|titleize}}', socialButtonsBlockButtonManyInView: '{{provider|titleize}}', diff --git a/packages/types/src/appearance.ts b/packages/types/src/appearance.ts index 4330d90b008..e10f74badea 100644 --- a/packages/types/src/appearance.ts +++ b/packages/types/src/appearance.ts @@ -640,6 +640,10 @@ export type ElementsConfig = { subscriptionDetailsDetailRow: WithOptions; subscriptionDetailsDetailRowLabel: WithOptions; subscriptionDetailsDetailRowValue: WithOptions; + + enterpriseConnectionsRoot: WithOptions; + enterpriseConnectionButton: WithOptions; + enterpriseConnectionButtonText: WithOptions; }; export type Elements = { diff --git a/packages/types/src/factors.ts b/packages/types/src/factors.ts index 38c1a4e0d12..407b5904538 100644 --- a/packages/types/src/factors.ts +++ b/packages/types/src/factors.ts @@ -63,6 +63,14 @@ export type SamlFactor = { export type EnterpriseSSOFactor = { strategy: EnterpriseSSOStrategy; + /** + * @experimental + */ + enterpriseConnectionId?: string; + /** + * @experimental + */ + enterpriseConnectionName?: string; }; export type TOTPFactor = { @@ -116,6 +124,10 @@ export type EnterpriseSSOConfig = EnterpriseSSOFactor & { redirectUrl: string; actionCompleteRedirectUrl: string; oidcPrompt?: string; + /** + * @experimental + */ + enterpriseConnectionId?: string; }; export type PhoneCodeSecondFactorConfig = { diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index 3714242dc53..dc1fdee75e5 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -140,6 +140,14 @@ export interface SignUpJSON extends ClerkResourceJSON { verifications: SignUpVerificationsJSON | null; } +/** + * @experimental + */ +export interface SignUpEnterpriseConnectionJSON extends ClerkResourceJSON { + id: string; + name: string; +} + export interface SessionJSON extends ClerkResourceJSON { object: 'session'; id: string; diff --git a/packages/types/src/localization.ts b/packages/types/src/localization.ts index a9050305ec5..f007114741d 100644 --- a/packages/types/src/localization.ts +++ b/packages/types/src/localization.ts @@ -361,6 +361,10 @@ export type __internal_LocalizationResource = { label__onlyTermsOfService: LocalizationValue<'termsOfServiceLink'>; }; }; + enterpriseConnections: { + title: LocalizationValue; + subtitle: LocalizationValue; + }; }; signIn: { start: { @@ -515,6 +519,10 @@ export type __internal_LocalizationResource = { action__addAccount: LocalizationValue; action__signOutAll: LocalizationValue; }; + enterpriseConnections: { + title: LocalizationValue; + subtitle: LocalizationValue; + }; }; reverification: { password: { diff --git a/packages/types/src/redirects.ts b/packages/types/src/redirects.ts index 4b1e3833f22..5f81d7cce2a 100644 --- a/packages/types/src/redirects.ts +++ b/packages/types/src/redirects.ts @@ -91,6 +91,11 @@ export type AuthenticateWithRedirectParams = { * Optional for `oauth_` or `enterprise_sso` strategies. The value to pass to the [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.) in the generated OAuth redirect URL. */ oidcPrompt?: string; + + /** + * @experimental + */ + enterpriseConnectionId?: string; }; export type AuthenticateWithPopupParams = AuthenticateWithRedirectParams & { popup: Window | null }; diff --git a/packages/types/src/signUp.ts b/packages/types/src/signUp.ts index f6c9e9bd48b..8cfcc7debce 100644 --- a/packages/types/src/signUp.ts +++ b/packages/types/src/signUp.ts @@ -113,4 +113,17 @@ export interface SignUpResource extends ClerkResource { * @internal */ __internal_future: SignUpFutureResource; + + /** + * @experimental + */ + __experimental_getEnterpriseConnections: () => Promise; +} + +/** + * @experimental + */ +export interface SignUpEnterpriseConnectionResource extends ClerkResource { + id: string; + name: string; }