> ## 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.

# Logging into the app with Okta

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 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 the Okta, Box, and basic application set up, we can turn our attention to
the first step in the application code flow, the Okta login.

During the Okta login we will employ the OpenID Connect (OIDC) frameworks of
the language used to redirect the user to Okta to log in and pass Okta user
information back to the application. Those Okta user details will in turn be
used to validate and create Box users in the next step.

This section will walk you through:

* Setting up the application configuration skeleton.
* Defining the routes for the chosen framework to handle user traffic.
* Passing Okta user information to the next Box user validation step.

## Set up the Skeleton

<Choice option="programming.platform" value="node" color="none">
  In your local application directory, load the `server.js` file created in
  step 1.

  Start by copying the following package definitions and configuration
  information into the file.

  ```js theme={null}
  const session = require('express-session');
  const { ExpressOIDC } = require('@okta/oidc-middleware');
  const bodyParser = require('body-parser');
  const boxSDK = require('box-node-sdk');
  const config = require('./config.js');
  const express = require('express')();
  const http = require('http');
  const path = require('path');
  const fs = require('fs');

  express.use(session({
      secret: 'this should be secure',
      resave: true,
      saveUninitialized: false
  }));

  const oidc = new ExpressOIDC({
      issuer: `https://${config.oktaOrgUrl}/oauth2/default`,
      client_id: config.oktaClientId,
      client_secret: config.oktaClientSecret,
      appBaseUrl: config.oktaBaseUrl,
      loginRedirectUri: `${config.oktaBaseUrl}${config.oktaRedirect}`,
      scope: 'openid profile'
  });

  express.use(oidc.router);
  express.use(bodyParser.json());
  express.use(bodyParser.urlencoded({
      extended: true
  }));
  ```

  This sets up the Express configuration and Okta OIDC connector
  information. Express is set to use the OIDC connector and the Okta
  information that we saved in step 2 of this quick start is used to configure
  the connector for our Okta integration.

  Now add the routing details.

  ```js theme={null}
  // Redirect to Okta login
  express.get('/', (req, res) => {
      // TODO: HANDLE ROUTE
  });
  ```

  This defines the entry route for our application. When a user attempts to
  visit our application root (`/`) the code within this route will be run.

  Lastly, add the Express server initialization to listen for traffic.

  ```js theme={null}
  // Create server
  const port = process.env.PORT || 3000;
  http.createServer(express).listen(port, () => {
      console.log(`Server started: Listening on port ${port}`);
  });
  ```
</Choice>

<Choice option="programming.platform" value="java" color="none">
  In your local application directory, load the
  `/src/main/java/com/box/sample/Application.java` file created in step 1, or
  similar directory if an alternate application name was used.

  Copy the following basic application structure into the file.

  ```java theme={null}
  package com.box.okta.sample;

  import java.io.FileReader;
  import java.io.IOException;
  import java.io.Reader;
  import java.net.URL;

  import org.springframework.boot.SpringApplication;
  import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  import org.springframework.security.core.annotation.AuthenticationPrincipal;
  import org.springframework.security.oauth2.core.oidc.user.OidcUser;
  import org.springframework.web.bind.annotation.RequestMapping;
  import org.springframework.web.bind.annotation.RestController;

  import com.box.sdk.BoxAPIRequest;
  import com.box.sdk.BoxConfig;
  import com.box.sdk.BoxDeveloperEditionAPIConnection;
  import com.box.sdk.BoxJSONResponse;
  import com.box.sdk.BoxUser;
  import com.box.sdk.CreateUserParams;
  import com.eclipsesource.json.JsonArray;
  import com.eclipsesource.json.JsonObject;
  import com.eclipsesource.json.JsonValue;

  @RestController
  @EnableAutoConfiguration
  public class Application {
      static BoxDeveloperEditionAPIConnection api;

      // TODO: SET ROUTE

      // TODO: INITIALIZE SERVER
  }
  ```

  This sets up the needed imports, the `Application` class, and a standard shared
  Box API connection attribute, to be defined in the next step.

  Replace `// TODO: SET ROUTE` with the following.

  ```java theme={null}
  @RequestMapping("/")
  String home(@AuthenticationPrincipal OidcUser user) throws IOException {
    // TODO: HANDLE ROUTE
  }
  ```

  The route mapping defines the entry route for our application. When a user
  attempts to visit our application root (`/`) in a logged out state, the OIDC
  connector will automatically push them through the Okta login, so we don't need
  to setup a redirect. When the user is in a logged in state, the code within
  this route will be run.

  Replace `// TODO: INITIALIZE SERVER` with the following to initialize the
  Spring Boot server to listen for traffic.

  ```java theme={null}
  public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
  }
  ```
