diff --git a/.changeset/clever-cougars-argue.md b/.changeset/clever-cougars-argue.md new file mode 100644 index 0000000000..94e761410f --- /dev/null +++ b/.changeset/clever-cougars-argue.md @@ -0,0 +1,15 @@ +--- +'@clerk/clerk-js': minor +--- + +- Update the supported API version to `2024-10-01` that includes the following changes + - Notification for new sign ins to users' accounts feature becomes available. + - The response for Sign Ins with an email address that matches a **SAML connection** is updated. Instead of responding with a status of `needs_identifier` the API will now return a status of `needs_first_factor` and the email address that matched will be returned in the identifier field. the only strategy that will be included in supported first factors is `enterprise_sso` + + Read more in the [API Version docs](https://clerk.com/docs/backend-requests/versioning/available-versions#2024-10-01) + +- Update components to use the new `enterprise_sso` strategy for sign ins / sign ups that match an enterprise connection and handle the new API response. + + This strategy supersedes SAML to provide a single strategy as the entry point for Enterprise SSO regardless of the underlying protocol used to authenticate the user. + + For now there are two new types of connections that are supported in addition to SAML, Custom OAuth and EASIE (multi-tenant OAuth). diff --git a/.changeset/famous-experts-begin.md b/.changeset/famous-experts-begin.md new file mode 100644 index 0000000000..1342bbee6a --- /dev/null +++ b/.changeset/famous-experts-begin.md @@ -0,0 +1,13 @@ +--- +'@clerk/types': patch +--- + +Add support for the new `enterprise_sso` strategy. + +This strategy supersedes SAML to provide a single strategy as the entry point for Enterprise Single Sign On regardless of the underlying protocol used to authenticate the user. +For now there are two new types of connections that are supported in addition to SAML, Custom OAuth and EASIE (multi-tenant OAuth). + +- Add a new user setting `enterpriseSSO`, this gets enabled when there is an active enterprise connection for an instance. +- Add support for signing in / signing up with the new `enterprise_sso` strategy. +- Deprecated `userSettings.saml` in favor of `enterprise_sso`. +- Deprecated `saml` sign in strategy in favor of `enterprise_sso`. diff --git a/.changeset/short-mails-wash.md b/.changeset/short-mails-wash.md new file mode 100644 index 0000000000..fcfb1222a0 --- /dev/null +++ b/.changeset/short-mails-wash.md @@ -0,0 +1,9 @@ +--- +'@clerk/backend': patch +--- + +Update the supported API version to `2024-10-01` that includes the following changes + +No changes affecting the Backend API have been made in this version. + +Read more in the [API Version docs](https://clerk.com/docs/backend-requests/versioning/available-versions#2024-10-01) diff --git a/packages/backend/src/constants.ts b/packages/backend/src/constants.ts index 0e615aed61..210885ed3e 100644 --- a/packages/backend/src/constants.ts +++ b/packages/backend/src/constants.ts @@ -4,7 +4,7 @@ export const API_VERSION = 'v1'; export const USER_AGENT = `${PACKAGE_NAME}@${PACKAGE_VERSION}`; export const MAX_CACHE_LAST_UPDATED_AT_SECONDS = 5 * 60; export const JWKS_CACHE_TTL_MS = 1000 * 60 * 60; -export const SUPPORTED_BAPI_VERSION = '2021-02-05'; +export const SUPPORTED_BAPI_VERSION = '2024-10-01'; const Attributes = { AuthToken: '__clerkAuthToken', diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 8885f4bfc6..47e67ba8d0 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1382,7 +1382,8 @@ export class Clerk implements ClerkInterface { return navigateToSignIn(); } - const userHasUnverifiedEmail = si.status === 'needs_first_factor'; + const userHasUnverifiedEmail = + si.status === 'needs_first_factor' && !signIn.supportedFirstFactors?.every(f => f.strategy === 'enterprise_sso'); if (userHasUnverifiedEmail) { return navigateToFactorOne(); diff --git a/packages/clerk-js/src/core/constants.ts b/packages/clerk-js/src/core/constants.ts index ae34145cb6..4048385e63 100644 --- a/packages/clerk-js/src/core/constants.ts +++ b/packages/clerk-js/src/core/constants.ts @@ -46,4 +46,4 @@ export const SIGN_UP_MODES: Record = { }; // This is the currently supported version of the Frontend API -export const SUPPORTED_FAPI_VERSION = '2021-02-05'; +export const SUPPORTED_FAPI_VERSION = '2024-10-01'; diff --git a/packages/clerk-js/src/core/resources/SignIn.ts b/packages/clerk-js/src/core/resources/SignIn.ts index ce9efceead..2b5e210080 100644 --- a/packages/clerk-js/src/core/resources/SignIn.ts +++ b/packages/clerk-js/src/core/resources/SignIn.ts @@ -14,6 +14,7 @@ import type { CreateEmailLinkFlowReturn, EmailCodeConfig, EmailLinkConfig, + EnterpriseSSOConfig, PassKeyConfig, PasskeyFactor, PhoneCodeConfig, @@ -134,6 +135,12 @@ export class SignIn extends BaseResource implements SignInResource { actionCompleteRedirectUrl: factor.actionCompleteRedirectUrl, } as SamlConfig; break; + case 'enterprise_sso': + config = { + redirectUrl: factor.redirectUrl, + actionCompleteRedirectUrl: factor.actionCompleteRedirectUrl, + } as EnterpriseSSOConfig; + break; default: clerkInvalidStrategy('SignIn.prepareFirstFactor', factor.strategy); } @@ -215,7 +222,7 @@ export class SignIn extends BaseResource implements SignInResource { const { strategy, redirectUrl, redirectUrlComplete, identifier } = params || {}; const { firstFactorVerification } = - strategy === 'saml' && this.id + (strategy === 'saml' || strategy === 'enterprise_sso') && this.id ? await this.prepareFirstFactor({ strategy, redirectUrl: SignIn.clerk.buildUrlWithAuth(redirectUrl), diff --git a/packages/clerk-js/src/core/resources/UserSettings.ts b/packages/clerk-js/src/core/resources/UserSettings.ts index fa2248270e..0be9959eae 100644 --- a/packages/clerk-js/src/core/resources/UserSettings.ts +++ b/packages/clerk-js/src/core/resources/UserSettings.ts @@ -1,5 +1,6 @@ import type { Attributes, + EnterpriseSSOSettings, OAuthProviders, OAuthStrategy, PasskeySettingsData, @@ -30,6 +31,7 @@ export class UserSettings extends BaseResource implements UserSettingsResource { social!: OAuthProviders; saml!: SamlSettings; + enterpriseSSO!: EnterpriseSSOSettings; attributes!: Attributes; actions!: Actions; @@ -67,6 +69,7 @@ export class UserSettings extends BaseResource implements UserSettingsResource { this.social = data.social; this.saml = data.saml; + this.enterpriseSSO = data.enterprise_sso; this.attributes = Object.fromEntries( Object.entries(data.attributes).map(a => [a[0], { ...a[1], name: a[0] }]), ) as Attributes; diff --git a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx index a8f1a5478b..52052503f9 100644 --- a/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/SignInStart.tsx @@ -245,31 +245,34 @@ export function _SignInStart(): JSX.Element { const hasPassword = fields.some(f => f.name === 'password' && !!f.value); /** - * FAPI will return an error when password is submitted but the user's email matches requires SAML authentication. + * FAPI will return an error when password is submitted but the user's email matches requires enterprise sso authentication. * We need to strip password from the create request, and reconstruct it later. */ - if (!hasPassword || userSettings.saml.enabled) { + if (!hasPassword || userSettings.enterpriseSSO.enabled) { fields = fields.filter(f => f.name !== 'password'); } return { ...buildRequest(fields), - ...(hasPassword && !userSettings.saml.enabled && { strategy: 'password' }), + ...(hasPassword && !userSettings.enterpriseSSO.enabled && { strategy: 'password' }), } as SignInCreateParams; }; - const safePasswordSignInForSamlInstance = ( + const safePasswordSignInForEnterpriseSSOInstance = ( signInCreatePromise: Promise, fields: Array>, ) => { return signInCreatePromise.then(signInResource => { - if (!userSettings.saml.enabled) { + if (!userSettings.enterpriseSSO.enabled) { return signInResource; } /** - * For SAML enabled instances, perform sign in with password only when it is allowed for the identified user. + * For instances with Enterprise SSO enabled, perform sign in with password only when it is allowed for the identified user. */ const passwordField = fields.find(f => f.name === 'password')?.value; - if (!passwordField || signInResource.supportedFirstFactors?.some(ff => ff.strategy === 'saml')) { + if ( + !passwordField || + signInResource.supportedFirstFactors?.some(ff => ff.strategy === 'saml' || ff.strategy === 'enterprise_sso') + ) { return signInResource; } return signInResource.attemptFirstFactor({ strategy: 'password', password: passwordField }); @@ -278,16 +281,20 @@ export function _SignInStart(): JSX.Element { const signInWithFields = async (...fields: Array>) => { try { - const res = await safePasswordSignInForSamlInstance(signIn.create(buildSignInParams(fields)), fields); + const res = await safePasswordSignInForEnterpriseSSOInstance(signIn.create(buildSignInParams(fields)), fields); switch (res.status) { case 'needs_identifier': - // Check if we need to initiate a saml flow - if (res.supportedFirstFactors?.some(ff => ff.strategy === 'saml')) { - await authenticateWithSaml(); + // Check if we need to initiate an enterprise sso flow + if (res.supportedFirstFactors?.some(ff => ff.strategy === 'saml' || ff.strategy === 'enterprise_sso')) { + await authenticateWithEnterpriseSSO(); } break; case 'needs_first_factor': + if (res.supportedFirstFactors?.every(ff => ff.strategy === 'enterprise_sso')) { + await authenticateWithEnterpriseSSO(); + break; + } return navigate('factor-one'); case 'needs_second_factor': return navigate('factor-two'); @@ -306,12 +313,12 @@ export function _SignInStart(): JSX.Element { } }; - const authenticateWithSaml = async () => { + const authenticateWithEnterpriseSSO = async () => { const redirectUrl = buildSSOCallbackURL(ctx, displayConfig.signInUrl); const redirectUrlComplete = ctx.afterSignInUrl || '/'; return signIn.authenticateWithRedirect({ - strategy: 'saml', + strategy: 'enterprise_sso', redirectUrl, redirectUrlComplete, }); diff --git a/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx b/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx index a85f1f3be6..337f74a153 100644 --- a/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx +++ b/packages/clerk-js/src/ui/components/SignIn/__tests__/SignInStart.test.tsx @@ -286,7 +286,30 @@ describe('SignInStart', () => { await userEvent.click(screen.getByText('Continue')); expect(fixtures.signIn.create).toHaveBeenCalled(); expect(fixtures.signIn.authenticateWithRedirect).toHaveBeenCalledWith({ - strategy: 'saml', + strategy: 'enterprise_sso', + redirectUrl: 'http://localhost/#/sso-callback', + redirectUrlComplete: '/', + }); + }); + }); + + describe('Enterprise SSO', () => { + it('initiates a Enterprise SSO flow if enterprise_sso is listed as the only supported first factor', async () => { + const { wrapper, fixtures } = await createFixtures(f => { + f.withEmailAddress(); + }); + fixtures.signIn.create.mockReturnValueOnce( + Promise.resolve({ + status: 'needs_first_factor', + supportedFirstFactors: [{ strategy: 'enterprise_sso' }], + } as unknown as SignInResource), + ); + const { userEvent } = render(, { wrapper }); + await userEvent.type(screen.getByLabelText(/email address/i), 'hello@clerk.com'); + await userEvent.click(screen.getByText('Continue')); + expect(fixtures.signIn.create).toHaveBeenCalled(); + expect(fixtures.signIn.authenticateWithRedirect).toHaveBeenCalledWith({ + strategy: 'enterprise_sso', redirectUrl: 'http://localhost/#/sso-callback', redirectUrlComplete: '/', }); diff --git a/packages/clerk-js/src/ui/components/UserProfile/AccountPage.tsx b/packages/clerk-js/src/ui/components/UserProfile/AccountPage.tsx index 8588ed7a20..5e0f569478 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/AccountPage.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/AccountPage.tsx @@ -12,7 +12,7 @@ import { UserProfileSection } from './UserProfileSection'; import { Web3Section } from './Web3Section'; export const AccountPage = withCardStateProvider(() => { - const { attributes, social } = useEnvironment().userSettings; + const { attributes, social, enterpriseSSO } = useEnvironment().userSettings; const card = useCardState(); const { user } = useUser(); @@ -20,7 +20,7 @@ export const AccountPage = withCardStateProvider(() => { const showEmail = attributes.email_address.enabled; const showPhone = attributes.phone_number.enabled; const showConnectedAccounts = social && Object.values(social).filter(p => p.enabled).length > 0; - const showEnterpriseAccounts = user && user.enterpriseAccounts.length > 0; + const showEnterpriseAccounts = user && enterpriseSSO.enabled; const showWeb3 = attributes.web3_wallet.enabled; const shouldAllowIdentificationCreation = diff --git a/packages/clerk-js/src/ui/components/UserProfile/__tests__/AccountPage.test.tsx b/packages/clerk-js/src/ui/components/UserProfile/__tests__/AccountPage.test.tsx index c22ffc0637..9ee58dd871 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/__tests__/AccountPage.test.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/__tests__/AccountPage.test.tsx @@ -91,7 +91,7 @@ describe('AccountPage', () => { const { wrapper } = await createFixtures(f => { f.withEmailAddress(); - f.withSaml(); + f.withEnterpriseSso(); f.withUser({ email_addresses: [emailAddress], enterprise_accounts: [ @@ -157,6 +157,7 @@ describe('AccountPage', () => { const { wrapper } = await createFixtures(f => { f.withEmailAddress(); + f.withEnterpriseSso(); f.withUser({ email_addresses: [emailAddress], enterprise_accounts: [ @@ -268,7 +269,7 @@ describe('AccountPage', () => { f.withEmailAddress(); f.withPhoneNumber(); f.withSocialProvider({ provider: 'google' }); - f.withSaml(); + f.withEnterpriseSso(); f.withUser({ email_addresses: [emailAddress], enterprise_accounts: [enterpriseAccount], @@ -291,7 +292,7 @@ describe('AccountPage', () => { f.withEmailAddress(); f.withPhoneNumber(); f.withSocialProvider({ provider: 'google' }); - f.withSaml(); + f.withEnterpriseSso(); f.withUser({ email_addresses: [emailAddress], phone_numbers: [phoneNumber], diff --git a/packages/clerk-js/src/ui/components/UserProfile/__tests__/EnterpriseAccountsSection.test.tsx b/packages/clerk-js/src/ui/components/UserProfile/__tests__/EnterpriseAccountsSection.test.tsx index 0191ebafbd..67ba687f4b 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/__tests__/EnterpriseAccountsSection.test.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/__tests__/EnterpriseAccountsSection.test.tsx @@ -18,6 +18,7 @@ const withoutEnterpriseConnection = createFixtures.config(f => { const withInactiveEnterpriseConnection = createFixtures.config(f => { f.withSocialProvider({ provider: 'google' }); f.withSocialProvider({ provider: 'github' }); + f.withEnterpriseSso(); f.withUser({ enterprise_accounts: [ { @@ -67,6 +68,7 @@ const withInactiveEnterpriseConnection = createFixtures.config(f => { }); const withOAuthBuiltInEnterpriseConnection = createFixtures.config(f => { + f.withEnterpriseSso(); f.withUser({ enterprise_accounts: [ { @@ -117,6 +119,7 @@ const withOAuthBuiltInEnterpriseConnection = createFixtures.config(f => { const withOAuthCustomEnterpriseConnection = (logoPublicUrl: string | null) => createFixtures.config(f => { + f.withEnterpriseSso(); f.withUser({ enterprise_accounts: [ { @@ -166,6 +169,7 @@ const withOAuthCustomEnterpriseConnection = (logoPublicUrl: string | null) => }); const withSamlEnterpriseConnection = createFixtures.config(f => { + f.withEnterpriseSso(); f.withUser({ enterprise_accounts: [ { diff --git a/packages/clerk-js/src/ui/components/UserProfile/__tests__/PasswordSection.test.tsx b/packages/clerk-js/src/ui/components/UserProfile/__tests__/PasswordSection.test.tsx index a89d1f76bd..fbe5dac88d 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/__tests__/PasswordSection.test.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/__tests__/PasswordSection.test.tsx @@ -106,13 +106,13 @@ describe('PasswordSection', () => { }); }); - describe('with SAML', () => { + describe('with Enterprise SSO', () => { it('prevents setting a password if user has active enterprise connections', async () => { const emailAddress = 'george@jungle.com'; const config = createFixtures.config(f => { f.withEmailAddress(); - f.withSaml(); + f.withEnterpriseSso(); f.withUser({ email_addresses: [emailAddress], enterprise_accounts: [ @@ -185,7 +185,7 @@ describe('PasswordSection', () => { const config = createFixtures.config(f => { f.withEmailAddress(); - f.withSaml(); + f.withEnterpriseSso(); f.withUser({ email_addresses: [emailAddress], enterprise_accounts: [ @@ -315,13 +315,13 @@ describe('PasswordSection', () => { expect(queryByRole('heading', { name: /update password/i })).not.toBeInTheDocument(); }); - describe('with SAML', () => { + describe('with Enterprise SSO', () => { it('prevents changing a password if user has active enterprise connections', async () => { const emailAddress = 'george@jungle.com'; const config = createFixtures.config(f => { f.withEmailAddress(); - f.withSaml(); + f.withEnterpriseSso(); f.withUser({ password_enabled: true, email_addresses: [emailAddress], @@ -395,7 +395,7 @@ describe('PasswordSection', () => { const config = createFixtures.config(f => { f.withEmailAddress(); - f.withSaml(); + f.withEnterpriseSso(); f.withUser({ password_enabled: true, email_addresses: [emailAddress], diff --git a/packages/clerk-js/src/ui/components/UserProfile/__tests__/UserProfileSection.test.tsx b/packages/clerk-js/src/ui/components/UserProfile/__tests__/UserProfileSection.test.tsx index 75e076ab97..1c28747a32 100644 --- a/packages/clerk-js/src/ui/components/UserProfile/__tests__/UserProfileSection.test.tsx +++ b/packages/clerk-js/src/ui/components/UserProfile/__tests__/UserProfileSection.test.tsx @@ -51,7 +51,7 @@ describe('ProfileSection', () => { }); }); - describe('with SAML', () => { + describe('with Enterprise SSO', () => { it('disables the first & last name inputs if user has active enterprise connections', async () => { const emailAddress = 'george@jungle.com'; const firstName = 'George'; @@ -59,7 +59,7 @@ describe('ProfileSection', () => { const config = createFixtures.config(f => { f.withEmailAddress(); - f.withSaml(); + f.withEnterpriseSso(); f.withName(); f.withUser({ first_name: firstName, @@ -134,7 +134,7 @@ describe('ProfileSection', () => { const config = createFixtures.config(f => { f.withEmailAddress(); - f.withSaml(); + f.withEnterpriseSso(); f.withName(); f.withUser({ first_name: firstName, diff --git a/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts b/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts index cfed51ac42..4edbdaa623 100644 --- a/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts +++ b/packages/clerk-js/src/ui/utils/test/fixtureHelpers.ts @@ -466,8 +466,9 @@ const createUserSettingsFixtureHelpers = (environment: EnvironmentJSON) => { }; }; - const withSaml = () => { + const withEnterpriseSso = () => { us.saml = { enabled: true }; + us.enterprise_sso = { enabled: true }; }; const withBackupCode = (opts?: Partial) => { @@ -524,7 +525,7 @@ const createUserSettingsFixtureHelpers = (environment: EnvironmentJSON) => { withPassword, withPasswordComplexity, withSocialProvider, - withSaml, + withEnterpriseSso, withBackupCode, withAuthenticatorApp, withPasskey, diff --git a/packages/clerk-js/src/ui/utils/test/fixtures.ts b/packages/clerk-js/src/ui/utils/test/fixtures.ts index 5547cddd4c..a7cf180205 100644 --- a/packages/clerk-js/src/ui/utils/test/fixtures.ts +++ b/packages/clerk-js/src/ui/utils/test/fixtures.ts @@ -179,6 +179,7 @@ const createBaseUserSettings = (): UserSettingsJSON => { actions: { delete_self: false, create_organization: false }, social: { ...socialConfig }, saml: { enabled: false }, + enterprise_sso: { enabled: false }, sign_in: { second_factor: { required: false, diff --git a/packages/clerk-js/src/utils/__tests__/completeSignUpFlow.test.ts b/packages/clerk-js/src/utils/__tests__/completeSignUpFlow.test.ts index dab6d593c6..f4c1fcb560 100644 --- a/packages/clerk-js/src/utils/__tests__/completeSignUpFlow.test.ts +++ b/packages/clerk-js/src/utils/__tests__/completeSignUpFlow.test.ts @@ -104,7 +104,35 @@ describe('completeSignUpFlow', () => { expect(mockHandleComplete).not.toHaveBeenCalled(); expect(mockNavigate).not.toHaveBeenCalled(); expect(mockAuthenticateWithRedirect).toHaveBeenCalledWith({ - strategy: 'saml', + strategy: 'enterprise_sso', + redirectUrl, + redirectUrlComplete, + continueSignUp: true, + }); + }); + + it('initiates a Enterprise SSO flow if enterprise_sso is listed as a missing field', async () => { + const redirectUrl = 'https://www.in.gr/acs'; + const redirectUrlComplete = 'https://www.in.gr/tada'; + + const mockSignUp = { + status: 'missing_requirements', + missingFields: ['enterprise_sso'], + authenticateWithRedirect: mockAuthenticateWithRedirect, + } as unknown as SignUpResource; + + await completeSignUpFlow({ + signUp: mockSignUp, + handleComplete: mockHandleComplete, + navigate: mockNavigate, + redirectUrl: 'https://www.in.gr/acs', + redirectUrlComplete: 'https://www.in.gr/tada', + }); + + expect(mockHandleComplete).not.toHaveBeenCalled(); + expect(mockNavigate).not.toHaveBeenCalled(); + expect(mockAuthenticateWithRedirect).toHaveBeenCalledWith({ + strategy: 'enterprise_sso', redirectUrl, redirectUrlComplete, continueSignUp: true, diff --git a/packages/clerk-js/src/utils/completeSignUpFlow.ts b/packages/clerk-js/src/utils/completeSignUpFlow.ts index 6d9742ad30..baf951eba2 100644 --- a/packages/clerk-js/src/utils/completeSignUpFlow.ts +++ b/packages/clerk-js/src/utils/completeSignUpFlow.ts @@ -22,9 +22,9 @@ export const completeSignUpFlow = ({ if (signUp.status === 'complete') { return handleComplete && handleComplete(); } else if (signUp.status === 'missing_requirements') { - if (signUp.missingFields.some(mf => mf === 'saml')) { + if (signUp.missingFields.some(mf => mf === 'saml' || mf === 'enterprise_sso')) { return signUp.authenticateWithRedirect({ - strategy: 'saml', + strategy: 'enterprise_sso', redirectUrl, redirectUrlComplete, continueSignUp: true, diff --git a/packages/types/src/factors.ts b/packages/types/src/factors.ts index ae435e3efb..1eb79ecfb9 100644 --- a/packages/types/src/factors.ts +++ b/packages/types/src/factors.ts @@ -3,6 +3,7 @@ import type { BackupCodeStrategy, EmailCodeStrategy, EmailLinkStrategy, + EnterpriseSSOStrategy, OAuthStrategy, PasskeyStrategy, PasswordStrategy, @@ -58,6 +59,10 @@ export type SamlFactor = { strategy: SamlStrategy; }; +export type EnterpriseSSOFactor = { + strategy: EnterpriseSSOStrategy; +}; + export type TOTPFactor = { strategy: TOTPStrategy; }; @@ -103,6 +108,11 @@ export type SamlConfig = SamlFactor & { actionCompleteRedirectUrl: string; }; +export type EnterpriseSSOConfig = EnterpriseSSOFactor & { + redirectUrl: string; + actionCompleteRedirectUrl: string; +}; + export type PhoneCodeSecondFactorConfig = { strategy: PhoneCodeStrategy; phoneNumberId?: string; diff --git a/packages/types/src/redirects.ts b/packages/types/src/redirects.ts index ece1d6abf8..346127eb24 100644 --- a/packages/types/src/redirects.ts +++ b/packages/types/src/redirects.ts @@ -1,4 +1,4 @@ -import type { OAuthStrategy, SamlStrategy } from './strategies'; +import type { EnterpriseSSOStrategy, OAuthStrategy, SamlStrategy } from './strategies'; export type AfterSignOutUrl = { /** @@ -66,17 +66,17 @@ export type AuthenticateWithRedirectParams = { /** * One of the supported OAuth providers you can use to authenticate with, eg 'oauth_google'. - * Or alternatively `saml`, to authenticate with SAML. + * Alternatively `saml` or `enterprise_sso`, to authenticate with Enterprise SSO. */ - strategy: OAuthStrategy | SamlStrategy; + strategy: OAuthStrategy | SamlStrategy | EnterpriseSSOStrategy; /** - * Identifier to use for targeting a SAML connection at sign-in + * Identifier to use for targeting a Enterprise Connection at sign-in */ identifier?: string; /** - * Email address to use for targeting a SAML connection at sign-up + * Email address to use for targeting a Enterprise Connection at sign-up */ emailAddress?: string; diff --git a/packages/types/src/signIn.ts b/packages/types/src/signIn.ts index 7a3b4dcd16..136150917e 100644 --- a/packages/types/src/signIn.ts +++ b/packages/types/src/signIn.ts @@ -6,6 +6,8 @@ import type { EmailCodeFactor, EmailLinkConfig, EmailLinkFactor, + EnterpriseSSOConfig, + EnterpriseSSOFactor, OAuthConfig, OauthFactor, PasskeyAttempt, @@ -51,6 +53,7 @@ import type { BackupCodeStrategy, EmailCodeStrategy, EmailLinkStrategy, + EnterpriseSSOStrategy, GoogleOneTapStrategy, OAuthStrategy, PasskeyStrategy, @@ -130,7 +133,8 @@ export type SignInFirstFactor = | ResetPasswordEmailCodeFactor | Web3SignatureFactor | OauthFactor - | SamlFactor; + | SamlFactor + | EnterpriseSSOFactor; export type SignInSecondFactor = PhoneCodeFactor | TOTPFactor | BackupCodeFactor; @@ -152,7 +156,8 @@ export type PrepareFirstFactorParams = | ResetPasswordPhoneCodeFactorConfig | ResetPasswordEmailCodeFactorConfig | OAuthConfig - | SamlConfig; + | SamlConfig + | EnterpriseSSOConfig; export type AttemptFirstFactorParams = | PasskeyAttempt @@ -169,7 +174,7 @@ export type AttemptSecondFactorParams = PhoneCodeAttempt | TOTPAttempt | BackupC export type SignInCreateParams = ( | { - strategy: OAuthStrategy | SamlStrategy; + strategy: OAuthStrategy | SamlStrategy | EnterpriseSSOStrategy; redirectUrl: string; actionCompleteRedirectUrl?: string; identifier?: string; @@ -234,7 +239,8 @@ export type SignInStrategy = | TOTPStrategy | BackupCodeStrategy | OAuthStrategy - | SamlStrategy; + | SamlStrategy + | EnterpriseSSOStrategy; export interface SignInJSON extends ClerkResourceJSON { object: 'sign_in'; diff --git a/packages/types/src/signUp.ts b/packages/types/src/signUp.ts index 5b9d183c9d..d608d452fb 100644 --- a/packages/types/src/signUp.ts +++ b/packages/types/src/signUp.ts @@ -14,6 +14,7 @@ import type { ClerkResource } from './resource'; import type { EmailCodeStrategy, EmailLinkStrategy, + EnterpriseSSOStrategy, GoogleOneTapStrategy, OAuthStrategy, PhoneCodeStrategy, @@ -150,7 +151,7 @@ export type SignUpVerifiableField = | Web3WalletIdentifier; // TODO: Does it make sense that the identification *field* holds a *strategy*? -export type SignUpIdentificationField = SignUpVerifiableField | OAuthStrategy | SamlStrategy; +export type SignUpIdentificationField = SignUpVerifiableField | OAuthStrategy | SamlStrategy | EnterpriseSSOStrategy; // TODO: Replace with discriminated union type export type SignUpCreateParams = Partial< @@ -158,7 +159,7 @@ export type SignUpCreateParams = Partial< externalAccountStrategy: string; externalAccountRedirectUrl: string; externalAccountActionCompleteRedirectUrl: string; - strategy: OAuthStrategy | SamlStrategy | TicketStrategy | GoogleOneTapStrategy; + strategy: OAuthStrategy | SamlStrategy | EnterpriseSSOStrategy | TicketStrategy | GoogleOneTapStrategy; redirectUrl: string; actionCompleteRedirectUrl: string; transfer: boolean; diff --git a/packages/types/src/strategies.ts b/packages/types/src/strategies.ts index c46801542d..0b73fb8849 100644 --- a/packages/types/src/strategies.ts +++ b/packages/types/src/strategies.ts @@ -13,8 +13,12 @@ export type BackupCodeStrategy = 'backup_code'; export type ResetPasswordPhoneCodeStrategy = 'reset_password_phone_code'; export type ResetPasswordEmailCodeStrategy = 'reset_password_email_code'; export type CustomOAuthStrategy = `oauth_custom_${string}`; +export type EnterpriseSSOStrategy = 'enterprise_sso'; export type OAuthStrategy = `oauth_${OAuthProvider}` | CustomOAuthStrategy; export type Web3Strategy = `web3_${Web3Provider}_signature`; +/** + * @deprecated Use `EnterpriseSSOStrategy` instead + */ export type SamlStrategy = 'saml'; diff --git a/packages/types/src/userSettings.ts b/packages/types/src/userSettings.ts index 847109d801..f6f9fbd1bb 100644 --- a/packages/types/src/userSettings.ts +++ b/packages/types/src/userSettings.ts @@ -83,6 +83,10 @@ export type SamlSettings = { enabled: boolean; }; +export type EnterpriseSSOSettings = { + enabled: boolean; +}; + export type AttributesJSON = { [attribute in Attribute]: AttributeDataJSON; }; @@ -103,7 +107,11 @@ export interface UserSettingsJSON extends ClerkResourceJSON { actions: Actions; social: OAuthProviders; + /** + * @deprecated Use `enterprise_sso` instead + */ saml: SamlSettings; + enterprise_sso: EnterpriseSSOSettings; sign_in: SignInData; sign_up: SignUpData; @@ -115,7 +123,11 @@ export interface UserSettingsResource extends ClerkResource { id?: undefined; social: OAuthProviders; + /** + * @deprecated Use `enterprise_sso` instead + */ saml: SamlSettings; + enterpriseSSO: EnterpriseSSOSettings; attributes: Attributes; actions: Actions;