> ## Documentation Index
> Fetch the complete documentation index at: https://developer.box.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Run the application

export const ProgressBar = ({pages = [], ...props}) => {
  const [currentStep, setCurrentStep] = useState(0);
  const [isDarkMode, setIsDarkMode] = useState(false);
  useEffect(() => {
    const checkDarkMode = () => {
      const isDark = document.documentElement.classList.contains('dark');
      console.log('ProgressBar - isDarkMode:', isDark);
      setIsDarkMode(isDark);
    };
    checkDarkMode();
    const observer = new MutationObserver(() => {
      checkDarkMode();
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => {
      observer.disconnect();
    };
  }, []);
  useEffect(() => {
    if (pages.length > 0) {
      const currentPath = window.location.pathname;
      const stepIndex = pages.findIndex(page => {
        const pagePath = page.startsWith('/') ? page : `/${page}`;
        return currentPath.endsWith(pagePath) || currentPath.includes(pagePath);
      });
      if (stepIndex !== -1) {
        setCurrentStep(stepIndex + 1);
      }
    }
  }, [pages]);
  if (!pages || pages.length === 0) {
    return null;
  }
  const step = currentStep;
  const total = pages.length;
  console.log('ProgressBar - Rendering with isDarkMode:', isDarkMode);
  const progressBarContainerStyle = {
    width: '100%',
    marginBottom: '32px',
    display: 'flex',
    alignItems: 'center',
    gap: '16px'
  };
  const stepsContainerStyle = {
    display: 'flex',
    alignItems: 'center',
    gap: '8px',
    flexShrink: 0
  };
  const progressBarTrackStyle = {
    flex: 1,
    height: '22px',
    backgroundColor: 'rgba(169, 210, 244, 0.06)',
    border: isDarkMode ? '1px solid rgba(230, 241, 247, 0.67)' : '1px solid #e3ecf3',
    borderRadius: '4px',
    overflow: 'hidden',
    position: 'relative'
  };
  const progressBarFillStyle = {
    height: '100%',
    backgroundColor: 'rgba(113, 192, 248, 0.23)',
    width: `${step / total * 100}%`,
    transition: 'width 0.3s ease'
  };
  const getStepStyle = (stepNumber, isActive) => {
    if (isDarkMode) {
      return {
        width: '22px',
        height: '22px',
        borderRadius: '4px',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: '12px',
        fontWeight: '600',
        position: 'relative',
        zIndex: 1,
        transition: 'all 0.3s ease',
        backgroundColor: isActive ? 'rgba(113, 192, 248, 0.23)' : 'transparent',
        color: isActive ? '#60a5fa' : '#a0aec0',
        border: isActive ? '1px solid #e3ecf3' : '1px solid #e0e6eb',
        cursor: 'pointer',
        textDecoration: 'none'
      };
    }
    if (isActive) {
      return {
        width: '22px',
        height: '22px',
        borderRadius: '4px',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: '12px',
        fontWeight: '600',
        position: 'relative',
        zIndex: 1,
        transition: 'all 0.3s ease',
        backgroundColor: 'rgba(169, 210, 244, 0.32)',
        color: '#374151',
        border: '1px solid #e1eef8',
        cursor: 'pointer',
        textDecoration: 'none'
      };
    }
    return {
      width: '22px',
      height: '22px',
      borderRadius: '4px',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      fontSize: '12px',
      fontWeight: '600',
      position: 'relative',
      zIndex: 1,
      transition: 'all 0.3s ease',
      backgroundColor: '#fbfbfb',
      color: '#9ca3af',
      border: '1px solid #e3ecf3',
      cursor: 'pointer',
      textDecoration: 'none'
    };
  };
  return <div style={progressBarContainerStyle} {...props}>
      <div style={stepsContainerStyle}>
        {Array.from({
    length: total
  }, (_, index) => {
    const stepNumber = index + 1;
    const pageIndex = index;
    const pagePath = pages[pageIndex];
    const fullPath = pagePath.startsWith('/') ? pagePath : `/${pagePath}`;
    const isActive = stepNumber === step;
    return <a key={stepNumber} href={fullPath} style={getStepStyle(stepNumber, isActive)}>
              {stepNumber}
            </a>;
  })}
      </div>
      <div style={progressBarTrackStyle}>
        <div style={progressBarFillStyle}></div>
      </div>
    </div>;
};

export const Link = ({href, children, className, ...props}) => {
  const localizedHref = localizeLink(href);
  return <a href={localizedHref} className={className} {...props}>
      {children}
    </a>;
};

export const ChoiceDebug = ({option}) => {
  const [currentValue, setCurrentValue] = useState(null);
  const [allState, setAllState] = useState({});
  const [isDarkMode, setIsDarkMode] = useState(false);
  useEffect(() => {
    const updateState = () => {
      if (window.choiceStateManager) {
        setCurrentValue(window.choiceStateManager.getValue(option));
        setAllState(window.choiceStateManager.getState());
      }
    };
    updateState();
    const unsubscribe = window.listenToChoice?.(option, updateState) || (() => {});
    const handleGlobalUpdate = () => updateState();
    window.addEventListener("choiceStateUpdate", handleGlobalUpdate);
    return () => {
      unsubscribe();
      window.removeEventListener("choiceStateUpdate", handleGlobalUpdate);
    };
  }, [option]);
  useEffect(() => {
    const checkDarkMode = () => {
      if (document.documentElement.classList.contains("dark")) {
        setIsDarkMode(true);
      } else if (document.documentElement.classList.contains("light")) {
        setIsDarkMode(false);
      } else {
        setIsDarkMode(window.matchMedia("(prefers-color-scheme: dark)").matches);
      }
    };
    checkDarkMode();
    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    const handleMediaChange = e => {
      if (!document.documentElement.classList.contains("dark") && !document.documentElement.classList.contains("light")) {
        setIsDarkMode(e.matches);
      }
    };
    mediaQuery.addEventListener("change", handleMediaChange);
    const observer = new MutationObserver(() => {
      checkDarkMode();
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => {
      mediaQuery.removeEventListener("change", handleMediaChange);
      observer.disconnect();
    };
  }, []);
  return <div style={{
    padding: "10px",
    backgroundColor: isDarkMode ? "#2d3748" : "#f5f5f5",
    border: isDarkMode ? "1px solid #4a5568" : "1px solid #ddd",
    borderRadius: "4px",
    fontSize: "12px",
    fontFamily: "monospace",
    marginTop: "20px",
    color: isDarkMode ? "#e2e8f0" : "inherit"
  }}>
      <strong>Choice Debug:</strong>
      <br />
      Option: {option}
      <br />
      Current Value: {currentValue || "undefined"}
      <br />
      All State: {JSON.stringify(allState, null, 2)}
    </div>;
};

export const Observe = ({option, value, children, ...props}) => {
  const [shouldShow, setShouldShow] = useState(false);
  useEffect(() => {
    const updateVisibility = () => {
      const matches = window.matchesChoiceValues?.(option, value) || false;
      setShouldShow(matches);
    };
    updateVisibility();
    const unsubscribe = window.listenToChoice?.(option, updateVisibility) || (() => {});
    return unsubscribe;
  }, [option, value]);
  if (!shouldShow) {
    return null;
  }
  return <div {...props}>{children}</div>;
};

export const Trigger = ({option, value, children, ...props}) => {
  const handleClick = () => {
    window.triggerChoice?.(option, value);
  };
  return <div onClick={handleClick} style={{
    cursor: "pointer"
  }} {...props}>
      {children}
    </div>;
};

export const Grid = ({columns = 2, compact = false, children, ...props}) => {
  const gridStyles = {
    display: "grid",
    gridTemplateColumns: `repeat(${columns}, 1fr)`,
    gap: compact ? "8px" : "16px",
    marginBottom: compact ? "10px" : "20px"
  };
  return <div style={gridStyles} {...props}>
      {children}
    </div>;
};

export const Choice = ({option, value, color = "", unset = false, lazy = false, children, ...props}) => {
  const [shouldShow, setShouldShow] = useState(false);
  const [hasEverShown, setHasEverShown] = useState(false);
  const [isDarkMode, setIsDarkMode] = useState(false);
  useEffect(() => {
    const updateVisibility = () => {
      const hasOptionValue = window.hasChoiceValue?.(option) || false;
      const matchesValue = window.matchesChoiceValues?.(option, value) || false;
      let show = false;
      if (unset && !hasOptionValue) {
        show = true;
      } else if (!unset && matchesValue) {
        show = true;
      }
      setShouldShow(show);
      if (show && !hasEverShown) {
        setHasEverShown(true);
      }
    };
    updateVisibility();
    const unsubscribe = window.listenToChoice?.(option, updateVisibility) || (() => {});
    return unsubscribe;
  }, [option, value, unset, hasEverShown]);
  useEffect(() => {
    const checkDarkMode = () => {
      if (document.documentElement.classList.contains("dark")) {
        setIsDarkMode(true);
      } else if (document.documentElement.classList.contains("light")) {
        setIsDarkMode(false);
      } else {
        setIsDarkMode(window.matchMedia("(prefers-color-scheme: dark)").matches);
      }
    };
    checkDarkMode();
    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    const handleMediaChange = e => {
      if (!document.documentElement.classList.contains("dark") && !document.documentElement.classList.contains("light")) {
        setIsDarkMode(e.matches);
      }
    };
    mediaQuery.addEventListener("change", handleMediaChange);
    const observer = new MutationObserver(() => {
      checkDarkMode();
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => {
      mediaQuery.removeEventListener("change", handleMediaChange);
      observer.disconnect();
    };
  }, []);
  const getColorStyles = () => {
    const baseStyles = {
      border: isDarkMode ? "1px dashed #4a5568" : "1px dashed #e1e5e9",
      padding: "20px",
      marginBottom: "20px",
      borderRadius: "8px",
      backgroundColor: isDarkMode ? "#1a202c" : "#ffffff"
    };
    const colorMap = {
      green: {
        light: {
          backgroundColor: "#d4edda",
          borderColor: "#28a745"
        },
        dark: {
          backgroundColor: "#1a3a2a",
          borderColor: "#66bb6a"
        }
      },
      red: {
        light: {
          backgroundColor: "#f8d7da",
          borderColor: "#dc3545"
        },
        dark: {
          backgroundColor: "#3a1a1a",
          borderColor: "#ef5350"
        }
      },
      blue: {
        light: {
          backgroundColor: "#d1ecf1",
          borderColor: "#0c5460"
        },
        dark: {
          backgroundColor: "#1a2a3a",
          borderColor: "#42a5f5"
        }
      },
      none: {
        backgroundColor: "transparent",
        padding: "0",
        margin: "0",
        border: "none"
      }
    };
    const colorStyles = color !== "none" ? colorMap[color]?.[isDarkMode ? "dark" : "light"] || ({}) : colorMap.none;
    return {
      ...baseStyles,
      ...colorStyles
    };
  };
  if (lazy && !hasEverShown && !shouldShow) {
    return null;
  }
  return <div style={{
    ...getColorStyles(),
    display: shouldShow ? "block" : "none"
  }} className="choice-content" {...props}>
      {children}
    </div>;
};

export const Choose = ({option, value, color = "", children, ...props}) => {
  const [isSelected, setIsSelected] = useState(false);
  const [hasOptionTriggered, setHasOptionTriggered] = useState(false);
  const [isDarkMode, setIsDarkMode] = useState(false);
  useEffect(() => {
    const currentValue = window.getChoiceValue?.(option);
    const optionTriggered = window.hasChoiceValue?.(option) || false;
    setIsSelected(currentValue === value);
    setHasOptionTriggered(optionTriggered);
    const unsubscribe = window.listenToChoice?.(option, newValue => {
      setIsSelected(newValue === value);
      setHasOptionTriggered(true);
    }) || (() => {});
    return unsubscribe;
  }, [option, value]);
  useEffect(() => {
    const checkDarkMode = () => {
      if (document.documentElement.classList.contains("dark")) {
        setIsDarkMode(true);
      } else if (document.documentElement.classList.contains("light")) {
        setIsDarkMode(false);
      } else {
        setIsDarkMode(window.matchMedia("(prefers-color-scheme: dark)").matches);
      }
    };
    checkDarkMode();
    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    const handleMediaChange = e => {
      if (!document.documentElement.classList.contains("dark") && !document.documentElement.classList.contains("light")) {
        setIsDarkMode(e.matches);
      }
    };
    mediaQuery.addEventListener("change", handleMediaChange);
    const observer = new MutationObserver(() => {
      checkDarkMode();
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => {
      mediaQuery.removeEventListener("change", handleMediaChange);
      observer.disconnect();
    };
  }, []);
  const handleClick = () => {
    window.triggerChoice?.(option, value);
  };
  const handleKeyDown = e => {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      handleClick();
    }
  };
  const getColorStyles = () => {
    const baseStyles = {
      border: isDarkMode ? "1px dashed #4a5568" : "1px dashed #e1e5e9",
      cursor: "pointer",
      padding: "20px",
      position: "relative",
      backgroundColor: isDarkMode ? "#2d3748" : "#f8f9fa",
      outline: "none",
      height: "100%",
      borderRadius: "8px",
      transition: "all 0.2s ease",
      display: "flex",
      flexDirection: "column"
    };
    const colorMap = {
      green: {
        light: {
          backgroundColor: "#d4edda",
          borderColor: "#28a745"
        },
        dark: {
          backgroundColor: "#1a3a2a",
          borderColor: "#66bb6a"
        }
      },
      red: {
        light: {
          backgroundColor: "#f8d7da",
          borderColor: "#dc3545"
        },
        dark: {
          backgroundColor: "#3a1a1a",
          borderColor: "#ef5350"
        }
      },
      blue: {
        light: {
          backgroundColor: "#d1ecf1",
          borderColor: "#0c5460"
        },
        dark: {
          backgroundColor: "#1a2a3a",
          borderColor: "#42a5f5"
        }
      }
    };
    const colorStyles = colorMap[color]?.[isDarkMode ? "dark" : "light"] || ({});
    if (isSelected) {
      return {
        ...baseStyles,
        ...colorStyles,
        borderStyle: "solid",
        borderWidth: "3px",
        borderColor: colorStyles.borderColor || (isDarkMode ? "#42a5f5" : "#0061d5"),
        backgroundColor: colorStyles.backgroundColor || (isDarkMode ? "#1a2a3a" : "#e3f2fd"),
        boxShadow: isDarkMode ? "0 2px 8px rgba(66, 165, 245, 0.3)" : "0 2px 8px rgba(0, 97, 213, 0.3)",
        transform: "scale(1.02)"
      };
    }
    if (hasOptionTriggered && !isSelected) {
      return {
        ...baseStyles,
        ...colorStyles,
        opacity: 0.5
      };
    }
    return {
      ...baseStyles,
      ...colorStyles
    };
  };
  const iconStyles = {
    float: "left",
    position: "relative",
    top: "2px",
    marginRight: "12px",
    width: "20px",
    height: "20px",
    color: isSelected ? isDarkMode ? "#42a5f5" : "#0061d5" : isDarkMode ? "#a0aec0" : "#666"
  };
  return <div onClick={handleClick} style={getColorStyles()} tabIndex={0} onKeyDown={handleKeyDown} {...props}>
      <div style={iconStyles}>
        {isSelected ? <svg viewBox="0 0 24 24" fill="currentColor" style={{
    width: "100%",
    height: "100%"
  }}>
            <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
          </svg> : <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" style={{
    width: "100%",
    height: "100%"
  }}>
            <circle cx="12" cy="12" r="10" />
          </svg>}
      </div>
      <div style={{
    flex: 1
  }} className="choose-content">
        {children}
      </div>
    </div>;
};

<ProgressBar
  pages={[
                      "guides/sso-identities-and-app-users/connect-okta-to-app-users/scaffold-application-code",
                      "guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-okta",
                      "guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-box",
                      "guides/sso-identities-and-app-users/connect-okta-to-app-users/logging-into-app",
                      "guides/sso-identities-and-app-users/connect-okta-to-app-users/find-or-create-box-users",
                      "guides/sso-identities-and-app-users/connect-okta-to-app-users/run-the-app"
                    ]}
/>

With all the components of the sample in place, it's time to run our program to
see if everything is working correctly.

<Choice option="programming.platform" value="node" color="none">
  From the terminal / command prompt in the local application directory, type
  `node server.js` and click enter. The server will start up and output
  `Server started: Listening on port 3000`.

  From your browser, go to `http://localhost:3000/`. Since this is the first time
  we'll be testing out the user sign in flow, you'll see the Okta login.

  <Frame noborder center shadow>
    <img src="https://mintcdn.com/box/K3l9SyfYdcG-z-Ku/guides/sso-identities-and-app-users/connect-okta-to-app-users/img/okta-qs-step6-login.png?fit=max&auto=format&n=K3l9SyfYdcG-z-Ku&q=85&s=24a7fb970c32ad4cc5928abf6f6d2fbc" alt="Okta Login" width="463" height="611" data-path="guides/sso-identities-and-app-users/connect-okta-to-app-users/img/okta-qs-step6-login.png" />
  </Frame>

  Sign in with the credentials for the Okta user you created in the last section
  of <Link href="/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-okta">step 2</Link>.

  <br />

  Once signed in, you will see a message stating
  `New user created: {{USERNAME}}` output to the browser.

  <br />

  When attempting to log in with this user in subsequent attempts, you
  will now see `Hello {{USERNAME}}` output to the browser.
</Choice>

<Choice option="programming.platform" value="java" color="none">
  From Eclipse (or your preferred editor) click to run the application. You will
  see console output stating that the Spring boot application is now running on
  port 8080.

  From your browser, go to `http://localhost:8080/`. Since this is the first time
  we'll be testing out the user sign in flow, you'll see the Okta login.

  <Frame noborder center shadow>
    <img src="https://mintcdn.com/box/K3l9SyfYdcG-z-Ku/guides/sso-identities-and-app-users/connect-okta-to-app-users/img/okta-qs-step6-login.png?fit=max&auto=format&n=K3l9SyfYdcG-z-Ku&q=85&s=24a7fb970c32ad4cc5928abf6f6d2fbc" alt="Okta Login" width="463" height="611" data-path="guides/sso-identities-and-app-users/connect-okta-to-app-users/img/okta-qs-step6-login.png" />
  </Frame>

  Sign in with the credentials for the Okta user you created in the last section
  of <Link href="/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-okta">step 2</Link>.

  <br />

  Once signed in, you will see a message stating `New User Created: {{USERNAME}}`
  output to the browser.

  <br />

  When attempting to log in with this user in subsequent attempts, you
  will now see `Hello {{USERNAME}}` output to the browser.
</Choice>

<Choice option="programming.platform" value="python" color="none">
  From the terminal / command prompt in the local application directory, type
  `env FLASK_APP=server.py flask run` and click enter. The server will start up
  and output `Running on http://127.0.0.1:5000/`.

  From your browser, go to `http://127.0.0.1:5000/`. Since this is the first time
  we'll be testing out the user sign in flow, you'll see the Okta login.

  <Frame noborder center shadow>
    <img src="https://mintcdn.com/box/K3l9SyfYdcG-z-Ku/guides/sso-identities-and-app-users/connect-okta-to-app-users/img/okta-qs-step6-login.png?fit=max&auto=format&n=K3l9SyfYdcG-z-Ku&q=85&s=24a7fb970c32ad4cc5928abf6f6d2fbc" alt="Okta Login" width="463" height="611" data-path="guides/sso-identities-and-app-users/connect-okta-to-app-users/img/okta-qs-step6-login.png" />
  </Frame>

  Sign in with the credentials for the Okta user you created in the last section
  of <Link href="/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-okta">step 2</Link>.

  <br />

  Once signed in, you will see a message stating
  `New user created: {{USERNAME}}` output to the browser.

  <br />

  When attempting to log in with this user in subsequent attempts, you
  will now see `Hello {{USERNAME}}` output to the browser.
</Choice>

<Choice option="programming.platform" value="cs" color="none">
  From Visual Studio Code (or your preferred editor) click `Run` ->
  `Start Debugging` from the menu. You will see debug console output as the
  application loads, then a browser window will be loaded with the application at
  `https://localhost:5001/`.

  Since this is the first time testing the application, the sign in link will be
  displayed. Once clicked, the Okta login will be automatically loaded.

  <Frame noborder center shadow>
    <img src="https://mintcdn.com/box/K3l9SyfYdcG-z-Ku/guides/sso-identities-and-app-users/connect-okta-to-app-users/img/okta-qs-step6-login.png?fit=max&auto=format&n=K3l9SyfYdcG-z-Ku&q=85&s=24a7fb970c32ad4cc5928abf6f6d2fbc" alt="Okta Login" width="463" height="611" data-path="guides/sso-identities-and-app-users/connect-okta-to-app-users/img/okta-qs-step6-login.png" />
  </Frame>

  Sign in with the credentials for the Okta user you created in the last section
  of <Link href="/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-okta">step 2</Link>.

  <br />

  Once signed in, you will see a message in the debug console stating
  `New user created: {{USERNAME}}` output to the browser.

  <br />

  When attempting to log in with this user in subsequent attempts, you
  will now see `Current user name: {{USERNAME}}` output to the browser.
</Choice>

<Choice option="programming.platform" unset color="none">
  <Danger>
    **Incomplete previous step**
    Please select a preferred language / framework in step 1 to get started.
  </Danger>
</Choice>

## Summary

You've reached the end of this Quick Start guide. By now you should have taken
the following steps.

1. <Link href="/guides/sso-identities-and-app-users/connect-okta-to-app-users/scaffold-application-code">Created a web application scaffold</Link>.
2. <Link href="/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-okta">Setup and configured an Okta application</Link> and created a first user that could be used to log into the web app.
3. <Link href="/guides/sso-identities-and-app-users/connect-okta-to-app-users/configure-box">Setup and configured a Box application</Link> so that the web app can connect to Box.
4. <Link href="/guides/sso-identities-and-app-users/connect-okta-to-app-users/logging-into-app">Created a log in flow for the web application</Link>, allowing the Okta user to log in.
5. <Link href="/guides/sso-identities-and-app-users/connect-okta-to-app-users/find-or-create-box-users">Added a check to find an existing Box user</Link>, and optionally create a Box user the first time that Okta user logs into the web application.
6. <Link href="/guides/sso-identities-and-app-users/connect-okta-to-app-users/run-the-app">And finally, ran the application</Link> and saw the complete flow in action.

## Next Steps

We recommend the following resource for anyone who wants to learn more about
more advanced capabilities that may be attached into the user creation and
access process.

* <Link href="/guides/users/provision">User provisioning</Link> best practices for advanced user folder architecture setup.
* <Link href="/guides/users/deprovision">User deprovisioning</Link> best practices cleaning up inactive users and transferring user content to another account.
* <Link href="/guides/uploads">Uploading content</Link> into Box, including running preflight checks and large file (chunked) uploading.
