<%@ Page Title="Companion broker (server-side OAuth)" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" %>
<asp:Content ID="TitleContent" ContentPlaceHolderID="TitleContent" runat="server">Companion broker - AjaxUploader v5.3 Demos</asp:Content>
<asp:Content ID="HeaderContent" ContentPlaceHolderID="HeaderTitle" runat="server">Companion broker (server-side OAuth)</asp:Content>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<div class="demo-card">
  <div class="demo-card-header">Keep cloud credentials server-side with a self-hosted broker</div>
  <div class="demo-card-body">
    <p class="demo-description">
      The in-browser cloud pickers (Drive, Dropbox, OneDrive, Box) use implicit OAuth - the access token lives in client JavaScript. The <strong>Companion broker</strong> moves the whole OAuth exchange to your own server: the provider client secret and access token never reach the browser, and remote files are proxied through the broker. It is a Node/Express middleware modeled on Uppy Companion, shipped as <code>multipleupload/server/companion</code> with zero new dependencies.
    </p>

    <div class="demo-expect"><strong>What you should see:</strong> cloud pickers backed by the self-hosted Companion broker: OAuth happens on YOUR server and the browser only ever holds a session cookie.</div> <div class="alert alert-warning mt-2" role="alert">
      <strong>This demo needs a running broker.</strong> The picker below is wired to <code>/companion</code>; it works once you mount the broker on your server (see the snippet) and register Dropbox/Box OAuth apps. Without a broker the source button shows a clear "not configured" state.
    </div>

    <div class="card p-3 mt-3" style="background:#fff;border:1px solid #e2e8f0;">
      <div class="d-flex gap-2 mb-2">
        <button type="button" class="btn btn-primary" id="startBtn_companionOut">Start upload</button>
        <button type="button" class="btn btn-outline-secondary" id="clearBtn_companionOut">Clear queue</button>
      </div>
      <div id="companionOut"></div>
    </div>
    <script> (function () {
      function init() {
        if (!window.AjaxUploader || typeof AjaxUploader.create !== 'function') return;
        var out = document.getElementById('companionOut');
        var uploader = AjaxUploader.create(out, {
          uploadUrl: '<%= ResolveUrl("~/ajaxupload.axd/upload") %>',
          multiple: true,
          autoUpload: false,
          remoteSources: ['companion']
        });
        // Point at your mounted broker. Until it exists this stays "not configured".
        AjaxUploader.configureRemoteSource('companion', {
          companionUrl: '/companion',
          providers: ['dropbox', 'box', 'googledrive', 'onedrive']
        });
        if (uploader && typeof uploader._renderRemoteSources === 'function') uploader._renderRemoteSources();
        var startButton = document.getElementById('startBtn_companionOut');
        var clearButton = document.getElementById('clearBtn_companionOut');
        if (startButton) {
          startButton.addEventListener('click', function () {
            if (uploader && typeof uploader.upload === 'function') uploader.upload();
          });
        }
        if (clearButton) {
          clearButton.addEventListener('click', function () {
            if (uploader && typeof uploader.clear === 'function') uploader.clear();
          });
        }
      }
      if (document.readyState !== 'loading') init();
      else document.addEventListener('DOMContentLoaded', init);
    })(); </script>

    <h5 class="mt-4">1. Mount the broker (Node / Express)</h5>
    <div class="code-panel">
<pre><code><span style="color:#94a3b8">const</span> express = require('express');
<span style="color:#94a3b8">const</span> { createCompanion } = require('multipleupload/server/companion');

app.use(createCompanion({
    basePath: '/companion',
    secret: process.env.COMPANION_SECRET,        <span style="color:#94a3b8">// signs state + session cookie</span>
    providers: {
        dropbox:     { clientId: '...', clientSecret: '...', redirectUri: 'https://app/companion/dropbox/callback' },
        box:         { clientId: '...', clientSecret: '...', redirectUri: 'https://app/companion/box/callback' },
        googledrive: { clientId: '...', clientSecret: '...', redirectUri: 'https://app/companion/googledrive/callback' },
        onedrive:    { clientId: '...', clientSecret: '...', redirectUri: 'https://app/companion/onedrive/callback' },
    },
}));</code></pre>
    </div>

    <h5 class="mt-4">2. Point the client at it</h5>
    <div class="code-panel">
<pre><code>AjaxUploader.configureRemoteSource('companion', {
    companionUrl: '/companion',
    providers: ['dropbox', 'box', 'googledrive', 'onedrive']
});
AjaxUploader.create('#uploader', { remoteSources: ['companion'] });</code></pre>
    </div>

    <h5 class="mt-4">What the broker guarantees</h5>
    <ul class="mb-3">
      <li>Provider <strong>client secret</strong> stays on your server - never sent to the browser.</li>
      <li>OAuth <strong>access token</strong> is stored server-side (pluggable token store); the browser only holds a signed, HttpOnly session cookie.</li>
      <li>OAuth <code>state</code> is HMAC-signed for CSRF protection.</li>
      <li>Remote file bytes are <strong>proxied</strong> through the broker (server-to-server fetch), then handed to the normal upload pipeline.</li>
    </ul>
    <p class="text-muted small mb-0">Built-in providers: Dropbox, Box, Google Drive, and OneDrive. Add more via the same <code>providers</code> config shape.</p>
  </div>
</div>
</asp:Content>