</Choice>

<Choice option="programming.platform" value="python" color="none">
  In your local application directory, load the `server.py` file created in step 1.

  Copy the following basic application structure into the file.

  ```python theme={null}
  from flask import Flask, redirect, g, url_for
  from flask_oidc import OpenIDConnect
  from okta import UsersClient
  from boxsdk import Client
  from boxsdk import JWTAuth
  import requests
  import config
  import json

  app = Flask(__name__)
  app.config.update({
      'SECRET_KEY': config.okta_client_secret,
      'OIDC_CLIENT_SECRETS': './client_secrets.json',
      'OIDC_DEBUG': True,
      'OIDC_ID_TOKEN_COOKIE_SECURE': False,
      'OIDC_SCOPES': ["openid", "profile"],
      'OIDC_CALLBACK_ROUTE': config.okta_callback_route
  })

  oidc = OpenIDConnect(app)
  okta_client = UsersClient(config.okta_org_url, config.okta_auth_token)
  ```

  This sets up the Flask configuration, Okta client, and Okta OIDC
  connector information. Flask is set to use the OIDC connector and the Okta
  information that we saved in step 2 of this quick start is used to configure
  the connector for our Okta integration.

  Next, add a `before_request` definition to be run before route handling is
  engaged. We'll be using this to capture our Okta user information, if available.

  ```python theme={null}
  # Fetch Okta user record if logged in
  @app.before_request
  def before_request():
    # TODO: HANDLE BEFORE REQUEST
  ```

  Lastly, define the entry route for our application, as well as a `box_auth`
  route.

  ```python theme={null}
  # Main application route
  @app.route('/')
  def start():
    # TODO: HANDLE MAIN ROUTE

  # Box user verification
  @app.route("/box_auth")
  @oidc.require_login
  def box_auth():
    # TODO: HANDLE BOX AUTH ROUTE

  return 'Complete'
  ```

  When a user attempts to visit our application root (`/`) the code
  within this route will be run. When we validate an Okta user, the code within
  the `box_auth` route will be run.
</Choice>

<Choice option="programming.platform" value="cs" color="none">
  In your local application, load `Views` > `Shared` > `Layout.cshtml`. Once the
  ASP.NET application loads this will be the visual component that is rendered.
  At the top of the page, insert the following.

  ```csharp theme={null}
  @using System.Security.Claims;

  @if (User.Identity.IsAuthenticated)
  {
      <p class="navbar-text">Hello, @User.Identity.Name</p>
  }
  else
  {
      <a asp-controller="Account" asp-action="SignIn">Sign In</a>
  }
  ```

  If a user who is logged in to Okta visits, they will see the hello message. If
  the user is not logged in a sign in link will be provided to them.

  Within the line `<a asp-controller="Account" asp-action="SignIn">Sign In</a>`,
  `asp-controller="Account"` means that the to be created Account controller will
  handle the request, and `asp-action="SignIn"` states that the `SignIn` method
  in that controller will be enacted. Save and close that file.

  Within the `Controllers` directory create a new file, `AccountController.cs`.
  This will be the controller that is enacted when that sign in link is clicked.

  Copy the following basic application structure into the file.

  ```csharp theme={null}
  using System.IO;
  using System.Linq;
  using System.Threading.Tasks;
  using Microsoft.AspNetCore.Mvc;
  using Microsoft.AspNetCore.Authorization;
  using Okta.AspNetCore;
  using Box.V2;
  using Box.V2.Config;
  using Box.V2.JWTAuth;
  using Box.V2.Models;

  public class AccountController : Controller
  {
      public IActionResult SignIn()
      {
          if (!HttpContext.User.Identity.IsAuthenticated)
          {
              return Challenge(OktaDefaults.MvcAuthenticationScheme);
          }

          return RedirectToAction("Profile", "Account");
      }

      [Authorize]
      [Route("~/profile")]
      public IActionResult Profile()
      {
          // TODO: HANDLE ROUTE
      }
  }
  ```

  When the user clicks on the sign in link the `SignIn` method in this controller
  will be run. If the user is not already authenticated they will be sent to
  `Challenge`, which will redirect the user to Okta to log in. This functionality
  is handled by the routing framework and does not require any additional code to
  enact. If the user is authenticated, they will be redirected to the `Profile`
  routing method.
