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

> Ce tutoriel montre comment appeler votre API depuis un appareil à capacité de saisie limitée au moyen du flux d’autorisation de l’appareil.

# Flux d’autorisation de l’appareil

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 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="Nom de l'application" placeholder="Mon application" value={name} onChange={setName} />
        <p className="text-sm text-zinc-500">Vous pourrez modifier ce paramètre plus tard dans les paramètres de l'application.</p>
        <div className="flex gap-3">
          <Button onClick={() => onSave(name)}>Save</Button>
          <Button variant="outline" onClick={onCancel}>
            Cancel
          </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">Sélectionnez votre application</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="URL de rappel" placeholder="http://localhost:3000" value={callbacks} onChange={setCallbacks} />
          <Input id="logout" name="allowed_logout_urls" label="URL de déconnexion" placeholder="http://localhost:3000" value={logouts} onChange={setLogouts} />
          <Input id="origins" name="web_origins" label="Origines Web autorisées" placeholder="http://localhost:3000" value={origins} onChange={setOrigins} />

          <div className="flex gap-3 pt-2">
            <Button type="submit">Enregistrer</Button>
            <Button variant="outline" type="button" onClick={onCancel}>
              Annuler
            </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: "Application par défaut"
      }];
      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 || "Sans titre"
        }];
        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">Créer une nouvelle 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">Intégrer à une application existante</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">Voir un exemple d'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="Nom de l'application" placeholder="Mon application" value={name} onChange={setName} />
          <p className="text-sm text-zinc-500">Vous pourrez modifier ce paramètre ultérieurement dans les paramètres de l'application.</p>
          <div className="flex gap-3">
            <Button onClick={() => onSave(name)}>Enregistrer</Button>
            <Button variant="outline" onClick={onCancel}>
              Annuler
            </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">Sélectionnez votre application</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="URL de rappel" placeholder="http://localhost:3000" value={callbacks} onChange={setCallbacks} />
            <Input id="logout" name="allowed_logout_urls" label="URL de déconnexion" placeholder="http://localhost:3000" value={logouts} onChange={setLogouts} />
            <Input id="origins" name="web_origins" label="Origines Web autorisées" placeholder="http://localhost:3000" value={origins} onChange={setOrigins} />

            <div className="flex gap-3 pt-2">
              <Button type="submit">Enregistrer</Button>
              <Button variant="outline" type="button" onClick={onCancel}>
                Annuler
              </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="S'inscrire à un compte Auth0" style={{
      width: "250px",
      height: "250px"
    }} />
        <span className="text-center" style={{
      width: "400px"
    }}>
          Inscrivez-vous pour obtenir un{" "}
          <a href="https://auth0.com/signup" target="_blank" rel="noopener noreferrer">
            compte Auth0
          </a>{" "}
          ou{" "}
          <span className="font-semibold text-primary dark:text-white cursor-pointer" onClick={() => console.log("log in")}>
            connectez-vous
          </span>{" "}
          à votre compte existant pour vous intégrer directement à votre propre locataire.
        </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"
    }}>
          S'inscrire
        </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>;
};

export const sections = [{
  id: "prerequisites",
  title: "Prérequis"
}, {
  id: "request-device-code",
  title: "Demander un code d’appareil"
}, {
  id: "receive-device-code",
  title: "Recevoir un code d’appareil"
}, {
  id: "request-device-activation",
  title: "Demander l’activation de l’appareil"
}, {
  id: "poll-the-token-endpoint",
  title: "Interroger le point de terminaison des jetons"
}, {
  id: "user-authorization",
  title: "Autorisation de l’utilisateur"
}, {
  id: "receive-tokens",
  title: "Recevoir les jetons"
}, {
  id: "call-your-api",
  title: "Appeler votre API"
}, {
  id: "refresh-tokens",
  title: "Actualiser les jetons"
}, {
  id: "troubleshooting",
  title: "Dépannage"
}, {
  id: "sample-implementations",
  title: "Exemples d’implémentation"
}, {
  id: "limitations",
  title: "Limitations"
}];

