openvibers/openvibe.live Commits

Restream OAuth: a redirect mismatch says which URL to add to the platform's app (the owner saw only Twitch's 'redirect_uri does not match') (#162)

OP OpenVibers committed 28 days ago · 235a5db91d · 2 files, +37 −1

Changes

ddiff --git a/server/streaming/restream-routes.js b/server/streaming/restream-routes.js
iindex 1f038022..178134c4 100644
---- a/server/streaming/restream-routes.js
++++ b/server/streaming/restream-routes.js
@@@ -642,6 +642,19 @@ router.get('/oauth/:platform/start', requireAuth, async (req, res) => {
}
});
++/**
++ * What a provider's refusal means for the person connecting. A redirect mismatch (Twitch "redirect_mismatch", or a
++ * description saying the redirect URI does not match) is the developer app's redirect list, not anything they did:
++ * the owner tried to connect Twitch four times on 10-16 and saw only "The provided redirect_uri does not match".
++ */
++function oauthErrorText(platform, err, description) {
++ const name = (platformOAuth.PLATFORMS[platform] && platformOAuth.PLATFORMS[platform].name) || String(platform).charAt(0).toUpperCase() + String(platform).slice(1);
++ if (/redirect/i.test(String(err || '')) || /redirect[_ ]?ur[il]/i.test(String(description || ''))) {
++ return `${name} refused the sign-in because this site's address is not on its app's list. In the ${name} developer console, add ${platformOAuth.redirectUri(platform)} as an OAuth redirect URL for the app whose client ID is set in Admin → Integrations, then connect again. Until then, paste the stream key into the destination by hand.`;
++ }
++ return description || err;
++}
++
// ── GET /oauth/:platform/callback — exchange code, save connection + destination
router.get('/oauth/:platform/callback', async (req, res) => {
const { platform } = req.params;
@@@ -652,7 +665,7 @@ router.get('/oauth/:platform/callback', async (req, res) => {
const { code, state, error: oauthErr, error_description } = req.query;
if (oauthErr) {
console.warn(`[Restream OAuth] ${platform} provider error: ${oauthErr} — ${error_description || ''} | our redirect_uri=${platformOAuth.redirectUri(platform)}`);
-- return send({ ok: false, platform, error: error_description || oauthErr });
++ return send({ ok: false, platform, error: oauthErrorText(platform, oauthErr, error_description) });
}
if (!code || !state) return send({ ok: false, platform, error: 'Missing authorization code' });
@@@ -749,3 +762,4 @@ router.delete('/oauth/:platform/connection', requireAuth, async (req, res) => {
});
module.exports = router;
++module.exports.oauthErrorText = oauthErrorText;
ddiff --git a/test/restream-oauth-errors.test.js b/test/restream-oauth-errors.test.js
nnew file mode 100644
iindex 00000000..2c57d57c
---- /dev/null
++++ b/test/restream-oauth-errors.test.js
@@@ -0,0 +1,22 @@
++/**
++ * A provider that refuses a restream sign-in says what to do (server/streaming/restream-routes.js). Twitch answers
++ * "redirect_mismatch — The provided redirect_uri does not match" when its developer app does not list this site's
++ * callback; the owner tried to connect Twitch four times on 2026-10-16 and saw only that sentence. The page now names
++ * the exact URL to add and the hand-pasted key as the way meanwhile. Other errors pass through as the provider wrote
++ * them.
++ *
++ * node test/restream-oauth-errors.test.js
++ */
++'use strict';
++const assert = require('assert');
++const { oauthErrorText } = require('../server/streaming/restream-routes');
++const { redirectUri } = require('../server/integrations/platform-oauth');
++
++const twitch = oauthErrorText('twitch', 'redirect_mismatch', 'The provided redirect_uri does not match');
++assert.ok(twitch.startsWith('Twitch refused the sign-in because this site\'s address is not on its app\'s list.'), twitch);
++assert.ok(twitch.includes(redirectUri('twitch')) && /\/api\/restream\/oauth\/twitch\/callback/.test(twitch), 'names the exact URL to add');
++assert.ok(/paste the stream key into the destination by hand/.test(twitch), 'and the way meanwhile');
++assert.ok(/YouTube refused the sign-in/.test(oauthErrorText('youtube', 'invalid_request', 'Invalid parameter value for redirect_uri: Mismatch')), 'a description about the redirect URI counts too');
++assert.strictEqual(oauthErrorText('kick', 'access_denied', 'The user denied the request'), 'The user denied the request', 'other errors pass through');
++assert.strictEqual(oauthErrorText('kick', 'access_denied', ''), 'access_denied');
++console.log('restream oauth errors: all checks passed');