Skip to content

Commit

Permalink
Support enterprise_sso strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
LauraBeatris committed Jan 16, 2025
1 parent 93e30ce commit 7cc0576
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 25 deletions.
6 changes: 6 additions & 0 deletions .changeset/quiet-dingos-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@clerk/elements': patch
'@clerk/types': patch
---

Support `enterprise_sso` strategy (SAML, OIDC, EASIE) on custom flows with `@clerk/elements`
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export const SignInRouterMachine = setup({
},
})),
},
'AUTHENTICATE.SAML': {
'AUTHENTICATE.ENTERPRISE_SSO': {
actions: sendTo(ThirdPartyMachineId, ({ context }) => ({
type: 'REDIRECT',
params: {
Expand Down
16 changes: 16 additions & 0 deletions packages/elements/src/internals/machines/sign-up/router.machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,22 @@ export const SignUpRouterMachine = setup({
},
})),
},
'AUTHENTICATE.ENTERPRISE_SSO': {
actions: sendTo(ThirdPartyMachineId, ({ context }) => ({
type: 'REDIRECT',
params: {
strategy: 'enterprise_sso',
emailAddress: context.formRef.getSnapshot().context.fields.get('emailAddress')?.value,
redirectUrl: `${
context.router?.mode === ROUTING.virtual
? context.clerk.__unstable__environment?.displayConfig.signUpUrl
: context.router?.basePath
}${SSO_CALLBACK_PATH_ROUTE}`,
redirectUrlComplete:
context.router?.searchParams().get('redirect_url') || context.clerk.buildAfterSignUpUrl(),
},
})),
},
'AUTHENTICATE.WEB3': {
actions: sendTo('start', ({ event }) => event),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ClerkAPIResponseError } from '@clerk/shared/error';
import type { OAuthStrategy, SamlStrategy, Web3Strategy } from '@clerk/types';
import type { EnterpriseSSOStrategy, OAuthStrategy, SamlStrategy, Web3Strategy } from '@clerk/types';
import type { ActorRefFrom, ErrorActorEvent } from 'xstate';

import type { FormMachine } from '~/internals/machines/form';
Expand All @@ -18,12 +18,17 @@ export type SignUpStartSubmitEvent = { type: 'SUBMIT'; action: 'submit' };
// TODO: Consolidate with SignInStartMachine
export type SignUpStartRedirectOauthEvent = { type: 'AUTHENTICATE.OAUTH'; strategy: OAuthStrategy };
export type SignUpStartRedirectSamlEvent = { type: 'AUTHENTICATE.SAML'; strategy?: SamlStrategy };
export type SignUpStartRedirectEnterpriseSSOEvent = {
type: 'AUTHENTICATE.ENTERPRISE_SSO';
strategy?: EnterpriseSSOStrategy;
};
export type SignUpStartRedirectWeb3Event = { type: 'AUTHENTICATE.WEB3'; strategy: Web3Strategy };

export type SignUpStartRedirectEvent =
| SignUpStartRedirectOauthEvent
| SignUpStartRedirectSamlEvent
| SignUpStartRedirectWeb3Event;
| SignUpStartRedirectWeb3Event
| SignUpStartRedirectEnterpriseSSOEvent;

export type SignUpStartEvents = ErrorActorEvent | SignUpStartSubmitEvent | SignUpStartRedirectEvent | SetFormEvent;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ClerkRouter } from '@clerk/shared/router';
import type {
ClerkResource,
EnterpriseSSOStrategy,
LoadedClerk,
OAuthStrategy,
SamlStrategy,
Expand Down Expand Up @@ -46,13 +47,18 @@ export type BaseRouterLoadingEvent<TSteps extends BaseRouterLoadingStep> = (

export type BaseRouterRedirectOauthEvent = { type: 'AUTHENTICATE.OAUTH'; strategy: OAuthStrategy };
export type BaseRouterRedirectSamlEvent = { type: 'AUTHENTICATE.SAML'; strategy?: SamlStrategy };
export type BaseRouterRedirectEnterpriseSSOEvent = {
type: 'AUTHENTICATE.ENTERPRISE_SSO';
strategy?: EnterpriseSSOStrategy;
};
export type BaseRouterRedirectWeb3Event = { type: 'AUTHENTICATE.WEB3'; strategy: Web3Strategy };
export type BaseRouterSetClerkEvent = { type: 'CLERK.SET'; clerk: LoadedClerk };

export type BaseRouterRedirectEvent =
| BaseRouterRedirectOauthEvent
| BaseRouterRedirectSamlEvent
| BaseRouterRedirectWeb3Event;
| BaseRouterRedirectWeb3Event
| BaseRouterRedirectEnterpriseSSOEvent;

// ---------------------------------- Input ---------------------------------- //

Expand Down
4 changes: 2 additions & 2 deletions packages/elements/src/react/common/connections.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { OAuthProvider, SamlStrategy, Web3Provider } from '@clerk/types';
import type { EnterpriseSSOStrategy, OAuthProvider, SamlStrategy, Web3Provider } from '@clerk/types';
import { Slot } from '@radix-ui/react-slot';
import { createContext, useContext } from 'react';

Expand Down Expand Up @@ -29,7 +29,7 @@ export const useConnectionContext = () => {

export interface ConnectionProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
asChild?: boolean;
name: OAuthProvider | Web3Provider | SamlStrategy;
name: OAuthProvider | Web3Provider | SamlStrategy | EnterpriseSSOStrategy;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/elements/src/react/common/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useClerk } from '@clerk/shared/react';
import { eventComponentMounted } from '@clerk/shared/telemetry';
import type { OAuthProvider, SamlStrategy, Web3Provider } from '@clerk/types';
import type { EnterpriseSSOStrategy, OAuthProvider, SamlStrategy, Web3Provider } from '@clerk/types';
import { useSelector } from '@xstate/react';
import * as React from 'react';

Expand All @@ -15,7 +15,7 @@ import type { TSignUpStep } from '~/react/sign-up/step';
import { SIGN_UP_STEPS } from '~/react/sign-up/step';
import { isProviderStrategyScope, mapScopeToStrategy } from '~/react/utils/map-scope-to-strategy';

type Strategy = OAuthProvider | SamlStrategy | Web3Provider;
type Strategy = OAuthProvider | SamlStrategy | EnterpriseSSOStrategy | Web3Provider;
type LoadingScope<T extends TSignInStep | TSignUpStep> =
| 'global'
| `step:${T}`
Expand Down
37 changes: 25 additions & 12 deletions packages/elements/src/react/hooks/use-third-party-provider.hook.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useClerk } from '@clerk/shared/react';
import type { OAuthProvider, SamlStrategy, Web3Provider } from '@clerk/types';
import type { EnterpriseSSOStrategy, OAuthProvider, SamlStrategy, Web3Provider } from '@clerk/types';
import type React from 'react';
import { useCallback } from 'react';
import type { ActorRef } from 'xstate';
Expand All @@ -11,21 +11,28 @@ import type { UseThirdPartyProviderReturn } from '~/react/common/connections';
import {
getEnabledThirdPartyProviders,
isAuthenticatableOauthStrategy,
isEnterpriseSSOStrategy,
isSamlStrategy,
isWeb3Strategy,
providerToDisplayData,
} from '~/utils/third-party-strategies';

const useIsProviderEnabled = (provider: OAuthProvider | Web3Provider | SamlStrategy): boolean | null => {
const useIsProviderEnabled = (
provider: OAuthProvider | Web3Provider | SamlStrategy | EnterpriseSSOStrategy,
): boolean | null => {
const clerk = useClerk();

// null indicates we don't know for sure
if (!clerk.loaded) {
return null;
}

if (provider === 'saml') {
return clerk.__unstable__environment?.userSettings.saml.enabled ?? false;
if (provider === 'saml' || provider === 'enterprise_sso') {
return (
clerk.__unstable__environment?.userSettings.saml.enabled ??
clerk.__unstable__environment?.userSettings.enterpriseSSO.enabled ??
false
);
}

const data = getEnabledThirdPartyProviders(clerk.__unstable__environment);
Expand All @@ -40,16 +47,18 @@ export const useThirdPartyProvider = <
TActor extends ActorRef<any, SignInRouterEvents> | ActorRef<any, SignUpRouterEvents>,
>(
ref: TActor,
provider: OAuthProvider | Web3Provider | SamlStrategy,
provider: OAuthProvider | Web3Provider | SamlStrategy | EnterpriseSSOStrategy,
): UseThirdPartyProviderReturn => {
const isProviderEnabled = useIsProviderEnabled(provider);
const isSaml = isSamlStrategy(provider);
const details = isSaml
? {
name: 'SAML',
strategy: 'saml' as SamlStrategy,
}
: providerToDisplayData[provider];
const isEnterpriseSSO = isEnterpriseSSOStrategy(provider);
const details =
isEnterpriseSSO || isSaml
? {
name: 'SSO',
strategy: provider,
}
: providerToDisplayData[provider];

const authenticate = useCallback(
(event: React.MouseEvent<Element>) => {
Expand All @@ -63,6 +72,10 @@ export const useThirdPartyProvider = <
return ref.send({ type: 'AUTHENTICATE.SAML' });
}

if (isEnterpriseSSO) {
return ref.send({ type: 'AUTHENTICATE.ENTERPRISE_SSO' });
}

if (provider === 'metamask') {
return ref.send({ type: 'AUTHENTICATE.WEB3', strategy: 'web3_metamask_signature' });
}
Expand All @@ -77,7 +90,7 @@ export const useThirdPartyProvider = <

return ref.send({ type: 'AUTHENTICATE.OAUTH', strategy: `oauth_${provider}` });
},
[provider, isProviderEnabled, isSaml, ref],
[provider, isProviderEnabled, isSaml, isEnterpriseSSO, ref],
);

if (isProviderEnabled === false) {
Expand Down
8 changes: 6 additions & 2 deletions packages/elements/src/react/utils/map-scope-to-strategy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { OAuthProvider, SamlStrategy, SignInStrategy, Web3Provider } from '@clerk/types';
import type { EnterpriseSSOStrategy, OAuthProvider, SamlStrategy, SignInStrategy, Web3Provider } from '@clerk/types';

type Strategy = OAuthProvider | SamlStrategy | Web3Provider;
type Strategy = OAuthProvider | SamlStrategy | EnterpriseSSOStrategy | Web3Provider;

export function isProviderStrategyScope(value: string): value is Strategy {
return value.startsWith('provider:');
Expand All @@ -21,6 +21,10 @@ export function mapScopeToStrategy<T extends `provider:${Strategy}`>(scope: T):
return 'saml';
}

if (scope === 'provider:enterprise_sso') {
return 'enterprise_sso';
}

const scopeWithoutPrefix = scope.replace('provider:', '') as OAuthProvider;

return `oauth_${scopeWithoutPrefix}`;
Expand Down
9 changes: 7 additions & 2 deletions packages/elements/src/utils/third-party-strategies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { iconImageUrl } from '@clerk/shared/constants';
import { OAUTH_PROVIDERS } from '@clerk/shared/oauth';
import { WEB3_PROVIDERS } from '@clerk/shared/web3';
import type {
EnterpriseSSOStrategy,
EnvironmentResource,
OAuthProvider,
OAuthStrategy,
Expand All @@ -21,7 +22,7 @@ export type ThirdPartyStrategy =
name: string;
}
| {
strategy: SamlStrategy;
strategy: SamlStrategy | EnterpriseSSOStrategy;
iconUrl?: never;
name: string;
};
Expand All @@ -33,7 +34,7 @@ export type ThirdPartyProvider =
name: string;
}
| {
strategy: SamlStrategy;
strategy: SamlStrategy | EnterpriseSSOStrategy;
iconUrl?: never;
name: string;
};
Expand Down Expand Up @@ -72,6 +73,10 @@ export function isSamlStrategy(strategy: any): strategy is SamlStrategy {
return strategy === 'saml';
}

export function isEnterpriseSSOStrategy(strategy: any): strategy is EnterpriseSSOStrategy {
return strategy === 'enterprise_sso';
}

export function isWeb3Strategy(
strategy: any,
available: EnabledThirdPartyProviders['web3Strategies'],
Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/signUp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export type PrepareVerificationParams =
oidcLoginHint?: string;
}
| {
strategy: SamlStrategy;
strategy: SamlStrategy | EnterpriseSSOStrategy;
redirectUrl?: string;
actionCompleteRedirectUrl?: string;
};
Expand Down

0 comments on commit 7cc0576

Please sign in to comment.