diff --git a/EXAMPLES.md b/EXAMPLES.md
index 20992283..9850cba2 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -24,6 +24,7 @@
- [Session Expiry from Upstream IdP (IPSIE)](#session-expiry-from-upstream-idp-ipsie)
- [Use Suspense for loading state (React 19+)](#use-suspense-for-loading-state-react-19)
- [Enterprise Connect](#enterprise-connect)
+- [Forcing an Experiment Center variant](#forcing-an-experiment-center-variant)
## Use with a Class Component
@@ -2112,4 +2113,71 @@ await logout({
```
The `returnTo` URL must be registered in the application's **Allowed Logout
-URLs** in the Auth0 Dashboard, or the logout redirect will be rejected.
\ No newline at end of file
+URLs** in the Auth0 Dashboard, or the logout redirect will be rejected.
+
+## Forcing an Experiment Center variant
+
+> [!NOTE]
+> [Experiment Center](https://auth0.com/docs/customize/experiment-center/overview) support via SDKs is currently in Early Access. To request access to this feature, contact your Auth0 representative.
+
+Experiment Center lets you A/B test your login flow. To force a specific variant - for testing or to apply a decision from a feature-flag service - pass `experiment_id` and `variation_id` via `authorizationParams`. Auth0 will use them instead of its server-side deterministic assignment. Both IDs are obtained from your Auth0 Dashboard or the Management API. You can also pass the optional `segment_id` when the experiment uses segment targeting.
+
+```jsx
+import { useAuth0 } from '@auth0/auth0-react';
+
+function LoginButton() {
+ const { loginWithRedirect } = useAuth0();
+
+ return (
+
+ );
+}
+```
+
+The same params work with `loginWithPopup`:
+
+```jsx
+import { useAuth0 } from '@auth0/auth0-react';
+
+function LoginButton() {
+ const { loginWithPopup } = useAuth0();
+
+ return (
+
+ );
+}
+```
+
+> [!IMPORTANT]
+> Pass these parameters per call on `loginWithRedirect` (or `loginWithPopup`), not on `Auth0Provider`'s `authorizationParams`. Setting them on the provider pins every login including silent `prompt=none` token-renewal calls - to the same variation, which cancels the A/B test. Experiment Center does not run on silent checks.
+
+- **Testing:** drive the IDs from test automation (e.g. Cypress/Playwright) using values from a CI environment variable against a staging tenant. Do not hard-code them in shipped app code.
+- **Production:** pass the variant decision from a feature-flag tool (e.g. LaunchDarkly) that has already decided which variant the user should see for this request.
+
+The override applies only to this request; the next login without these params reverts to normal server-side assignment.
diff --git a/README.md b/README.md
index 09b3a7c5..091671aa 100644
--- a/README.md
+++ b/README.md
@@ -141,6 +141,13 @@ function App() {
export default App;
```
+### Experiment Center
+
+> [!NOTE]
+> [Experiment Center](https://auth0.com/docs/customize/experiment-center) support via SDKs is currently in Early Access. To request access to this feature, contact your Auth0 representative.
+
+You can also force a specific Experiment Center variant per login by passing `experiment_id`, `variation_id`, and the optional `segment_id` in `authorizationParams` - see the [Experiment Center example](https://github.com/auth0/auth0-react/blob/main/EXAMPLES.md#forcing-an-experiment-center-variant).
+
For more code samples on how to integrate **auth0-react** SDK in your **React** application, have a look at our [examples](https://github.com/auth0/auth0-react/blob/main/EXAMPLES.md).
## API reference
diff --git a/__tests__/auth-provider.test.tsx b/__tests__/auth-provider.test.tsx
index d6775f63..74e63e34 100644
--- a/__tests__/auth-provider.test.tsx
+++ b/__tests__/auth-provider.test.tsx
@@ -491,6 +491,110 @@ describe('Auth0Provider', () => {
});
});
+ it('should forward Experiment Center params through loginWithRedirect', async () => {
+ const wrapper = createWrapper();
+ const { result } = renderHook(
+ () => useContext(Auth0Context),
+ { wrapper }
+ );
+ await waitFor(() => {
+ expect(result.current.loginWithRedirect).toBeInstanceOf(Function);
+ });
+ await result.current.loginWithRedirect({
+ authorizationParams: {
+ redirect_uri: '__redirect_uri__',
+ experiment_id: '__experiment_id__',
+ variation_id: '__variation_id__',
+ segment_id: '__segment_id__',
+ },
+ });
+ expect(clientMock.loginWithRedirect).toHaveBeenCalledWith({
+ authorizationParams: {
+ redirect_uri: '__redirect_uri__',
+ experiment_id: '__experiment_id__',
+ variation_id: '__variation_id__',
+ segment_id: '__segment_id__',
+ },
+ });
+ });
+
+ it('should forward Experiment Center params through loginWithRedirect when segment_id is omitted', async () => {
+ const wrapper = createWrapper();
+ const { result } = renderHook(
+ () => useContext(Auth0Context),
+ { wrapper }
+ );
+ await waitFor(() => {
+ expect(result.current.loginWithRedirect).toBeInstanceOf(Function);
+ });
+ await result.current.loginWithRedirect({
+ authorizationParams: {
+ experiment_id: '__experiment_id__',
+ variation_id: '__variation_id__',
+ },
+ });
+ expect(clientMock.loginWithRedirect).toHaveBeenCalledWith({
+ authorizationParams: {
+ experiment_id: '__experiment_id__',
+ variation_id: '__variation_id__',
+ },
+ });
+ });
+
+ it('should forward Experiment Center params through loginWithPopup', async () => {
+ const wrapper = createWrapper();
+ const { result } = renderHook(
+ () => useContext(Auth0Context),
+ { wrapper }
+ );
+ await waitFor(() => {
+ expect(result.current.loginWithPopup).toBeInstanceOf(Function);
+ });
+ await result.current.loginWithPopup({
+ authorizationParams: {
+ experiment_id: '__experiment_id__',
+ variation_id: '__variation_id__',
+ segment_id: '__segment_id__',
+ },
+ });
+ expect(clientMock.loginWithPopup).toHaveBeenCalledWith(
+ {
+ authorizationParams: {
+ experiment_id: '__experiment_id__',
+ variation_id: '__variation_id__',
+ segment_id: '__segment_id__',
+ },
+ },
+ undefined
+ );
+ });
+
+ it('should forward Experiment Center params through loginWithPopup when segment_id is omitted', async () => {
+ const wrapper = createWrapper();
+ const { result } = renderHook(
+ () => useContext(Auth0Context),
+ { wrapper }
+ );
+ await waitFor(() => {
+ expect(result.current.loginWithPopup).toBeInstanceOf(Function);
+ });
+ await result.current.loginWithPopup({
+ authorizationParams: {
+ experiment_id: '__experiment_id__',
+ variation_id: '__variation_id__',
+ },
+ });
+ expect(clientMock.loginWithPopup).toHaveBeenCalledWith(
+ {
+ authorizationParams: {
+ experiment_id: '__experiment_id__',
+ variation_id: '__variation_id__',
+ },
+ },
+ undefined
+ );
+ });
+
it('should provide a login method supporting redirectUri', async () => {
const warn = jest.spyOn(console, "warn").mockImplementation(() => undefined);
const wrapper = createWrapper();