<Recipe>
  <Content>
    Ce tutoriel explique comment appeler votre API depuis un appareil à saisie limitée à l'aide du flux d'autorisation d'appareil. Nous vous recommandons de vous connecter pour
    suivre ce démarrage rapide avec des exemples configurés pour votre compte.

    Pour une expérience interactive, vous pouvez utiliser le [bac à sable Device Flow](https://auth0.github.io/device-flow-playground/).

    <Section id={sections[0].id} title={sections[0].titre} stepNumber="1">
      * Enregistrez une application native.
      * Assurez-vous que l’option **OIDC Conformant** est activée. Pour en savoir plus, consultez Authentification conforme à OIDC.
      * Ajoutez **Device Code** aux types d’octroi de l’application. Pour en savoir plus, consultez Mettre à jour les types d’octroi.
      * Ajoutez **Refresh Token** aux types d’octroi de l’application si vous souhaitez activer les jetons d’actualisation.
      * Configurez et activez au moins une connexion pour
        l’application.
      * Enregistrez votre API dans Auth0.
      * Activez **Allow Offline Access** si vous utilisez des jetons d’actualisation. Pour en savoir plus, consultez Paramètres de l’API.
      * Configurez les paramètres du code utilisateur de l’appareil pour définir le
        jeu de caractères, le format et la longueur du code utilisateur généré aléatoirement.
    </Section>

    <Section id={sections[1].id} title={sections[1].title} stepNumber="2">
      Lorsque l'utilisateur démarre l'application de l'appareil et souhaite l'autoriser, votre application doit demander un code d'appareil à l'API d'authentification Auth0 pour l'associer à la session utilisateur.

      Pour obtenir le code d'appareil, votre application doit appeler le [point de terminaison d'autorisation](https://auth0.com/docs/api/authentication#-post-oauth-device-code-) du flux d'autorisation d'appareil de l'API d'authentification :

      <AuthCodeGroup>
        ```bash cURL lines theme={null}
        curl --request post \
          --url 'https://{yourDomain}/oauth/device/code' \
          --header 'content-type: application/x-www-form-urlencoded'
        ```

        ```csharp C# lines theme={null}
        var client = new RestClient("https://{yourDomain}/oauth/device/code");
        var request = new RestRequest(Method.POST);
        request.AddHeader("content-type", "application/x-www-form-urlencoded");
        IRestResponse response = client.Execute(request);
        ```

        ```go Go lines  theme={null}
        package main
        import (
        "fmt"
        "net/http"
        "io/ioutil"
        )
        func main() {
        url := "https://{yourDomain}/oauth/device/code"
        req, _ := http.NewRequest("post", url, nil)
        req.Header.Add("content-type", "application/x-www-form-urlencoded")
        res, _ := http.DefaultClient.Do(req)
        defer res.Body.Close()
        body, _ := ioutil.ReadAll(res.Body)
        fmt.Println(res)
        fmt.Println(string(body))
        }
        ```

        ```java Java lines theme={null}
        HttpResponse<String> response = Unirest.post("https://{yourDomain}/oauth/device/code")
          .header("content-type", "application/x-www-form-urlencoded")
          .asString();
        ```

        ```javascript Node.JS lines theme={null}
        var axios = require("axios").default;
        var options = {
        method: 'post',
        url: 'https://{yourDomain}/oauth/device/code',
        headers: {'content-type': 'application/x-www-form-urlencoded'}
        };
        axios.request(options).then(function (response) {
        console.log(response.data);
        }).catch(function (error) {
        console.error(error);
        });
        ```

        ```objc Obj-C lines  theme={null}
        #import <Foundation/Foundation.h>
        NSDictionary *headers = @{ @"content-type": @"application/x-www-form-urlencoded" };
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/oauth/device/code"]
                                                          cachePolicy:NSURLRequestUseProtocolCachePolicy

                                                      timeoutInterval:10.0];

        [request setHTTPMethod:@"post"];
        [request setAllHTTPHeaderFields:headers];
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

                                                    if (error) {

                                                        NSLog(@&quot;%@&quot;, error);

                                                    } else {

                                                        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;

                                                        NSLog(@&quot;%@&quot;, httpResponse);

                                                    }

                                                }];

        [dataTask resume];
        ```

        ```php PHP lines  theme={null}
        $curl = curl_init();
        curl_setopt_array($curl, [
        CURLOPT_URL => "https://{yourDomain}/oauth/device/code",
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => "post",
        CURLOPT_HTTPHEADER => [
        &quot;content-type: application/x-www-form-urlencoded&quot;

        ],
        ]);
        $response = curl_exec($curl);
        $err = curl_error($curl);
        curl_close($curl);
        if ($err) {
        echo "cURL Error #:" . $err;
        } else {
        echo $response;
        }
        ```

        ```python Python lines theme={null}
        import http.client
        conn = http.client.HTTPSConnection("")
        headers = { 'content-type': "application/x-www-form-urlencoded" }
        conn.request("post", "/{yourDomain}/oauth/device/code", headers=headers)
        res = conn.getresponse()
        data = res.read()
        print(data.decode("utf-8"))
        ```

        ```ruby Ruby lines theme={null}
        require 'uri'
        require 'net/http'
        require 'openssl'
        url = URI("https://{yourDomain}/oauth/device/code")
        http = Net::HTTP.new(url.host, url.port)
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
        request = Net::HTTP::Post.new(url)
        request["content-type"] = 'application/x-www-form-urlencoded'
        response = http.request(request)
        puts response.read_body
        ```

        ```swift Swift lines  theme={null}
        import Foundation
        let headers = ["content-type": "application/x-www-form-urlencoded"]
        let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/oauth/device/code")! as URL,
                                            cachePolicy: .useProtocolCachePolicy,

                                        timeoutInterval: 10.0)

        request.httpMethod = "post"
        request.allHTTPHeaderFields = headers
        let session = URLSession.shared
        let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
        if (error != nil) {
        print(error)

        } else {
        let httpResponse = response as? HTTPURLResponse

        print(httpResponse)

        }
        })
        dataTask.resume()
        ```
      </AuthCodeGroup>
    </Section>

    <Section id={sections[2].id} title={sections[2].title} stepNumber="3">
      L’application sur l’appareil devrait recevoir une réponse HTTP 200 et une charge utile semblable à celle-ci :

      ```json lines theme={null}
      {
        "device_code": "GmRh...k9eS",
        "user_code": "WDJB-MJHT",
        "verification_uri": "https://my-tenant.auth0.com/device",
        "verification_uri_complete": "https://my-tenant.auth0.com/device?user_code=WDJB-MJHT",
        "expires_in": 900,
        "interval": 5
      }
      ```
    </Section>

    <Section id={sections[3].id} title={sections[3].title} stepNumber="4">
      Après que votre application sur l’appareil a reçu le `device_code` et le `user_code`, elle doit
      demander à l’utilisateur de se rendre à `verification_uri` et d’entrer le `user_code`.

      <Frame>
        <img src="https://mintcdn.com/translations/3nS3prIggmJG9TUI/docs/images/cdy7uua7fh8z/3Q9q41wocl6SojRoiGefXT/a98582a3c86740aaeb3d957f2dc4afe6/request-device-activation.png?fit=max&auto=format&n=3nS3prIggmJG9TUI&q=85&s=63ab39d633ebef6b8aa1ca47e28be708" width="1500" height="1082" data-path="docs/images/cdy7uua7fh8z/3Q9q41wocl6SojRoiGefXT/a98582a3c86740aaeb3d957f2dc4afe6/request-device-activation.png" />
      </Frame>

      <Info>
        Le `device_code` n’est pas destiné à être utilisé directement par l’utilisateur et ne doit pas être affiché pendant
        l’interaction afin d’éviter toute confusion.
      </Info>

      <Info>
        Si vous créez une CLI, vous pouvez omettre cette étape et ouvrir immédiatement le navigateur avec
        `verification_uri_complete`.
      </Info>
    </Section>

    <Section id={sections[4].id} title={sections[4].title} stepNumber="5">
      Pendant que votre application sur l’appareil attend que l’utilisateur l’active, elle doit appeler périodiquement le [point de terminaison POST /oauth/token de l’Authentication API](https://auth0.com/docs/api/authentication#-post-oauth-token-)
      et traiter la réponse en conséquence.

      <Info>
        Assurez-vous que votre application sur l’appareil attend le délai `interval` (en secondes) ou jusqu’à
        ce qu’elle reçoive une réponse indiquant le succès, selon la période la plus longue, afin d’éviter les problèmes de latence réseau.
      </Info>

      ```bash theme={null}
      curl --request POST \ 
        --url 'https://{yourDomain}/oauth/token' \
        --header 'content-type: application/x-www-form-urlencoded' \
        --data grant_type=urn:ietf:params:oauth:grant-type:device_code \
        --data device_code=AUTH0_SCOPES \
        --data 'client_id={yourClientId}'
      ```
    </Section>

    <Section id={sections[5].id} title={sections[5].title} stepNumber="6">
      L’utilisateur numérisera le code QR ou ouvrira la page d’activation et saisira le code utilisateur :

      <Frame>
        <img src="https://mintcdn.com/translations/c0RQ9V0YAcT0-8l5/docs/images/cdy7uua7fh8z/7IwrVX4s5a36CvfY95rKCo/dfc962ecde65502945741b1dc8b5c879/Device_Activation_-_English.png?fit=max&auto=format&n=c0RQ9V0YAcT0-8l5&q=85&s=4ca2f4ac3ad08e7629cae14f94870e23" width="493" height="669" data-path="docs/images/cdy7uua7fh8z/7IwrVX4s5a36CvfY95rKCo/dfc962ecde65502945741b1dc8b5c879/Device_Activation_-_English.png" />
      </Frame>

      Une page de confirmation s’affichera pour demander à l’utilisateur de confirmer qu’il s’agit du bon appareil :

      <Frame>
        <img src="https://mintcdn.com/translations/MV7tE-x71x8RWRES/docs/images/cdy7uua7fh8z/5dwhOyM1HRNwfV3Co4Da2o/424a2bc1a1bb44a54c587a0bcddd8222/Device_Confirmation_-_English.png?fit=max&auto=format&n=MV7tE-x71x8RWRES&q=85&s=8abd58dfb1cb31c6930d7c0478226ead" width="495" height="672" data-path="docs/images/cdy7uua7fh8z/5dwhOyM1HRNwfV3Co4Da2o/424a2bc1a1bb44a54c587a0bcddd8222/Device_Confirmation_-_English.png" />
      </Frame>

      L’utilisateur terminera la transaction en se connectant. Cette étape peut inclure un ou plusieurs des
      processus suivants :

      * Authentifier l’utilisateur
      * Rediriger l’utilisateur vers un fournisseur d’identité pour gérer l’authentification
      * Vérifier s’il existe des sessions SSO actives
      * Obtenir le consentement de l’utilisateur pour l’appareil, à moins qu’il n’ait déjà été accordé

      <Frame>
        <img src="https://mintcdn.com/translations/3nS3prIggmJG9TUI/docs/images/cdy7uua7fh8z/3GqqaNB7sjcAYTQiTnEEsn/7199a9b319283686057ca32c853f7a05/Login_screen_-_English.png?fit=max&auto=format&n=3nS3prIggmJG9TUI&q=85&s=3b8ce1c4a34fa7bacf72b53c69838400" width="497" height="925" data-path="docs/images/cdy7uua7fh8z/3GqqaNB7sjcAYTQiTnEEsn/7199a9b319283686057ca32c853f7a05/Login_screen_-_English.png" />
      </Frame>

      Une fois l’authentification réussie et le consentement accordé, l’invite de confirmation s’affichera :

      <Frame>
        <img src="https://mintcdn.com/translations/Dcx0M11uuptU53TX/docs/images/cdy7uua7fh8z/2TsQpMa8fzifiojuEXLvDo/484a49b775ec5a6b3522983a425599fe/Success_message_-_English.png?fit=max&auto=format&n=Dcx0M11uuptU53TX&q=85&s=b44d9cdd65cd73d0bc1401e682e06038" width="497" height="672" data-path="docs/images/cdy7uua7fh8z/2TsQpMa8fzifiojuEXLvDo/484a49b775ec5a6b3522983a425599fe/Success_message_-_English.png" />
      </Frame>

      À ce stade, l’utilisateur s’est authentifié et l’appareil a été autorisé.
    </Section>

    <Section id={sections[6].id} title={sections[6].title} stepNumber="7">
      Une fois que l’utilisateur a autorisé l’application de l’appareil, celle-ci reçoit une réponse HTTP 200 et la charge utile suivante :

      ```json lines theme={null}
      {
        "access_token": "eyJz93a...k4laUWw",
        "refresh_token": "GEbRxBN...edjnXbL",
        "id_token": "eyJ0XAi...4faeEoQ",
        "token_type": "Bearer",
        "expires_in": 86400
      }
      ```

      <Info>
        Vous devriez valider vos jetons avant de les enregistrer. Pour savoir comment faire, consultez Valider les jetons d’accès et Valider les jetons d’identité.
      </Info>

      Les jetons d’accès servent à appeler le [point de terminaison Get User Info](https://auth0.com/docs/api/authentication#get-user-info) de l’API d’authentification (si votre
      application d’appareil a demandé le scope `openid`) ou l’API spécifiée par le paramètre `audience`.
      Si vous appelez votre propre API, votre application d’appareil doit vérifier le jeton d’accès avant de l’utiliser.

      Les jetons d’identité contiennent des renseignements sur l’utilisateur qui doivent être [décodés et extraits](/fr-CA/docs/secure/tokens/id-tokens#id-token-payload). L’API d’authentification
      ne retourne un `id_token` que si votre application d’appareil a demandé le scope `openid`.

      Les jetons d’actualisation servent à obtenir un nouveau jeton d’accès ou
      un nouveau jeton d’identité après l’expiration du précédent. L’API d’authentification ne retourne un `refresh_token` que si
      le réglage **Autoriser l’accès hors ligne** est activé pour l’API spécifiée par le paramètre `audience`,
      et que votre application d’appareil a demandé le scope `offline_access`.
    </Section>

    <Section id={sections[7].id} title={sections[7].title} stepNumber="8">
      Pour appeler votre API, l’application de votre appareil doit transmettre le jeton d’accès en tant que jeton Bearer dans l’en-tête
      `Authorization` de votre requête HTTP.

      ```bash lines theme={null}
      curl --request GET \
        --url https://myapi.com/api \
        --header 'authorization: Bearer AUTH0_API_ACCESS_TOKEN' \
        --header 'content-type: application/json'
      ```
    </Section>

    <Section id={sections[8].id} title={sections[8].title} stepNumber="9">
      Pour obtenir un nouveau jeton d’accès pour un utilisateur, votre application sur l’appareil peut appeler le [point de terminaison POST /oauth/token de l’API d’authentification](https://auth0.com/docs/api/authentication#-post-oauth-token-) avec le
      paramètre `refresh_token`.

      ```bash lines theme={null}
      curl --request POST \
        --url 'https://{yourDomain}/oauth/token' \
        --header 'content-type: application/x-www-form-urlencoded' \
        --data grant_type=refresh_token \
        --data 'client_id={yourClientId}' \
        --data 'client_secret={yourClientSecret}' \
        --data refresh_token=AUTH0_REFRESH_TOKEN
      ```

      Si la requête réussit, l’application sur votre appareil reçoit une réponse HTTP 200 contenant la charge utile suivante :

      ```json lines theme={null}
      {
        "access_token": "eyJ...MoQ",
        "expires_in": 86400,
        "scope": "openid offline_access",
        "id_token": "eyJ...0NE",
        "token_type": "Bearer"
      }
      ```

      Pour en savoir plus sur les jetons d’actualisation, consultez [Jetons d’actualisation](https://auth0.com/docs/secure/tokens/refresh-tokens).
    </Section>

    <Section id={sections[9].id} title={sections[9].title} stepNumber="10">
      Des journaux du locataire sont créés pour chaque interaction
      et peuvent servir à résoudre les problèmes.

      ### Réponses de jeton

      Pendant que vous attendez que l’utilisateur autorise l’appareil, vous pourriez recevoir différentes réponses HTTP 4xx.

      #### Autorisation en attente

      Vous verrez cette erreur pendant que vous attendez que l’utilisateur passe à l’action. Continuez le sondage à l’aide de l’`interval`
      suggéré récupéré à l’étape précédente de ce tutoriel.

      ```json lines theme={null}
      `HTTP 403`
      {
      "error": "authorization_pending",
      "error_description": "..."
      }
      ```

      #### Ralentissez

      Vous effectuez des requêtes d’interrogation trop rapidement. Ralentissez et utilisez l’intervalle suggéré récupéré à l’étape précédente de ce
      tutoriel. Pour éviter que cette erreur ne se produise en raison de la latence réseau, vous devriez commencer à compter chaque intervalle après
      avoir reçu la réponse à la dernière requête d’interrogation.

      ```json lines theme={null}
      `HTTP 429`
      {
      "error": "slow_down",
      "error_description": "..."
      }
      ```

      #### Jeton expiré

      L’utilisateur n’a pas autorisé l’appareil assez rapidement, donc le `device_code` a expiré. Votre
      application doit aviser l’utilisateur que le flux a expiré et lui demander de redémarrer le flux.

      <Info>
        L’erreur `expired_token` n’est renvoyée qu’une seule fois. Par la suite, le point de terminaison renvoie l’erreur
        `invalid_grant`.
      </Info>

      ```json lines theme={null}
      `HTTP 403`
      {
      "error": "expired_token",
      "error_description": "..."
      }
      ```

      #### Accès refusé

      Si l’accès est refusé, vous obtenez :

      ```json lines theme={null}
      `HTTP 403`
      {
      "error": "access_denied",
      "error_description": "..."
      }
      ```

      Cela peut se produire pour diverses raisons, notamment :

      * L’utilisateur a refusé d’autoriser l’appareil.
      * Le serveur d’autorisation a rejeté la transaction.
      * Une Action configurée a refusé l’accès.
    </Section>

    <Section id={sections[10].id} title={sections[10].title} stepNumber="11">
      Consultez les exemples ci-dessous pour voir comment implémenter ce flux dans des applications réelles.

      * [Bac à sable
        d’autorisation de l’appareil](https://auth0.github.io/device-flow-playground/)
      * [AppleTV (Swift)](https://github.com/pushpabrol/auth0-device-flow-appletv) : Application simple montrant comment Auth0 peut être utilisé avec le
        flux d’autorisation de l’appareil sur une Apple TV.
      * [CLI (Node.js)](https://gist.github.com/panva/652c61e7d847e0ed99926c324fa91b36) : Exemple d’implémentation d’une CLI qui utilise le flux d’autorisation
        de l’appareil au lieu du flux du code d’autorisation. La principale différence est que votre CLI n’a pas besoin
        d’héberger un serveur Web ni d’écouter sur un port.
    </Section>

    <Section id={sections[11].id} title={sections[11].title} stepNumber="12">
      Pour utiliser le flux d’autorisation des appareils, une application sur appareil doit :

      * Prendre en charge l’indication du nom du serveur (SNI)
      * Être une application native
      * Avoir la [méthode d’authentification](/fr-CA/docs/secure/application-credentials#application-authentication-methods) définie à **None**
      * Être [conforme à OIDC](/fr-CA/docs/get-started/applications/application-settings#oauth)
      * Ne pas avoir été créée au moyen de l’enregistrement dynamique des applications

      De plus, le flux d’autorisation des appareils ne permet pas :

      * Les connexions sociales utilisant les clés de développeur Auth0, sauf si vous utilisez la nouvelle expérience Universal Login
      * D’accéder aux paramètres de chaîne de requête à partir d’une page de connexion hébergée ou d’Actions.
    </Section>

    ## Prochaines étapes

    Excellent travail ! Si vous êtes arrivé jusqu'ici, vous devriez maintenant avoir les fonctionnalités de connexion, de déconnexion et de profil utilisateur opérationnelles dans votre application.

    Ce tutoriel de démarrage rapide est maintenant terminé, mais il reste encore beaucoup à explorer. Pour en savoir plus sur ce que vous pouvez faire avec Auth0, consultez :

    * [Auth0 Dashboard](https://manage.auth0.com/dashboard/us/dev-gja8kxz4ndtex3rq) - Découvrez comment configurer et gérer votre locataire Auth0 ainsi que vos applications
    * [Auth0 Marketplace](https://marketplace.auth0.com/) - Découvrez les intégrations que vous pouvez activer pour étendre les fonctionnalités d’Auth0
  </Content>

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

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

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

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

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

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

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

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

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

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

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

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