SAML connections
In case our default SAML flow doesn’t cover your needs, you can leverage the Clerk SDK to build a completely custom SAML flow.
You still need to configure your instance through the Clerk Dashboard. You can create a SAML connection by visiting the Enterprise Connections(opens in a new tab) page.
When using SAML, the sign-in and sign-up are equivalent. A successful SAML flow consists of the following steps:
- Start the OAuth flow by calling
SignIn.authenticateWithRedirect(params)
orSignUp.authenticateWithRedirect(params)
. Note that both of these methods require aredirectUrl
param, which is the URL that the browser will be redirected to once the user authenticates with the OAuth provider. - Create a route at the URL
redirectUrl
points, typically/sso-callback
, that calls theClerk.handleRedirectCallback()
or simply renders the prebuilt<AuthenticateWithRedirectCallback/>
component.
The React example below uses react-router-dom
to define the required route. For NextJS apps, you only need to create a pages/sso-callback
file.
import React from "react"; import { BrowserRouter, Switch, Route, } from "react-router-dom"; import { SamlStrategy } from "@clerk/types"; import { ClerkProvider, ClerkLoaded, AuthenticateWithRedirectCallback, UserButton, useSignIn, SignedIn, SignedOut } from "@clerk/clerk-react"; const publishableKey = process.env.REACT_APP_CLERK_PUBLISHABLE_KEY; function App() { return ( // react-router-dom requires your app to be wrapped with a Router <BrowserRouter> <ClerkProvider publishableKey={publishableKey}> <Switch> {/* Define a / route that displays the OAuth buttons */} <Route path="/"> <SignedOut> <SignInSAMLButton /> </SignedOut> <SignedIn> <UserButton afterSignOutAllUrl="/" /> </SignedIn> </Route> {/* Define a /sso-callback route that handles the SAML redirect flow from the IdP */} <Route path="/sso-callback"> <SSOCallback /> </Route> </Switch> </ClerkProvider> </BrowserRouter> ); } function SSOCallback() { // Handle the redirect flow by rendering the // prebuilt AuthenticateWithRedirectCallback component. // This is the final step in the custom SAML flow return <AuthenticateWithRedirectCallback />; } function SignInSAMLButton() { const { signIn } = useSignIn(); const signInWith = (strategy: SamlStrategy) => { return signIn.authenticateWithRedirect({ identifier: "email_goes_here", strategy: strategy, redirectUrl: "/sso-callback", redirectUrlComplete: "/", }); }; // Render a button for each supported SAML provider // you want to add to your app return ( <div> <button onClick={() => signInWith("saml")}> Sign in with SAML </button> </div> ); } export default App;
To initiate a SAMLflow for a user that is already signed in, you can use the user.createExternalAccount(params)
method, where user
is a reference to the currently signed in user.
user .createExternalAccount({ strategy: strategy, redirect_url: 'your-redirect-url' }) .then(externalAccount => { // navigate to // externalAccount.verification!.externalVerificationRedirectURL }) .catch(err => { // handle error });
SAML account transfer flow
When a user initiates a SAML SSO verification during sign-in, or sign-up, it may sometimes be necessary to move the verification between the two flows.
For example, if someone already has an account, and tries to go through the sign up flow with the same SAML account, they can’t perform a successful sign up again. Or, if someone attempts to sign in with their SAML credentials but does not yet have an account, they won’t be signed in to the account. For these scenarios, we have “account transfers.”
// Get sign-in and sign-up objects in the SAML callback page const { signIn, signUp } = window.Clerk.client; // If the user has an account in your application, but does not yet // have a saml account connected, you can use the transfer method to // forward that information. const userExistsButNeedsToSignIn = signUp.verifications.externalAccount.status === "transferable" && signUp.verifications.externalAccount.error?.code === "external_account_exists"; if (userExistsButNeedsToSignIn) { const res = await signIn.create({ transfer: true }); if (res.status === "complete") { window.Clerk.setActive({ session: res.createdSessionId, }); } } // If the user has an existing saml account but does not yet exist as // a user in your app, you can use the transfer method to forward that // information. const userNeedsToBeCreated = signIn.firstFactorVerification.status === "transferable"; if (userNeedsToBeCreated) { const res = await signUp.create({ transfer: true, }); if (res.status === "complete") { window.Clerk.setActive({ session: res.createdSessionId, }); } }