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

> Esta guía muestra cómo integrar Auth0, añadir autorización basada en tokens y proteger las rutas de la aplicación con el SDK de PHP de Auth0.

# Añade autorización a tu aplicación PHP

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
              processedNode = processedNode.replaceAll(new RegExp(escapedKey, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

export const QuickstartButtons = ({githubLink}) => {
  const text = {
    viewOnGithub: "Ver en GitHub",
    loginAndDownload: "Descargar ejemplo"
  };
  const parseGithubUrl = url => {
    try {
      const urlObj = new URL(url);
      const pathParts = urlObj.pathname.split("/").filter(Boolean);
      if (pathParts.length >= 4 && pathParts[2] === "tree") {
        const repoName = pathParts[1];
        const branch = pathParts[3];
        const path = pathParts.slice(4).join("/") || undefined;
        return {
          repo: repoName,
          branch,
          path
        };
      }
      console.warn("No se pudo analizar la URL de GitHub:", url);
      return null;
    } catch (error) {
      console.error("Error al analizar la URL de GitHub:", error);
      return null;
    }
  };
  const handleDownload = async () => {
    const params = parseGithubUrl(githubLink);
    if (!params) {
      console.error("Formato de URL de GitHub no válido");
      return;
    }
    try {
      await window.Auth0DocsUI?.getSample(params);
    } catch (error) {
      console.error("No se pudo descargar el ejemplo:", error);
    }
  };
  return <div className="quickstart_buttons flex flex-wrap gap-3 mb-4">
      <a href={githubLink} target="_blank" rel="noopener noreferrer" className="no_external_icon quickstart_button inline-flex items-center justify-center px-6 py-3 text-sm font-medium rounded-[18px] bg-black dark:bg-white !text-white dark:!text-black hover:bg-gray-800 dark:hover:bg-gray-100 transition-colors">
        {text.viewOnGithub}
      </a>
      <button onClick={handleDownload} type="button" className="no_external_icon quickstart_button inline-flex items-center justify-center px-6 py-3 text-sm font-medium rounded-[18px] border border-gray-300 dark:border-[#454545] bg-white dark:bg-[#272728] !text-black dark:!text-white hover:bg-gray-50 dark:hover:bg-neutral-800 transition-colors">
        {text.loginAndDownload}
      </button>
    </div>;
};

export const LoggedInForm = ({sampleApp}) => {
  const LS_APPS_KEY = "auth_demo_apps";
  const LS_APP_CFG_KEY = "auth_demo_app_cfg";
  const CHANNEL = "auth_flows_sync_v1";
  const mkChannel = () => new BroadcastChannel(CHANNEL);
  function uid() {
    return Math.random().toString(36).slice(2) + Date.now().toString(36);
  }
  function loadApps() {
    const raw = localStorage.getItem(LS_APPS_KEY);
    if (raw) return JSON.parse(raw);
    const seeded = [{
      id: "{yourClientId}",
      name: "Default App"
    }];
    localStorage.setItem(LS_APPS_KEY, JSON.stringify(seeded));
    return seeded;
  }
  function saveApps(apps) {
    localStorage.setItem(LS_APPS_KEY, JSON.stringify(apps));
  }
  function loadCfg() {
    const raw = localStorage.getItem(LS_APP_CFG_KEY);
    return raw ? JSON.parse(raw) : {};
  }
  function saveCfg(cfg) {
    localStorage.setItem(LS_APP_CFG_KEY, JSON.stringify(cfg));
  }
  const RightChevron = ({className = "w-5 h-5", ...props}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} {...props}>
      <polyline points="9 18 15 12 9 6" />
    </svg>;
  const LightningIcon = () => <svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
      <path fillRule="evenodd" clipRule="evenodd" className="fill-[#3F59E4] dark:fill-[#99A7F1]" d="M24.971 30.152H7.088c-1.786 0-2.745-2.103-1.574-3.453l19.07-21.988c1.33-1.532 3.835-.4 3.569 1.607L24.97 30.152z" />
      <path fillRule="evenodd" clipRule="evenodd" className="fill-[#3F59E4] dark:fill-[#99A7F1]" d="M23.201 17.885h17.885c1.787 0 2.746 2.102 1.575 3.453l-19.073 21.99c-1.33 1.532-3.835.4-3.568-1.607L23.2 17.885z" />
    </svg>;
  const LayersIcon = () => <svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
      <path className="fill-[#3F59E4] dark:fill-[#99A7F1]" d="M34.54 29.135l6.373 3.183c1.566.782 1.566 3.017 0 3.8l-14.815 7.396a4.623 4.623 0 01-4.125 0L7.174 36.12c-1.565-.782-1.565-3.017 0-3.798l6.532-3.214" />
      <path className="fill-[#AAB6F3] dark:fill-[#3449BA]" d="M34.54 18.86l6.373 3.183c1.566.782 1.566 3.016 0 3.8L26.098 33.24a4.623 4.623 0 01-4.125 0L7.174 25.843c-1.565-.781-1.565-3.016 0-3.798l6.33-3.164" />
      <path className="fill-[#CFD6F8] dark:fill-[#22307C]" d="M21.94 23.058L7.306 15.745c-1.62-.81-1.62-3.123 0-3.932l14.631-7.319a4.693 4.693 0 014.194 0l14.648 7.319c1.622.81 1.62 3.124 0 3.932L26.13 23.058c-1.321.66-2.873.66-4.191 0z" />
    </svg>;
  const GithubIcon = () => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-5 h-5">
      <path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
    </svg>;
  function IconTile({children}) {
    return <div className="
          shrink-0 grid place-items-center w-10 h-10 rounded-lg
          bg-indigo-50 ring-1 ring-indigo-200/60
          dark:bg-indigo-950/40 dark:ring-white/10
        ">
        {children}
      </div>;
  }
  function Card({className = "", children}) {
    return <div className={`rounded-2xl shadow-sm ring-1 ring-zinc-200 dark:ring-zinc-800 ${className}`}>{children}</div>;
  }
  function Button({variant = "primary", type = "button", onClick, children}) {
    const base = "inline-flex items-center justify-center gap-2 h-10 px-4 rounded-xl font-medium transition";
    let styles = "";
    if (variant === "primary") {
      styles = "mint-bg-indigo-600 text-white hover:mint-bg-indigo-700";
    } else if (variant === "outline") {
      styles = "border border-zinc-300 dark:border-zinc-700 mint-bg-transparent hover:mint-bg-zinc-50 dark:hover:mint-bg-zinc-800";
    } else if (variant === "ghost") {
      styles = "hover:mint-bg-zinc-100 dark:hover:mint-bg-zinc-800";
    }
    return <button type={type} onClick={onClick} className={`${base} ${styles}`}>
        {children}
      </button>;
  }
  function Input({id, label, value, onChange, placeholder, name}) {
    return <label className="block space-y-1">
        <span className="text-sm text-zinc-700 dark:text-zinc-300">{label}</span>
        <input id={id} name={name} className="w-full h-11 px-3 rounded-xl border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder={placeholder} value={value} onChange={e => onChange(e.target.value)} />
      </label>;
  }
  function Select({label, value, onChange, options}) {
    return <label className="block space-y-1 max-w-[300px]">
        <span className="text-sm text-zinc-700 dark:text-zinc-300">{label}</span>
        <div className="relative">
          <select className="w-full h-11 appearance-none px-3 pr-9 rounded-xl border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-indigo-500" value={value} onChange={e => onChange(e.target.value)}>
            <optgroup label="Generic Applications">
              {options.map(o => <option key={o.id} value={o.id}>
                  {o.name}
                </option>)}
            </optgroup>
          </select>
          <svg className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 text-zinc-500" viewBox="0 0 24 24">
            <path d="M7 10l5 5 5-5z" fill="currentColor" />
          </svg>
        </div>
      </label>;
  }
  function Toast({open, onClose, children}) {
    useEffect(() => {
      if (!open) return;
      const t = setTimeout(onClose, 2200);
      return () => clearTimeout(t);
    }, [open, onClose]);
    return <div className={`fixed right-4 top-4 z-50 transition ${open ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-2 pointer-events-none"}`}>
        <div className="flex items-center gap-2 rounded-xl shadow ring-1 ring-emerald-200 bg-white dark:bg-zinc-900 px-4 py-2">
          <span className="w-1.5 h-8 rounded-l bg-emerald-500" />
          <svg className="w-5 h-5 text-emerald-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
            <path d="M20 6L9 17l-5-5" />
          </svg>
          <span className="text-sm text-zinc-900 dark:text-zinc-100">{children}</span>
        </div>
      </div>;
  }
  function Flows() {
    const [route, setRoute] = useState("menu");
    const [apps, setApps] = useState(loadApps());
    const [cfg, setCfg] = useState(loadCfg());
    const [selected, setSelected] = useState(apps[0]?.id || "");
    const [toast, setToast] = useState(false);
    const [bc] = useState(() => mkChannel());
    useEffect(() => {
      if (!apps.find(a => a.id === selected)) {
        setSelected(apps[0]?.id || "");
      }
    }, [apps, selected]);
    useEffect(() => {
      const onMsg = e => {
        const {type, payload} = e.data || ({});
        switch (type) {
          case "NAV":
            setRoute(payload.route);
            break;
          case "SELECT":
            setSelected(payload.appId);
            break;
          case "APPS_UPDATED":
            setApps(loadApps());
            break;
          case "CFG_UPDATED":
            setCfg(loadCfg());
            setToast(true);
            break;
          default:
            break;
        }
      };
      bc.addEventListener("message", onMsg);
      return () => bc.removeEventListener("message", onMsg);
    }, [bc]);
    const nav = nextRoute => {
      setRoute(nextRoute);
      bc.postMessage({
        type: "NAV",
        payload: {
          route: nextRoute
        }
      });
    };
    const selectApp = appId => {
      setSelected(appId);
      bc.postMessage({
        type: "SELECT",
        payload: {
          appId
        }
      });
    };
    const onCreate = name => {
      const id = uid();
      const next = [...apps, {
        id,
        name: name || "Untitled"
      }];
      setApps(next);
      saveApps(next);
      bc.postMessage({
        type: "APPS_UPDATED"
      });
      selectApp(id);
      nav("integrate");
    };
    const onSaveCfg = (appId, data) => {
      const next = {
        ...cfg,
        [appId]: data
      };
      setCfg(next);
      saveCfg(next);
      setToast(true);
      bc.postMessage({
        type: "CFG_UPDATED"
      });
    };
    return <div>
        {route === "menu" && <Menu onCreate={() => nav("create")} onIntegrate={() => nav("integrate")} />}

        {route === "create" && <CreateForm onCancel={() => nav("menu")} onSave={onCreate} />}

        {route === "integrate" && <IntegrateForm apps={apps} selected={selected} onSelect={selectApp} saved={cfg[selected]} onSave={data => onSaveCfg(selected, data)} onCancel={() => nav("menu")} />}

        <Toast open={toast} onClose={() => setToast(false)}>
          Successfully saved your changes.
        </Toast>
      </div>;
  }
  function Menu({onCreate, onIntegrate}) {
    return <ul className="space-y-4 list-none login_list">
        <li className="list-none !px-0">
          <button onClick={onCreate} className="w-full text-left">
            <Card className="p-5 hover:shadow-md transition">
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-4">
                  <IconTile>
                    <LightningIcon />
                  </IconTile>
                  <h2 className="text-lg">Create a new application</h2>
                </div>
                <RightChevron className="w-4 h-4 text-zinc-500" />
              </div>
            </Card>
          </button>
        </li>
        <li className="list-none !px-0">
          <button onClick={onIntegrate} className="w-full text-left">
            <Card className="p-5 hover:shadow-md transition">
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-4">
                  <IconTile>
                    <LayersIcon />
                  </IconTile>
                  <h2 className="text-lg">Integrate with an existing application</h2>
                </div>
                <RightChevron className="w-4 h-4 text-zinc-500" />
              </div>
            </Card>
          </button>
        </li>
        <li className="list-none !px-0">
          <a className="no_external_icon block" href={sampleApp ? sampleApp : "/"} target="_blank" rel="noreferrer">
            <Card className="p-5 hover:shadow-md transition">
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-4">
                  <IconTile>
                    <GithubIcon />
                  </IconTile>
                  <h2 className="text-lg">View a sample application</h2>
                </div>
                <RightChevron className="w-4 h-4 text-zinc-500" />
              </div>
            </Card>
          </a>
        </li>
      </ul>;
  }
  function CreateForm({onSave, onCancel}) {
    const [name, setName] = useState("");
    return <div className="space-y-6">
        <Input id="app-name" label="Application Name" placeholder="My App" value={name} onChange={setName} />
        <p className="text-sm text-zinc-500">You can change this later in the application settings.</p>
        <div className="flex gap-3">
          <Button onClick={() => onSave(name)}>Guardar</Button>
          <Button variant="outline" onClick={onCancel}>
            Cancelar
          </Button>
        </div>
      </div>;
  }
  function IntegrateForm({apps, selected, onSelect, saved, onSave, onCancel}) {
    const [callbacks, setCallbacks] = useState(saved?.callbacks ?? "");
    const [logouts, setLogouts] = useState(saved?.logouts ?? "");
    const [origins, setOrigins] = useState(saved?.origins ?? "");
    useEffect(() => {
      setCallbacks(loadCfg()[selected]?.callbacks ?? "");
      setLogouts(loadCfg()[selected]?.logouts ?? "");
      setOrigins(loadCfg()[selected]?.origins ?? "");
    }, [selected]);
    return <div className="space-y-6">
        <div>
          <span className="block text-sm text-zinc-600 dark:text-zinc-300 mb-1">Selecciona tu aplicación</span>
          <Select label="" value={selected} onChange={onSelect} options={apps} />
        </div>

        <form className="space-y-4" onSubmit={e => {
      e.preventDefault();
      onSave({
        callbacks,
        logouts,
        origins
      });
    }}>
          <Input id="callbacks" name="callbacks" label="URLs de callback" placeholder="http://localhost:3000" value={callbacks} onChange={setCallbacks} />
          <Input id="logout" name="allowed_logout_urls" label="URLs de cierre de sesión" placeholder="http://localhost:3000" value={logouts} onChange={setLogouts} />
          <Input id="origins" name="web_origins" label="Orígenes web permitidos" placeholder="http://localhost:3000" value={origins} onChange={setOrigins} />

          <div className="flex gap-3 pt-2">
            <Button type="submit">Guardar</Button>
            <Button variant="outline" type="button" onClick={onCancel}>
              Cancelar
            </Button>
          </div>
        </form>
      </div>;
  }
  return <div className="w-full mx-auto py-8">
      <Flows />
    </div>;
};

export const SignUpForm = () => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [storeReady, setStoreReady] = useState(false);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      setStoreReady(true);
      unsubscribe = window.autorun(() => {
        const authenticated = window.rootStore?.sessionStore?.isAuthenticated || false;
        setIsAuthenticated(authenticated);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, []);
  function LoggedInForm({sampleApp}) {
    const LS_APPS_KEY = "auth_demo_apps";
    const LS_APP_CFG_KEY = "auth_demo_app_cfg";
    const CHANNEL = "auth_flows_sync_v1";
    const mkChannel = () => new BroadcastChannel(CHANNEL);
    function uid() {
      return Math.random().toString(36).slice(2) + Date.now().toString(36);
    }
    function loadApps() {
      const raw = localStorage.getItem(LS_APPS_KEY);
      if (raw) return JSON.parse(raw);
      const seeded = [{
        id: "{yourClientId}",
        name: "Aplicación predeterminada"
      }];
      localStorage.setItem(LS_APPS_KEY, JSON.stringify(seeded));
      return seeded;
    }
    function saveApps(apps) {
      localStorage.setItem(LS_APPS_KEY, JSON.stringify(apps));
    }
    function loadCfg() {
      const raw = localStorage.getItem(LS_APP_CFG_KEY);
      return raw ? JSON.parse(raw) : {};
    }
    function saveCfg(cfg) {
      localStorage.setItem(LS_APP_CFG_KEY, JSON.stringify(cfg));
    }
    const RightChevron = ({className = "w-5 h-5", ...props}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} {...props}>
        <polyline points="9 18 15 12 9 6" />
      </svg>;
    const LightningIcon = () => <svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path fillRule="evenodd" clipRule="evenodd" className="fill-[#3F59E4] dark:fill-[#99A7F1]" d="M24.971 30.152H7.088c-1.786 0-2.745-2.103-1.574-3.453l19.07-21.988c1.33-1.532 3.835-.4 3.569 1.607L24.97 30.152z" />
        <path fillRule="evenodd" clipRule="evenodd" className="fill-[#3F59E4] dark:fill-[#99A7F1]" d="M23.201 17.885h17.885c1.787 0 2.746 2.102 1.575 3.453l-19.073 21.99c-1.33 1.532-3.835.4-3.568-1.607L23.2 17.885z" />
      </svg>;
    const LayersIcon = () => <svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path className="fill-[#3F59E4] dark:fill-[#99A7F1]" d="M34.54 29.135l6.373 3.183c1.566.782 1.566 3.017 0 3.8l-14.815 7.396a4.623 4.623 0 01-4.125 0L7.174 36.12c-1.565-.782-1.565-3.017 0-3.798l6.532-3.214" />
        <path className="fill-[#AAB6F3] dark:fill-[#3449BA]" d="M34.54 18.86l6.373 3.183c1.566.782 1.566 3.016 0 3.8L26.098 33.24a4.623 4.623 0 01-4.125 0L7.174 25.843c-1.565-.781-1.565-3.016 0-3.798l6.33-3.164" />
        <path className="fill-[#CFD6F8] dark:fill-[#22307C]" d="M21.94 23.058L7.306 15.745c-1.62-.81-1.62-3.123 0-3.932l14.631-7.319a4.693 4.693 0 014.194 0l14.648 7.319c1.622.81 1.62 3.124 0 3.932L26.13 23.058c-1.321.66-2.873.66-4.191 0z" />
      </svg>;
    const GithubIcon = () => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-5 h-5">
        <path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
      </svg>;
    function IconTile({children}) {
      return <div className="
            shrink-0 grid place-items-center w-10 h-10 rounded-lg
            bg-indigo-50 ring-1 ring-indigo-200/60
            dark:bg-indigo-950/40 dark:ring-white/10
          ">
          {children}
        </div>;
    }
    function Card({className = "", children}) {
      return <div className={`rounded-2xl shadow-sm ring-1 ring-zinc-200 dark:ring-zinc-800 ${className}`}>{children}</div>;
    }
    function Button({variant = "primary", type = "button", onClick, children}) {
      const base = "inline-flex items-center justify-center gap-2 h-10 px-4 rounded-xl font-medium transition";
      let styles = "";
      if (variant === "primary") {
        styles = "mint-bg-indigo-600 text-white hover:mint-bg-indigo-700";
      } else if (variant === "outline") {
        styles = "border border-zinc-300 dark:border-zinc-700 mint-bg-transparent hover:mint-bg-zinc-50 dark:hover:mint-bg-zinc-800";
      } else if (variant === "ghost") {
        styles = "hover:mint-bg-zinc-100 dark:hover:mint-bg-zinc-800";
      }
      return <button type={type} onClick={onClick} className={`${base} ${styles}`}>
          {children}
        </button>;
    }
    function Input({id, label, value, onChange, placeholder, name}) {
      return <label className="block space-y-1">
          <span className="text-sm text-zinc-700 dark:text-zinc-300">{label}</span>
          <input id={id} name={name} className="w-full h-11 px-3 rounded-xl border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder={placeholder} value={value} onChange={e => onChange(e.target.value)} />
        </label>;
    }
    function Select({label, value, onChange, options}) {
      return <label className="block space-y-1 max-w-[300px]">
          <span className="text-sm text-zinc-700 dark:text-zinc-300">{label}</span>
          <div className="relative">
            <select className="w-full h-11 appearance-none px-3 pr-9 rounded-xl border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-indigo-500" value={value} onChange={e => onChange(e.target.value)}>
              <optgroup label="Generic Applications">
                {options.map(o => <option key={o.id} value={o.id}>
                    {o.name}
                  </option>)}
              </optgroup>
            </select>
            <svg className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 text-zinc-500" viewBox="0 0 24 24">
              <path d="M7 10l5 5 5-5z" fill="currentColor" />
            </svg>
          </div>
        </label>;
    }
    function Toast({open, onClose, children}) {
      useEffect(() => {
        if (!open) return;
        const t = setTimeout(onClose, 2200);
        return () => clearTimeout(t);
      }, [open, onClose]);
      return <div className={`fixed right-4 top-4 z-50 transition ${open ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-2 pointer-events-none"}`}>
          <div className="flex items-center gap-2 rounded-xl shadow ring-1 ring-emerald-200 bg-white dark:bg-zinc-900 px-4 py-2">
            <span className="w-1.5 h-8 rounded-l bg-emerald-500" />
            <svg className="w-5 h-5 text-emerald-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M20 6L9 17l-5-5" />
            </svg>
            <span className="text-sm text-zinc-900 dark:text-zinc-100">{children}</span>
          </div>
        </div>;
    }
    function Flows() {
      const [route, setRoute] = useState("menu");
      const [apps, setApps] = useState(loadApps());
      const [cfg, setCfg] = useState(loadCfg());
      const [selected, setSelected] = useState(apps[0]?.id || "");
      const [toast, setToast] = useState(false);
      const [bc] = useState(() => mkChannel());
      useEffect(() => {
        if (!apps.find(a => a.id === selected)) {
          setSelected(apps[0]?.id || "");
        }
      }, [apps, selected]);
      useEffect(() => {
        const onMsg = e => {
          const {type, payload} = e.data || ({});
          switch (type) {
            case "NAV":
              setRoute(payload.route);
              break;
            case "SELECT":
              setSelected(payload.appId);
              break;
            case "APPS_UPDATED":
              setApps(loadApps());
              break;
            case "CFG_UPDATED":
              setCfg(loadCfg());
              setToast(true);
              break;
            default:
              break;
          }
        };
        bc.addEventListener("message", onMsg);
        return () => bc.removeEventListener("message", onMsg);
      }, [bc]);
      const nav = nextRoute => {
        setRoute(nextRoute);
        bc.postMessage({
          type: "NAV",
          payload: {
            route: nextRoute
          }
        });
      };
      const selectApp = appId => {
        setSelected(appId);
        bc.postMessage({
          type: "SELECT",
          payload: {
            appId
          }
        });
      };
      const onCreate = name => {
        const id = uid();
        const next = [...apps, {
          id,
          name: name || "Sin título"
        }];
        setApps(next);
        saveApps(next);
        bc.postMessage({
          type: "APPS_UPDATED"
        });
        selectApp(id);
        nav("integrate");
      };
      const onSaveCfg = (appId, data) => {
        const next = {
          ...cfg,
          [appId]: data
        };
        setCfg(next);
        saveCfg(next);
        setToast(true);
        bc.postMessage({
          type: "CFG_UPDATED"
        });
      };
      return <div>
          {route === "menu" && <Menu onCreate={() => nav("create")} onIntegrate={() => nav("integrate")} />}

          {route === "create" && <CreateForm onCancel={() => nav("menu")} onSave={onCreate} />}

          {route === "integrate" && <IntegrateForm apps={apps} selected={selected} onSelect={selectApp} saved={cfg[selected]} onSave={data => onSaveCfg(selected, data)} onCancel={() => nav("menu")} />}

          <Toast open={toast} onClose={() => setToast(false)}>
            Successfully saved your changes.
          </Toast>
        </div>;
    }
    function Menu({onCreate, onIntegrate}) {
      return <ul className="space-y-4 list-none login_list">
          <li className="list-none !px-0">
            <button onClick={onCreate} className="w-full text-left">
              <Card className="p-5 hover:shadow-md transition">
                <div className="flex items-center justify-between">
                  <div className="flex items-center gap-4">
                    <IconTile>
                      <LightningIcon />
                    </IconTile>
                    <h2 className="text-lg">Crear una nueva aplicación</h2>
                  </div>
                  <RightChevron className="w-4 h-4 text-zinc-500" />
                </div>
              </Card>
            </button>
          </li>
          <li className="list-none !px-0">
            <button onClick={onIntegrate} className="w-full text-left">
              <Card className="p-5 hover:shadow-md transition">
                <div className="flex items-center justify-between">
                  <div className="flex items-center gap-4">
                    <IconTile>
                      <LayersIcon />
                    </IconTile>
                    <h2 className="text-lg">Integrar con una aplicación existente</h2>
                  </div>
                  <RightChevron className="w-4 h-4 text-zinc-500" />
                </div>
              </Card>
            </button>
          </li>
          <li className="list-none !px-0">
            <a className="no_external_icon block" href={sampleApp ? sampleApp : "/"} target="_blank" rel="noreferrer">
              <Card className="p-5 hover:shadow-md transition">
                <div className="flex items-center justify-between">
                  <div className="flex items-center gap-4">
                    <IconTile>
                      <GithubIcon />
                    </IconTile>
                    <h2 className="text-lg">Ver una aplicación de ejemplo</h2>
                  </div>
                  <RightChevron className="w-4 h-4 text-zinc-500" />
                </div>
              </Card>
            </a>
          </li>
        </ul>;
    }
    function CreateForm({onSave, onCancel}) {
      const [name, setName] = useState("");
      return <div className="space-y-6">
          <Input id="app-name" label="Nombre de la aplicación" placeholder="Mi aplicación" value={name} onChange={setName} />
          <p className="text-sm text-zinc-500">Puedes cambiarlo más adelante en la configuración de la aplicación.</p>
          <div className="flex gap-3">
            <Button onClick={() => onSave(name)}>Guardar</Button>
            <Button variant="outline" onClick={onCancel}>
              Cancelar
            </Button>
          </div>
        </div>;
    }
    function IntegrateForm({apps, selected, onSelect, saved, onSave, onCancel}) {
      const [callbacks, setCallbacks] = useState(saved?.callbacks ?? "");
      const [logouts, setLogouts] = useState(saved?.logouts ?? "");
      const [origins, setOrigins] = useState(saved?.origins ?? "");
      useEffect(() => {
        setCallbacks(loadCfg()[selected]?.callbacks ?? "");
        setLogouts(loadCfg()[selected]?.logouts ?? "");
        setOrigins(loadCfg()[selected]?.origins ?? "");
      }, [selected]);
      return <div className="space-y-6">
          <div>
            <span className="block text-sm text-zinc-600 dark:text-zinc-300 mb-1">Selecciona tu aplicación</span>
            <Select label="" value={selected} onChange={onSelect} options={apps} />
          </div>

          <form className="space-y-4" onSubmit={e => {
        e.preventDefault();
        onSave({
          callbacks,
          logouts,
          origins
        });
      }}>
            <Input id="callbacks" name="callbacks" label="URLs de callback" placeholder="http://localhost:3000" value={callbacks} onChange={setCallbacks} />
            <Input id="logout" name="allowed_logout_urls" label="URLs de cierre de sesión" placeholder="http://localhost:3000" value={logouts} onChange={setLogouts} />
            <Input id="origins" name="web_origins" label="Orígenes web permitidos" placeholder="http://localhost:3000" value={origins} onChange={setOrigins} />

            <div className="flex gap-3 pt-2">
              <Button type="submit">Guardar</Button>
              <Button variant="outline" type="button" onClick={onCancel}>
                Cancelar
              </Button>
            </div>
          </form>
        </div>;
    }
    return <div className="w-full mx-auto py-8">
        <Flows />
      </div>;
  }
  function SignUpFormInternal() {
    return <div className="flex flex-col gap-2 items-center h-full">
        <img noZoom src="/docs/img/quickstarts/action_hero_dashboard.svg" alt="Regístrate para obtener una cuenta de Auth0" style={{
      width: "250px",
      height: "250px"
    }} />
        <span className="text-center" style={{
      width: "400px"
    }}>
          Regístrate para obtener una{" "}
          <a href="https://auth0.com/signup" target="_blank" rel="noopener noreferrer">
            cuenta de Auth0
          </a>{" "}
          o{" "}
          <span className="font-semibold text-primary dark:text-white cursor-pointer" onClick={() => console.log("log in")}>
            inicia sesión
          </span>{" "}
          en tu cuenta existente para integrarte directamente con tu propio tenant.
        </span>
        <button onClick={() => console.log("sign up")} className="bg-primary dark:bg-primary-light text-white dark:text-black px-4 py-2 rounded-md mt-4 font-medium" style={{
      width: "140px"
    }}>
          Regístrate
        </button>
      </div>;
  }
  return <></>;
};

export const SideMenuSectionItem = ({id, children}) => {
  return <div id={`side-menu-item-${id}`} className="recipe-side-menu-item flex flex-col w-full h-full">
      {children}
    </div>;
};

export const SideMenu = ({sections, children}) => {
  const [visibleSection, setVisibleSection] = useState(sections[0]?.id ?? null);
  const checkVisibility = () => {
    let currentVisible = null;
    const viewportHeight = window.innerHeight;
    const scrollY = window.scrollY;
    sections.forEach(({id}) => {
      const section = document.getElementById(id);
      if (section) {
        const rect = section.getBoundingClientRect();
        const sectionTop = rect.top + scrollY;
        const sectionBottom = sectionTop + rect.height;
        const multiplier = viewportHeight > 1600 ? 0.34 : 0.22;
        if (scrollY + viewportHeight * multiplier >= sectionTop && scrollY <= sectionBottom) {
          currentVisible = id;
        }
      }
    });
    if (currentVisible && currentVisible !== visibleSection) {
      setVisibleSection(currentVisible);
    }
  };
  useEffect(() => {
    const throttledCheck = () => {
      setTimeout(checkVisibility, 100);
    };
    checkVisibility();
    window.addEventListener("scroll", throttledCheck);
    return () => {
      window.removeEventListener("scroll", throttledCheck);
    };
  }, [sections, visibleSection]);
  useEffect(() => {
    sections.forEach(({id}) => {
      const section = document.getElementById(id);
      const sideMenuItem = document.getElementById(`side-menu-item-${id}`);
      if (section) {
        if (id === visibleSection) {
          section.classList.add("active-section");
        } else {
          section.classList.remove("active-section");
        }
      }
      if (sideMenuItem) {
        if (id === visibleSection) {
          sideMenuItem.classList.add("active-side-menu-item");
        } else {
          sideMenuItem.classList.remove("active-side-menu-item");
        }
      }
    });
  }, [visibleSection, sections]);
  return <div className="recipe-side-menu sticky px-2 py-1" style={{
    height: "calc(100vh - 7rem)",
    top: "7rem",
    scrollMarginTop: "var(--scroll-mt)"
  }}>
      {children.map(child => {
    if (child.props.id === visibleSection) {
      return child;
    }
    return null;
  })}
    </div>;
};

export const Section = ({id, title, stepNumber, children, isSingleColumn = false}) => {
  return <div id={id} className={`recipe-section flex flex-col transition-opacity duration-200`}>
      {}
      <Step title={title} stepNumber={stepNumber} titleSize="h3">
        {children}
      </Step>
    </div>;
};

export const Content = ({title, children}) => {
  return <div className="recipe-content flex flex-col">
      {title && <h1 className="text-3xl">{title}</h1>}
      {children}
    </div>;
};

export const Recipe = ({children, isSingleColumn = false}) => {
  return <div className={`pl-4 recipe-container mx-auto grid grid-cols-1 gap-10 relative ${isSingleColumn ? "md:grid-cols-1" : "md:grid-cols-2"}`}>
      {children}
    </div>;
};

<QuickstartButtons githubLink="https://github.com/auth0-samples/auth0-php-api-samples/tree/main/app" />

export const sections = [{
  id: "configure-auth0",
  title: "Configurar Auth0"
}, {
  id: "install-the-auth0-php-sdk",
  title: "Instalar el SDK de Auth0 para PHP"
}, {
  id: "listen-for-the-bearer-tokens",
  title: "Detectar los tokens de portador"
}, {
  id: "create-and-configure-routes",
  title: "Crear y configurar rutas"
}, {
  id: "configure-endpoint-authorization",
  title: "Configurar la autorización del endpoint"
}, {
  id: "authorize-with-scopes",
  title: "Autorizar con alcances"
}];

<Recipe>
  <Content>
    Auth0 le permite agregar autorización de endpoints basada en tokens a casi cualquier tipo de aplicación de forma rápida. Esta guía
    muestra cómo integrar Auth0, añadir autorización basada en tokens y proteger las rutas de la aplicación mediante el Auth0
    PHP SDK.

    Para usar este inicio rápido, necesitarás:

    <Section id={sections[0].id} title={sections[0].title} stepNumber="1">
      Para usar los servicios de Auth0, debe tener una aplicación registrada en el Auth0 Dashboard. En la aplicación de Auth0
      configura cómo quiere que funcione la autenticación para su proyecto.

      ### Configurar una aplicación

      Use el selector interactivo para crear una nueva aplicación de Auth0 o seleccionar una aplicación existente que represente
      el proyecto que quiere integrar. A cada aplicación de Auth0 se le asigna un ID de cliente alfanumérico único que
      el código de su aplicación usa para llamar a las API de Auth0 a través del SDK.

      Cualquier configuración que realice con esta guía de inicio rápido se actualizará automáticamente para su aplicación en el [Dashboard](https://manage.auth0.com/#/), donde
      podrá administrar sus aplicaciones en el futuro.

      Si prefiere explorar una configuración completa, puede ver una aplicación de ejemplo en su lugar.

      ### Configurar una API

      Del mismo modo, debe crear una nueva API de Auth0 o usar una API existente que represente el proyecto que
      quiere integrar desde el [Dashboard](https://manage.auth0.com/#/). Elija un identificador único para la API y anótelo. Necesita
      ese identificador para configurar su aplicación a continuación.
    </Section>

    <Section id={sections[1].id} title={sections[1].title} stepNumber="2">
      Auth0 proporciona un [SDK de
      PHP](https://github.com/auth0/auth0-PHP) (Auth0-PHP) para simplificar la implementación de la autenticación y la autorización de Auth0 en aplicaciones PHP.

      El SDK de PHP de Auth0 requiere [PSR-17](https://www.php-fig.org/psr/psr-17/) y [PSR-18](https://www.php-fig.org/psr/psr-18/) instalados, además de bibliotecas HTTP compatibles para gestionar las solicitudes de red. Si no
      dispone de bibliotecas, puede instalar opciones fiables ejecutando los siguientes comandos en la
      terminal:

      ```bash lines theme={null}
      cd <your-project-directory>
      composer require symfony/http-client nyholm/psr7
      ```

      Ahora, instala el SDK de PHP de Auth0 ejecutando el siguiente comando en la terminal:

      ```bash lines theme={null}
      composer require auth0/auth0-php
      ```

      ### Configurar el SDK de Auth0

      Para que el SDK funcione correctamente, debe establecer las siguientes propiedades en el SDK de Auth0 durante la inicialización:

      * `strategy`: La estrategia ayuda a definir el comportamiento del SDK según el caso de uso de su aplicación. En este
        caso, debe establecer esta propiedad en la constante

        `Auth0\SDK\Configuration\SdkConfiguration::STRATEGY_API`
      * domain: El dominio de su inquilino de Auth0. Por lo general, lo encontrará en el Auth0 Dashboard, en
        Settings de la aplicación, en el campo Domain. Si usa un dominio personalizado, establezca este valor en el de su dominio personalizado
        en su lugar.
      * clientId: El ID de la aplicación de Auth0 que configuró anteriormente en esta guía de inicio rápido. Puede encontrarlo en el
        Auth0 Dashboard, en Settings de su aplicación, en el campo Client ID.
      * clientSecret: El secreto de la aplicación de Auth0 que creó anteriormente en esta guía de inicio rápido. El secreto del cliente está en
        el Auth0 Dashboard, en Settings de su aplicación, en el campo Client Secret.
      * audience: El identificador de la API de Auth0 que registró anteriormente. Debe proporcionarse como un array.

      <Note>
        ##### Punto de control

        Su SDK de Auth0 ya está configurado correctamente. Ejecute su aplicación para verificar que:

        * El SDK se inicializa correctamente.
        * Su aplicación no genera ningún error relacionado con Auth0.
      </Note>
    </Section>

    <Section id={sections[2].id} title={sections[2].title} stepNumber="3">
      A continuación, amplíe su aplicación para recuperar y procesar tokens Bearer. Los tokens Bearer son tokens de acceso que los clientes proporcionan a
      su API con las solicitudes en nombre de los usuarios. Los tokens de acceso permiten o deniegan el acceso a las rutas de su
      aplicación. Esto se conoce como autorización de endpoints.

      La forma más sencilla de recuperar tokens de acceso de una solicitud es usar el método `getBearerToken()` del SDK de PHP.
      Este método recupera tokens de parámetros GET, cuerpos POST, encabezados de solicitud y otras fuentes. En este
      caso, el SDK de PHP procesa los tokens enviados en solicitudes GET en el parámetro `token` o desde el encabezado HTTP
      `Authorization`.
    </Section>

    <Section id={sections[3].id} title={sections[3].title} stepNumber="4">
      Ahora, instala una biblioteca de enrutamiento para dirigir las solicitudes entrantes a tu aplicación. Este paso no es obligatorio,
      pero simplifica la estructura de la aplicación para esta guía de inicio rápido.

      ```bash lines theme={null}
      composer require steampixel/simple-php-router
      ```

      Cree un nuevo archivo en su aplicación llamado `router.php` para definir las rutas. Copie el código desde
      el panel interactivo de la derecha, en la pestaña **router.php**.
    </Section>

    <Section id={sections[4].id} title={sections[4].title} stepNumber="5">
      Ahora que ha configurado su aplicación de Auth0, el SDK de PHP de Auth0, y su aplicación recupera tokens Bearer
      de las solicitudes, el siguiente paso es configurar la autorización de endpoints para su proyecto. El método
      `getBearerToken()` que implementó anteriormente devuelve una instancia de la clase `Token` que incluye
      detalles sobre el acceso de la solicitud.

      Dado que el método `getBearerToken()` valida y verifica automáticamente la solicitud entrante, su
      aplicación determina los detalles del token de acceso evaluando la respuesta del método. Cuando la respuesta es
      null, no se ha proporcionado ningún token válido. De lo contrario, inspeccione el contenido de la respuesta para obtener más información sobre la
      solicitud.

      En el panel interactivo de la derecha, puede ver una comprobación para determinar si la respuesta es null o no y así filtrar el acceso a
      su ruta `/api/private`.
    </Section>

    <Section id={sections[5].id} title={sections[5].title} stepNumber="6">
      En algunos casos, puede que quiera filtrar el acceso a una ruta específica en función de los alcances solicitados en un token de acceso.
      Como se muestra en el panel interactivo de la derecha, evalúe el contenido de la propiedad 'scope' de la
      respuesta del método `getBearerToken()` para comprobar los alcances otorgados por el token de acceso.
    </Section>

    ## Próximos pasos

    ¡Excelente trabajo! Si llegaste hasta aquí, ya deberías tener el inicio de sesión, el cierre de sesión y la información del perfil de usuario funcionando en tu aplicación.

    Con esto concluye nuestro tutorial de inicio rápido, pero hay mucho más por explorar. Para saber más sobre lo que puedes hacer con Auth0, consulta:

    * [Auth0 Dashboard](https://manage.auth0.com/dashboard/us/dev-gja8kxz4ndtex3rq) - Aprenda a configurar y administrar su inquilino de Auth0 y sus aplicaciones
    * [Auth0-PHP SDK](https://github.com/auth0/auth0-php) - Explora más a fondo el SDK utilizado en este tutorial
    * [Auth0 Marketplace](https://marketplace.auth0.com/) - Descubre las integraciones que puedes habilitar para ampliar las capacidades de Auth0
  </Content>

  <SideMenu sections={sections}>
    <SideMenuSectionItem id={sections[0].id}>
      <SignUpForm />
    </SideMenuSectionItem>

    <SideMenuSectionItem id={sections[1].id}>
      <AuthCodeGroup>
        ```php index.php lines highlight={} theme={null}
        <?php

              declare(strict_types=1);

              require('vendor/autoload.php');

              use Auth0\SDK\Auth0;
              use Auth0\SDK\Configuration\SdkConfiguration;

              $configuration = new SdkConfiguration(
        strategy: SdkConfiguration::STRATEGY_API,
        domain: '{yourDomain}',
        clientId: '{yourClientId}',
        clientSecret: '{yourClientSecret}',
        audience: ['{yourApiIdentifier}']
              );

              $sdk = new Auth0($configuration);

              $token = $sdk->getBearerToken(
        get: ['token'],
        server: ['Authorization']
              );

              require('router.php');
        ```

        ```php router.php lines highlight={} theme={null}
        <?php

              declare(strict_types=1);

              use Steampixel\Route;

              function routeResponse(
        array $response
              ) {
        header('Content-Type: application/json');
        print(json_encode($response, JSON_PRETTY_PRINT));
              }

              Route::add('/api/public', function() use ($token) {
        routeResponse([
          'message' => 'Hello from a public endpoint! You don\'t need to be authenticated to see this.',
          'token' => $token
        ]);
              });

              Route::add('/api/private', function() use ($token) {
        if ($token === null) {
          http_response_code(401);
          exit;
        }

        routeResponse([
          'message' => 'Hello from a private endpoint! You need to be authenticated to see this.',
          'token' => $token,
        ]);
              });

              Route::add('/api/private-scoped', function() use ($token) {
        if ($token === null) {
          http_response_code(401);
          exit;
        }

        if (! in_array('read:messages', $token['scopes'], true)) {
          http_response_code(401);
          exit;
        }

        routeResponse([
          'message' => 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.',
          'token' => $token,
        ]);
              });

              // La siguiente ruta es solo para evitar confusión.
              // No usamos una 'index route' en esta aplicación, por lo que redirigimos las solicitudes a /api/public.
              Route::add('/', function() {
        header('Location: /api/public');
              });

              Route::run();
        ```
      </AuthCodeGroup>
    </SideMenuSectionItem>

    <SideMenuSectionItem id={sections[2].id}>
      <AuthCodeGroup>
        ```php index.php lines highlight={20-23} theme={null}
        <?php

              declare(strict_types=1);

              require('vendor/autoload.php');

              use Auth0\SDK\Auth0;
              use Auth0\SDK\Configuration\SdkConfiguration;

              $configuration = new SdkConfiguration(
        strategy: SdkConfiguration::STRATEGY_API,
        domain: '{yourDomain}',
        clientId: '{yourClientId}',
        clientSecret: '{yourClientSecret}',
        audience: ['{yourApiIdentifier}']
              );

              $sdk = new Auth0($configuration);

              $token = $sdk->getBearerToken(
        get: ['token'],
        server: ['Authorization']
              );

              require('router.php');
        ```

        ```php router.php lines highlight={} theme={null}
        <?php

              declare(strict_types=1);

              use Steampixel\Route;

              function routeResponse(
        array $response
              ) {
        header('Content-Type: application/json');
        print(json_encode($response, JSON_PRETTY_PRINT));
              }

              Route::add('/api/public', function() use ($token) {
        routeResponse([
          'message' => 'Hello from a public endpoint! You don\'t need to be authenticated to see this.',
          'token' => $token
        ]);
              });

              Route::add('/api/private', function() use ($token) {
        if ($token === null) {
          http_response_code(401);
          exit;
        }

        routeResponse([
          'message' => 'Hello from a private endpoint! You need to be authenticated to see this.',
          'token' => $token,
        ]);
              });

              Route::add('/api/private-scoped', function() use ($token) {
        if ($token === null) {
          http_response_code(401);
          exit;
        }

        if (! in_array('read:messages', $token['scopes'], true)) {
          http_response_code(401);
          exit;
        }

        routeResponse([
          'message' => 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.',
          'token' => $token,
        ]);
              });

              // La siguiente ruta es solo para evitar confusión.
              // No usamos una 'index route' en esta aplicación, por lo que redirigimos las solicitudes a /api/public.
              Route::add('/', function() {
        header('Location: /api/public');
              });

              Route::run();
        ```
      </AuthCodeGroup>
    </SideMenuSectionItem>

    <SideMenuSectionItem id={sections[3].id}>
      <AuthCodeGroup>
        ```php router.php lines highlight={} theme={null}
        <?php

              declare(strict_types=1);

              use Steampixel\Route;

              function routeResponse(
        array $response
              ) {
        header('Content-Type: application/json');
        print(json_encode($response, JSON_PRETTY_PRINT));
              }

              Route::add('/api/public', function() use ($token) {
        routeResponse([
          'message' => 'Hello from a public endpoint! You don\'t need to be authenticated to see this.',
          'token' => $token
        ]);
              });

              Route::add('/api/private', function() use ($token) {
        if ($token === null) {
          http_response_code(401);
          exit;
        }

        routeResponse([
          'message' => 'Hello from a private endpoint! You need to be authenticated to see this.',
          'token' => $token,
        ]);
              });

              Route::add('/api/private-scoped', function() use ($token) {
        if ($token === null) {
          http_response_code(401);
          exit;
        }

        if (! in_array('read:messages', $token['scopes'], true)) {
          http_response_code(401);
          exit;
        }

        routeResponse([
          'message' => 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.',
          'token' => $token,
        ]);
              });

              // La siguiente ruta es solo para evitar confusión.
              // No usamos una 'index route' en esta aplicación, por lo que redirigimos las solicitudes a /api/public.
              Route::add('/', function() {
        header('Location: /api/public');
              });

              Route::run();
        ```

        ```php index.php lines highlight={} theme={null}
        <?php

              declare(strict_types=1);

              require('vendor/autoload.php');

              use Auth0\SDK\Auth0;
              use Auth0\SDK\Configuration\SdkConfiguration;

              $configuration = new SdkConfiguration(
        strategy: SdkConfiguration::STRATEGY_API,
        domain: '{yourDomain}',
        clientId: '{yourClientId}',
        clientSecret: '{yourClientSecret}',
        audience: ['{yourApiIdentifier}']
              );

              $sdk = new Auth0($configuration);

              $token = $sdk->getBearerToken(
        get: ['token'],
        server: ['Authorization']
              );

              require('router.php');
        ```
      </AuthCodeGroup>
    </SideMenuSectionItem>

    <SideMenuSectionItem id={sections[4].id}>
      <AuthCodeGroup>
        ```php router.php lines highlight={21-31} theme={null}
        <?php

              declare(strict_types=1);

              use Steampixel\Route;

              function routeResponse(
        array $response
              ) {
        header('Content-Type: application/json');
        print(json_encode($response, JSON_PRETTY_PRINT));
              }

              Route::add('/api/public', function() use ($token) {
        routeResponse([
          'message' => 'Hello from a public endpoint! You don\'t need to be authenticated to see this.',
          'token' => $token
        ]);
              });

              Route::add('/api/private', function() use ($token) {
        if ($token === null) {
          http_response_code(401);
          exit;
        }

        routeResponse([
          'message' => 'Hello from a private endpoint! You need to be authenticated to see this.',
          'token' => $token,
        ]);
              });

              Route::add('/api/private-scoped', function() use ($token) {
        if ($token === null) {
          http_response_code(401);
          exit;
        }

        if (! in_array('read:messages', $token['scopes'], true)) {
          http_response_code(401);
          exit;
        }

        routeResponse([
          'message' => 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.',
          'token' => $token,
        ]);
              });

              // La siguiente ruta es solo para evitar confusiones.
              // No usamos una 'index route' en esta aplicación, por lo que redirigimos las solicitudes a /api/public.
              Route::add('/', function() {
        header('Location: /api/public');
              });

              Route::run();
        ```

        ```php index.php lines highlight={} theme={null}
        <?php

              declare(strict_types=1);

              require('vendor/autoload.php');

              use Auth0\SDK\Auth0;
              use Auth0\SDK\Configuration\SdkConfiguration;

              $configuration = new SdkConfiguration(
        strategy: SdkConfiguration::STRATEGY_API,
        domain: '{yourDomain}',
        clientId: '{yourClientId}',
        clientSecret: '{yourClientSecret}',
        audience: ['{yourApiIdentifier}']
              );

              $sdk = new Auth0($configuration);

              $token = $sdk->getBearerToken(
        get: ['token'],
        server: ['Authorization']
              );

              require('router.php');
        ```
      </AuthCodeGroup>
    </SideMenuSectionItem>

    <SideMenuSectionItem id={sections[5].id}>
      <AuthCodeGroup>
        ```php router.php lines highlight={33-48} theme={null}
        <?php

              declare(strict_types=1);

              use Steampixel\Route;

              function routeResponse(
        array $response
              ) {
        header('Content-Type: application/json');
        print(json_encode($response, JSON_PRETTY_PRINT));
              }

              Route::add('/api/public', function() use ($token) {
        routeResponse([
          'message' => 'Hello from a public endpoint! You don\'t need to be authenticated to see this.',
          'token' => $token
        ]);
              });

              Route::add('/api/private', function() use ($token) {
        if ($token === null) {
          http_response_code(401);
          exit;
        }

        routeResponse([
          'message' => 'Hello from a private endpoint! You need to be authenticated to see this.',
          'token' => $token,
        ]);
              });

              Route::add('/api/private-scoped', function() use ($token) {
        if ($token === null) {
          http_response_code(401);
          exit;
        }

        if (! in_array('read:messages', $token['scopes'], true)) {
          http_response_code(401);
          exit;
        }

        routeResponse([
          'message' => 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.',
          'token' => $token,
        ]);
              });

              // La siguiente ruta es solo para evitar confusión.
              // No usamos una 'index route' en esta app, por lo que redirigimos las solicitudes a /api/public.
              Route::add('/', function() {
        header('Location: /api/public');
              });

              Route::run();
        ```

        ```php index.php lines highlight={} theme={null}
        <?php

              declare(strict_types=1);

              require('vendor/autoload.php');

              use Auth0\SDK\Auth0;
              use Auth0\SDK\Configuration\SdkConfiguration;

              $configuration = new SdkConfiguration(
        strategy: SdkConfiguration::STRATEGY_API,
        domain: '{yourDomain}',
        clientId: '{yourClientId}',
        clientSecret: '{yourClientSecret}',
        audience: ['{yourApiIdentifier}']
              );

              $sdk = new Auth0($configuration);

              $token = $sdk->getBearerToken(
        get: ['token'],
        server: ['Authorization']
              );

              require('router.php');
        ```
      </AuthCodeGroup>
    </SideMenuSectionItem>
  </SideMenu>
</Recipe>