</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>

## Setup Application Route

We now need to define the code that will run when our main route (`/`) is
engaged.

<Choice option="programming.platform" value="node" color="none">
  Replace `// TODO: HANDLE ROUTE` in the main route with the following code.

  ```js theme={null}
  if (req.userContext && req.userContext.userinfo) {
      const tokenSet = req.userContext.tokens;
      const userInfo = req.userContext.userinfo;

      // If Okta ID is present, pass to Box user validation
      if (userInfo.sub) {
          box.validateUser(userInfo, res);
      } else {
          console.log('No Okta ID identified');
      }
  } else {
      res.redirect('/login');
  }
  ```

  What we are doing in the above is first checking to see if there is any Okta
  user information available from the OIDC connector. When a user logs in the
  connector will make the Okta user and configuration information available to
  our route within `req.userContext`.

  If user information is present, meaning the user is logged in to Okta, we pass
  the user information to `box.validateUser` along with the Express response
  object to see if there is an associated Box user available, which we'll define
  in the next step.

  If no user information is present, we redirect the user to `/login`. The OIDC
  connector will automatically handle this route and force the user through to
  the Okta login.
</Choice>

<Choice option="programming.platform" value="java" color="none">
  Replace `// TODO: HANDLE MAIN ROUTE` in the main route with the following code.

  ```java theme={null}
  // Validate OIDC user against Box
  return validateUser(user);
  ```

  The Java OIDC connector handles most of the heavy lifting for us. When a logged
  out user accesses this route they will automatically be pushed to the Okta
  login. Once logged in, an OIDC user object will be made available to the route.
  We pass that user object to a `validateUser` function, which we'll define in
  the next step.
</Choice>

<Choice option="programming.platform" value="python" color="none">
  Replace `// TODO: HANDLE BEFORE REQUEST` in the main route with the following code.

  ```python theme={null}
  if oidc.user_loggedin:
      g.user = okta_client.get_user(oidc.user_getfield('sub'))
  else:
      g.user = None
  ```

  This will check if an OIDC user is available, meaning that the user has already
  logged in to Okta. If available we set a user object using the Okta client
  object, and if not we set the user object to `None`.

  Next, replace `// TODO: HANDLE ROUTE` in the main route with the following code.

  ```python theme={null}
      return redirect(url_for(".box_auth"))
  ```

  When this code is engaged, if the user is not logged in to Okta they will be
  redirected to Okta to log in by the OIDC connector. After login, or if the user
  is already logged in, they will then be forwarded to the `box_auth` route code.

  Finally, replace `// TODO: HANDLE BOX AUTH ROUTE` in the `box_auth` route with
  the following code.

  ```python theme={null}
      box = Box()
      return box.validateUser(g)
  ```

  This creates a new instance of the Box class and calls the `validateUser`
  method, passing in the Okta user object. We'll define this class and methods in
  the next step.
</Choice>

<Choice option="programming.platform" value="cs" color="none">
  Replace `// TODO: HANDLE ROUTE` in the main route with the following code.

  ```csharp theme={null}
  var subClaim = HttpContext.User.Claims.First(c => c.Type == "sub");
  var sub = subClaim.Value;

  var nameClaim = HttpContext.User.Claims.First(c => c.Type == "name");
  var name = nameClaim.Value;

  Task userSearch = validateUser(name, sub);

  Task.WaitAll(userSearch);

  return Content(name);
  ```

  This block will capture the Okta user account sub (unique ID) and name, then
  sends that data to the to be created `validateUser` method to find a matching
  Box user, which will be created in the next step.
</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 set up the skeleton routes and configuration for Okta.
* You set up the main route handler to pass to Box user verification.

<Observe option="box.app_type" value="use_own,create_new_">
  <Next>I have set up the Okta login</Next>
</Observe>
