Changeset 603d246 in Klonkt


Ignore:
Timestamp:
07/21/2026 01:54:46 AM (7 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
c3d12a6
Parents:
e685f55
git-author:
Robin <roboburr@…> (07/21/2026 01:54:34 AM)
git-committer:
Robin <roboburr@…> (07/21/2026 01:54:46 AM)
Message:

Feature: show the exact Patreon redirect URI in the paid admin

Bart hit Patreon's own error page ("Redirect URI .../paid/callback is not
supported by client") because the redirect URI our OAuth flow sends was
never whitelisted in his Patreon client, and the admin page told him
nowhere what that URI is. Once we redirect to patreon.com with an
unregistered redirect_uri, Patreon refuses to send the visitor back (open
redirect protection) and shows its own JSON error, which we cannot skin.

The only real defence is correct setup, so the admin now shows the exact
redirect URI to paste into the Patreon client, with a copy button. The URI
is built the same way paid.js builds it (PUBLIC_BASE_URL or the request
host + /paid/callback), so they always match.

Changed files:
src/routes/admin-paid.js

  • compute redirectUri (matches paid.js) and pass it to the view

src/views/pages/admin-paid.ejs

  • "Zet deze redirect-URI in je Patreon-client" block + copy button

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

Location:
src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • src/routes/admin-paid.js

    re685f55 r603d246  
    1818const router = express.Router();
    1919
     20// The redirect URI the owner MUST whitelist in their Patreon client. Must match
     21// exactly what paid.js sends, or Patreon shows its own error page (which we
     22// cannot skin) instead of returning the visitor to us.
     23const redirectUri = (req) =>
     24  (process.env.PUBLIC_BASE_URL || `${req.protocol}://${req.get('host')}`).replace(/\/+$/, '') + '/paid/callback';
     25
    2026function gate(req, res) {
    2127  if (!premiumUnlocked()) {
     
    3440    status: PaidPatreon.ownerStatus(res.locals.site.id),
    3541    secretReady: cryptoBoxReady(),
     42    redirectUri: redirectUri(req),
    3643    saved: req.query.saved === '1',
    3744    error: req.query.error || null,
  • src/views/pages/admin-paid.ejs

    re685f55 r603d246  
    2323    <% } %>
    2424  </p>
     25
     26  <div style="border:1px solid var(--border,#333);border-radius:10px;padding:.9rem 1rem;margin:0 0 1.3rem;background:color-mix(in srgb,var(--accent,#6b8f71) 8%,transparent)">
     27    <p style="margin:0 0 .4rem;font-weight:600">Zet deze redirect-URI in je Patreon-client</p>
     28    <p style="margin:0 0 .6rem;color:var(--ink-soft,#888);font-size:.9rem">
     29      Bij je Patreon API-client, onder <em>Redirect URIs</em>, moet exact deze regel staan.
     30      Klopt hij niet, dan geeft Patreon een foutmelding in plaats van je supporters terug te sturen.
     31    </p>
     32    <div style="display:flex;gap:.5rem;align-items:center">
     33      <input id="pd-redirect" type="text" readonly value="<%= redirectUri %>" style="flex:1;min-width:0;font-family:monospace;font-size:.9rem">
     34      <button type="button" id="pd-copy" class="btn">Kopieer</button>
     35    </div>
     36  </div>
    2537
    2638  <form method="post" action="/admin/paid" style="display:flex;flex-direction:column;gap:.9rem">
     
    5870  <p style="margin-top:1.5rem"><a href="/admin">&larr; Terug naar Beheer</a></p>
    5971</div>
     72
     73<script>
     74(function () {
     75  var field = document.getElementById('pd-redirect');
     76  var btn = document.getElementById('pd-copy');
     77  if (!field || !btn) return;
     78  field.addEventListener('focus', function () { field.select(); });
     79  btn.addEventListener('click', function () {
     80    field.select();
     81    var done = function () { var t = btn.textContent; btn.textContent = 'Gekopieerd'; setTimeout(function () { btn.textContent = t; }, 1400); };
     82    if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(field.value).then(done, function () { try { document.execCommand('copy'); done(); } catch (e) {} }); }
     83    else { try { document.execCommand('copy'); done(); } catch (e) {} }
     84  });
     85})();
     86</script>
Note: See TracChangeset for help on using the changeset viewer.