Aller au contenu

Intégration LinkedIn — Profil personnel Bernard

Contexte

Objectif : publier automatiquement sur le profil LinkedIn personnel de Bernard à chaque article publié sur tekdhi.dev. Ce document couvre la procédure complète depuis la création de l'app jusqu'au premier post réussi, ainsi que la correction des #open-graph tags pour un rendu riche.


Procédure complète — Obtenir le token OAuth

1. Créer l'app LinkedIn Developer

  • Aller sur https://www.linkedin.com/developers/apps/new
  • App name : tekdhi-publisher
  • LinkedIn Page : sélectionner la page tekdhi.dev (à créer si absente — gratuit sur https://www.linkedin.com/company/setup/new/)
  • Logo obligatoire (n'importe quelle image)
  • Cliquer Create app

2. Activer les produits requis (onglet Products)

Deux produits obligatoires — cliquer Request access sur chacun :

Produit Rôle Scope accordé
Share on LinkedIn Poster sur le profil w_member_social
Sign In with LinkedIn using OpenID Connect Accès profil + email openid profile email

⚠️ Sans le second produit, LinkedIn retourne une erreur 404 à l'ouverture de l'URL d'autorisation.

3. Configurer la Redirect URI (onglet Auth)

Ajouter : http://localhost:7654/callback

LinkedIn n'accepte pas les redirections HTTP vers une IP publique — localhost uniquement.

4. Récupérer Client ID et Client Secret (onglet Auth)

Valeurs à renseigner dans le script d'authentification.

5. Ouvrir le tunnel SSH depuis la machine locale

Dans un terminal local (pas le VPS) :

ssh -L 7654:localhost:7654 kanmaber@178.105.46.57 -N

Ce tunnel fait transiter localhost:7654 de la machine locale vers le VPS port 7654.

6. Lancer le script d'authentification sur le VPS

python3 /home/kanmaber/scripts/linkedin_auth.py

Le script : 1. Affiche l'URL d'autorisation LinkedIn 2. Lance un serveur HTTP sur le port 7654 3. Attend le callback LinkedIn (timeout : 5 min)

7. Ouvrir l'URL dans le navigateur et autoriser

LinkedIn affiche une page d'autorisation → cliquer Autoriser.

Le navigateur redirige vers localhost:7654/callback?code=... → tunnel → VPS → script capture le code → échange contre un token → sauvegarde.

8. Token sauvegardé

Fichier : /home/kanmaber/.tekdhi_social_tokens (chmod 600)

{
  "linkedin": {
    "access_token": "...",
    "expires_in": 5183999,
    "scope": "email,openid,profile,w_member_social",
    "member_id": "_5gBQzvFxa",
    "person_urn": "urn:li:person:_5gBQzvFxa"
  }
}

⚠️ Token expire dans ~60 jours. Relancer linkedin_auth.py pour renouveler.


Poster sur le profil LinkedIn

API utilisée

POST https://api.linkedin.com/v2/ugcPosts

Structure du payload

{
  "author": "urn:li:person:_5gBQzvFxa",
  "lifecycleState": "PUBLISHED",
  "specificContent": {
    "com.linkedin.ugc.ShareContent": {
      "shareCommentary": { "text": "Texte du post..." },
      "shareMediaCategory": "ARTICLE",
      "media": [{
        "status": "READY",
        "description": { "text": "Extrait de l'article" },
        "originalUrl": "https://tekdhi.dev/blog/slug-article",
        "title": { "text": "Titre de l'article" }
      }]
    }
  },
  "visibility": {
    "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
  }
}

Commande curl de référence

TOKEN=$(python3 -c "import json; print(json.load(open('/home/kanmaber/.tekdhi_social_tokens'))['linkedin']['access_token'])")
curl -X POST https://api.linkedin.com/v2/ugcPosts \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Restli-Protocol-Version: 2.0.0" \
  -d "$PAYLOAD"

La réponse contient l'ID du post : {"id":"urn:li:share:XXXX"}.


Correction Open Graph — tekdhi.dev (SPA React)

Problème

tekdhi.dev est une SPA React. Les crawlers (LinkedIn, Twitter, Google) n'exécutent pas JavaScript → ils voient les OG tags par défaut du index.html au lieu de ceux de l'article.

Solution : bot detection Caddy + endpoint OG Express

Caddyfile (/home/kanmaber/shared_infra/Caddyfile)

tekdhi.dev {
    @bot {
        header_regexp User-Agent `(?i)(linkedinbot|twitterbot|facebookexternalhit|googlebot|bingbot|slackbot|whatsapp|telegram)`
        path /blog/*
    }
    handle @bot {
        rewrite * /og{path}
        reverse_proxy 127.0.0.1:3902
    }
    handle {
        root * /home/kanmaber/tekdhi-landing
        try_files {path} /index.html
        file_server
    }
}

Quand un bot visite /blog/mon-slug → Caddy réécrit en /og/blog/mon-slug → Express sert du HTML statique avec les bonnes balises.

Route Express (server.ts)

Route ajoutée : GET /og/blog/:slug

Retourne un HTML minimal avec : - og:type, og:url, og:title, og:description, og:image - og:site_name, og:locale - article:published_time, article:author - name: author - twitter:card, twitter:title, twitter:description, twitter:image - <meta http-equiv="refresh"> pour rediriger les humains vers la vraie page

Image OG par défaut

Fichier : /home/kanmaber/tekdhi-landing/og-default.png
Source : /home/kanmaber/docs/tekdih_dev/Bannière2.png (PNG original, 3 Mo)

Utiliser le PNG directement — le format JPEG produit un rendu flou sur cette image.

Recharger Caddy après modification du Caddyfile

cd /home/kanmaber/shared_infra && docker compose restart caddy

Rebuilder le backend après modification de server.ts

cd /home/kanmaber/projets/tekdhi-site/BackEnd
npm run build
docker compose --env-file .env up -d --build

Vérifier l'aperçu LinkedIn

Post Inspector LinkedIn :

https://www.linkedin.com/post-inspector/inspect/https:%2F%2Ftekdhi.dev%2Fblog%2F<slug>


À faire

  • [ ] Publication sur la LinkedIn Page tekdhi.dev — nécessite w_organization_social scope + URN de la page (ex: urn:li:organization:XXXXX)
  • [ ] Publication automatique au clic "Publier" — déclencher le post LinkedIn depuis le backend tekdhi quand statut passe de BROUILLON à PUBLIE

Liens connexes