mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-20 21:48:40 +02:00
fix(oauth): déduire l'URI de redirection du domaine public réel
Sans APP_URL, Facebook et TikTok recevaient http://localhost:5555/api/{facebook,tiktok}/callback : une URI que les fournisseurs refusent, donc aucune page connectable derrière un reverse proxy. Le repli localhost n'est plus utilisé que hors requête HTTP ; en service, la base publique est reconstruite depuis X-Forwarded-Proto / X-Forwarded-Host (trust proxy est déjà activé). - `resolvePublicBaseUrl(req)` centralise la résolution (APP_URL prioritaire) - l'URI de redirection est déduite de la requête au démarrage du flux, à l'échange du code et dans l'affichage des paramètres : les trois restent identiques, comme l'exige OAuth - docker-compose ne force plus APP_URL à localhost, qui rendait l'auto détection inopérante - cookie de session en `secure: 'auto'` à défaut d'APP_URL, pour ne pas émettre un cookie non sécurisé derrière un proxy HTTPS Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Db2EazEcvnqgTTeg8oGTif
This commit is contained in:
9 files changed
+119
-27
No files matched your search
@@ -21,7 +21,9 @@
|
||||
* chaîne se renouvelle toute seule.
|
||||
*/
|
||||
|
||||
import type { Request } from 'express';
|
||||
import { storage } from '../storage';
|
||||
import { resolvePublicBaseUrl } from '../utils/public_url';
|
||||
|
||||
const GRAPH_VERSION = process.env.FACEBOOK_GRAPH_VERSION?.trim() || 'v19.0';
|
||||
const GRAPH_URL = `https://graph.facebook.com/${GRAPH_VERSION}`;
|
||||
@@ -99,12 +101,16 @@ export class FacebookOAuthService {
|
||||
}
|
||||
}
|
||||
|
||||
getRedirectUri(): string {
|
||||
/**
|
||||
* URI de redirection OAuth. `req` permet de la déduire du domaine réellement
|
||||
* utilisé quand `APP_URL` n'est pas défini : sans lui on renverrait
|
||||
* `http://localhost:5555`, que Facebook rejette.
|
||||
*/
|
||||
getRedirectUri(req?: Request): string {
|
||||
if (process.env.FACEBOOK_REDIRECT_URI) {
|
||||
return process.env.FACEBOOK_REDIRECT_URI;
|
||||
}
|
||||
const base = (process.env.APP_URL || 'http://localhost:5555').replace(/\/$/, '');
|
||||
return `${base}/api/facebook/callback`;
|
||||
return `${resolvePublicBaseUrl(req)}/api/facebook/callback`;
|
||||
}
|
||||
|
||||
getScopes(): string {
|
||||
@@ -112,12 +118,12 @@ export class FacebookOAuthService {
|
||||
}
|
||||
|
||||
/** URL d'autorisation à ouvrir pour connecter les pages d'un compte Facebook. */
|
||||
async buildAuthorizationUrl(state: string): Promise<string> {
|
||||
async buildAuthorizationUrl(state: string, req?: Request): Promise<string> {
|
||||
const { appId } = await this.getCredentials();
|
||||
|
||||
const params = new URLSearchParams({
|
||||
client_id: appId,
|
||||
redirect_uri: this.getRedirectUri(),
|
||||
redirect_uri: this.getRedirectUri(req),
|
||||
state,
|
||||
scope: this.getScopes(),
|
||||
response_type: 'code',
|
||||
@@ -131,13 +137,13 @@ export class FacebookOAuthService {
|
||||
* L'étape intermédiaire (token court) n'est jamais conservée : seul le token
|
||||
* longue durée rend les tokens de page permanents.
|
||||
*/
|
||||
async exchangeCodeForUserToken(code: string): Promise<FacebookLongLivedToken> {
|
||||
async exchangeCodeForUserToken(code: string, req?: Request): Promise<FacebookLongLivedToken> {
|
||||
const { appId, appSecret } = await this.getCredentials();
|
||||
|
||||
const shortLived = await this.graphGet<{ access_token?: string }>('/oauth/access_token', {
|
||||
client_id: appId,
|
||||
client_secret: appSecret,
|
||||
redirect_uri: this.getRedirectUri(),
|
||||
redirect_uri: this.getRedirectUri(req),
|
||||
code,
|
||||
}, "échange du code d'autorisation");
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
* compte). Le code s'adapte automatiquement à ce que le compte autorise.
|
||||
*/
|
||||
|
||||
import type { Request } from 'express';
|
||||
import { storage } from '../storage';
|
||||
import { resolvePublicBaseUrl } from '../utils/public_url';
|
||||
import type { SocialPage } from '@shared/schema';
|
||||
|
||||
const AUTH_BASE_URL = 'https://www.tiktok.com/v2/auth/authorize/';
|
||||
@@ -98,26 +100,30 @@ export class TiktokService {
|
||||
}
|
||||
}
|
||||
|
||||
getRedirectUri(): string {
|
||||
/**
|
||||
* URI de redirection OAuth. `req` sert de repli quand `APP_URL` n'est pas
|
||||
* défini : le domaine public est alors lu sur la requête plutôt que remplacé
|
||||
* par `http://localhost:5555`.
|
||||
*/
|
||||
getRedirectUri(req?: Request): string {
|
||||
if (process.env.TIKTOK_REDIRECT_URI) {
|
||||
return process.env.TIKTOK_REDIRECT_URI;
|
||||
}
|
||||
const base = (process.env.APP_URL || 'http://localhost:5555').replace(/\/$/, '');
|
||||
return `${base}/api/tiktok/callback`;
|
||||
return `${resolvePublicBaseUrl(req)}/api/tiktok/callback`;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL d'autorisation à ouvrir pour connecter le compte TikTok d'un magasin.
|
||||
* Chaque magasin répète ce flux : un compte = une ligne dans social_pages.
|
||||
*/
|
||||
async buildAuthorizationUrl(state: string): Promise<string> {
|
||||
async buildAuthorizationUrl(state: string, req?: Request): Promise<string> {
|
||||
const { clientKey } = await this.getCredentials();
|
||||
|
||||
const params = new URLSearchParams({
|
||||
client_key: clientKey,
|
||||
scope: process.env.TIKTOK_SCOPES || DEFAULT_SCOPES,
|
||||
response_type: 'code',
|
||||
redirect_uri: this.getRedirectUri(),
|
||||
redirect_uri: this.getRedirectUri(req),
|
||||
state,
|
||||
});
|
||||
|
||||
@@ -125,7 +131,7 @@ export class TiktokService {
|
||||
}
|
||||
|
||||
/** Échange le code d'autorisation contre un couple access/refresh token. */
|
||||
async exchangeCodeForToken(code: string): Promise<TiktokTokenResponse> {
|
||||
async exchangeCodeForToken(code: string, req?: Request): Promise<TiktokTokenResponse> {
|
||||
const { clientKey, clientSecret } = await this.getCredentials();
|
||||
|
||||
return await this.requestToken({
|
||||
@@ -134,7 +140,7 @@ export class TiktokService {
|
||||
// TikTok renvoie le code URL-encodé dans la query string
|
||||
code: decodeURIComponent(code),
|
||||
grant_type: 'authorization_code',
|
||||
redirect_uri: this.getRedirectUri(),
|
||||
redirect_uri: this.getRedirectUri(req),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user