Creata pagina con "= 🔐 Autenticazione SSO LinkedIn – Flusso Personalizzato Masticationpedia = Questa guida documenta l’intero flusso di login con LinkedIn su Masticationpedia, realizzato senza estensioni MediaWiki instabili. Il sistema è basato su tre file PHP custom nella cartella `/oauth/`, e consente il login diretto dopo autenticazione LinkedIn, usando come chiave primaria l'`ID LinkedIn (sub)`. == 📁 Struttura directory == Inserire i seguenti file nella cartella del server..."
 
Nessun oggetto della modifica
Riga 1: Riga 1:
= 🔐 Autenticazione SSO LinkedIn – Flusso Personalizzato Masticationpedia =
= Documentazione Tecnica SSO LinkedIn per Masticationpedia =


Questa guida documenta l’intero flusso di login con LinkedIn su Masticationpedia, realizzato senza estensioni MediaWiki instabili. Il sistema è basato su tre file PHP custom nella cartella `/oauth/`, e consente il login diretto dopo autenticazione LinkedIn, usando come chiave primaria l'`ID LinkedIn (sub)`.
Questa pagina descrive la struttura tecnica per integrare il login Single Sign-On (SSO) tramite LinkedIn, con creazione automatica dell’utente su MediaWiki.


== 📁 Struttura directory ==
Tutti i file sono caricati nella cartella: <code>/oauth/</code>
Inserire i seguenti file nella cartella del server:


<code>/var/www/html/masticationpedia-staging/oauth/</code>
== 📁 1. linkedin-login.php ==


Contenuti da inserire:
<syntaxhighlight lang="php">
* <code>linkedin-login.php</code>
<?php
* <code>linkedin-callback.php</code>
// linkedin-login.php
* <code>create_mw_user_direct.php</code>


== 🔁 Redirect URL nella App Developer di LinkedIn ==
$client_id = 'TUO_CLIENT_ID';
Nel pannello sviluppatori LinkedIn (https://www.linkedin.com/developers/):
$redirect_uri = 'https://staging.masticationpedia.org/oauth/linkedin-callback.php';
* Vai su → Products → "Sign in with LinkedIn"
$scope = 'openid email profile';
* Inserisci come redirect ufficiale:
$state = bin2hex(random_bytes(16));
  <pre>https://staging.masticationpedia.org/oauth/linkedin-callback.php</pre>


== 🧠 Flusso in sintesi ==
$url = "https://www.linkedin.com/oauth/v2/authorization?response_type=code"
1. L’utente clicca "Login con LinkedIn" e apre `linkedin-login.php`
    . "&client_id={$client_id}"
2. LinkedIn autentica l’utente → ritorna su `linkedin-callback.php` con `code`
    . "&redirect_uri=" . urlencode($redirect_uri)
3. Il server ottiene l’access token e i dati utente (nome, email, ID)
    . "&state={$state}"
4. I dati vengono salvati in `$_SESSION`
    . "&scope=" . urlencode($scope);
5. Il file `create_mw_user_direct.php` crea e logga l’utente direttamente in MediaWiki


---
header("Location: $url");
 
exit;
== 📜 Codice: linkedin-login.php ==
</syntaxhighlight>


```php
'''Funzione''': 
<?php
Genera l'URL di richiesta login e consensi a LinkedIn. Avvia il flusso OAuth2.
session_start();
$client_id = 'TUO_CLIENT_ID';
$redirect_uri = 'https://staging.masticationpedia.org/oauth/linkedin-callback.php';


$linkedin_auth_url = 'https://www.linkedin.com/oauth/v2/authorization?' . http_build_query([
----
    'response_type' => 'code',
    'client_id' => $client_id,
    'redirect_uri' => $redirect_uri,
    'scope' => 'r_liteprofile r_emailaddress'
]);


header('Location: ' . $linkedin_auth_url);
== 📁 2. linkedin-callback.php ==
exit;


<syntaxhighlight lang="php">
<?php
<?php
session_start();
// linkedin-callback.php


$client_id = 'TUO_CLIENT_ID';
ini_set('display_errors', 1);
$client_secret = 'TUO_CLIENT_SECRET';
error_reporting(E_ALL);
$redirect_uri = 'https://staging.masticationpedia.org/oauth/linkedin-callback.php';
$log = __DIR__ . '/linkedin_callback.log';
function log_debug($msg) {
    global $log;
    file_put_contents($log, "[".date('Y-m-d H:i:s')."] $msg\n", FILE_APPEND);
}


if (!isset($_GET['code'])) exit('Missing code.');
if (!isset($_GET['code'], $_GET['state'])) exit('Errore: code/state mancanti');
$code = $_GET['code'];
$code = $_GET['code'];


$token_url = 'https://www.linkedin.com/oauth/v2/accessToken';
$token_url = 'https://www.linkedin.com/oauth/v2/accessToken';
$response = file_get_contents($token_url . '?' . http_build_query([
$post = [
     'grant_type' => 'authorization_code',
     'grant_type' => 'authorization_code',
     'code' => $code,
     'code' => $code,
     'redirect_uri' => $redirect_uri,
     'redirect_uri' => 'https://staging.masticationpedia.org/oauth/linkedin-callback.php',
     'client_id' => $client_id,
     'client_id' => 'TUO_CLIENT_ID',
     'client_secret' => $client_secret
     'client_secret' => 'TUO_CLIENT_SECRET'
]));
];
$token_data = json_decode($response, true);
$ch = curl_init($token_url);
$access_token = $token_data['access_token'] ?? null;
curl_setopt_array($ch, [
if (!$access_token) exit('No access token.');
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query($post),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false
]);
$resp = curl_exec($ch);
curl_close($ch);
$data = json_decode($resp, true);
$token = $data['access_token'] ?? '';
if (!$token) exit('Errore token');


function getLinkedInData($url, $token) {
$ch2 = curl_init('https://api.linkedin.com/v2/userinfo');
    $opts = [
curl_setopt_array($ch2, [
        'http' => [
    CURLOPT_RETURNTRANSFER => true,
            'method' => 'GET',
    CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"],
            'header' => "Authorization: Bearer $token\r\n"
     CURLOPT_SSL_VERIFYPEER => false
        ]
]);
     ];
$userinfo = curl_exec($ch2);
    return json_decode(file_get_contents($url, false, stream_context_create($opts)), true);
curl_close($ch2);
}
$user = json_decode($userinfo, true);
$sub = $user['sub'] ?? '';
$name = $user['name'] ?? '';
$email = $user['email'] ?? '';


$profile = getLinkedInData('https://api.linkedin.com/v2/me', $access_token);
if (!$sub || !$name) exit('Errore dati incompleti');
$emailData = getLinkedInData('https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', $access_token);


$sub = $profile['id'] ?? '';
$ch3 = curl_init('https://staging.masticationpedia.org/oauth/create_mw_user_direct.php');
$name = $profile['localizedFirstName'] . ' ' . $profile['localizedLastName'];
curl_setopt_array($ch3, [
$email = $emailData['elements'][0]['handle~']['emailAddress'] ?? '';
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'sub' => $sub,
        'name' => $name,
        'email' => $email
    ]),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false
]);
curl_exec($ch3);
curl_close($ch3);


$_SESSION['linkedin_user'] = [
header("Location: https://staging.masticationpedia.org/wiki/Main_Page");
    'sub' => $sub,
exit;
    'name' => $name,
?>
    'email' => $email
</syntaxhighlight>
];


header('Location: /oauth/create_mw_user_direct.php');
'''Funzione''':
exit;
Scambia il code con un token, ottiene i dati da LinkedIn e li invia al file che crea l'utente.


<?php
----
session_start();
if (!isset($_SESSION['linkedin_user'])) {
    exit('No LinkedIn user in session.');
}


$userData = $_SESSION['linkedin_user'];
== 📁 3. create_mw_user_direct.php ==
$sub = $userData['sub'];
$name = $userData['name'];
$email = $userData['email'];


require_once __DIR__ . '/../includes/WebStart.php';
<syntaxhighlight lang="php">
<?php
use MediaWiki\MediaWikiServices;
use MediaWiki\MediaWikiServices;


$user = User::newFromName($sub);
ini_set('display_errors', 1);
if ($user && $user->getId()) {
error_reporting(E_ALL);
     $user->setCookies();
$log = __DIR__ . '/mw_user_creation.log';
     header('Location: /wiki/Main_Page');
function log_debug($m) {
    exit;
     global $log;
     file_put_contents($log, "[".date('Y-m-d H:i:s')."] $m\n", FILE_APPEND);
}
}


$user = User::newFromName($sub);
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
$user->addToDatabase();
    http_response_code(405);
$user->setEmail($email);
    exit('❌ Metodo non consentito.');
$user->setRealName($name);
}
$user->setToken();
$user->saveSettings();


$user->setCookies();
$sub = $_POST['sub']  ?? 'no-sub';
User::setCurrent($user);
$name = $_POST['name'] ?? 'Anon';
$email = $_POST['email'] ?? 'noemail@mpedia';


header('Location: /wiki/Main_Page');
file_put_contents(__DIR__.'/incoming_user_debug.json',
exit;
    json_encode(['ts'=>date('c'),'sub'=>$sub,'name'=>$name,'email'=>$email], JSON_PRETTY_PRINT)."\n", FILE_APPEND);


if (!$sub || !$name) {
    log_debug("Missing user data");
    http_response_code(400);
    exit;
}


require_once __DIR__ . '/../includes/WebStart.php';
$services = MediaWikiServices::getInstance();
$userFactory = $services->getUserFactory();
$userLookup = $services->getUserLookup();


== Roadmap funzionale del flusso SSO LinkedIn ==
$username = substr(preg_replace('/[^A-Za-z0-9]/','_',$name . '_' . substr($sub,0,6)), 0, 50);


{| class="wikitable"
$userObj = \User::newFromName($username);
! Passaggio !! File coinvolto !! Azione eseguita
if ($userObj && $userObj->getId() !== 0) {
|-
    log_debug("User exists: $username");
| 1 || <code>linkedin-login.php</code> || Reindirizza l’utente verso la pagina di login di LinkedIn
    exit;
|-
}
| 2 || <code>linkedin-callback.php</code> || Riceve il parametro "code", lo scambia per un access_token e recupera dati profilo/email
|-
| 3 || <code>create_mw_user_direct.php</code> || Crea il nuovo utente su MediaWiki (usando sub come username) o lo riconosce se già presente
|-
| 4 || MediaWiki || Mostra la pagina <code>Main_Page</code> con utente autenticato tramite cookie
|}
 
 
 
== 🔐 Sicurezza e best practice ==
 
L’ID LinkedIn (sub) viene usato come nome utente per evitare abusi
 
L’email viene salvata ma non viene usata per login
 
Il sistema può essere potenziato in futuro con usergroups personalizzati
 
Per limitare accessi, si può creare una whitelist di sub ammessi (opzionale)


== 📌 Note finali ==
$userObj = $userFactory->newFromName($username);
$userObj->addToDatabase();
$userObj->setEmail($email);
$userObj->setRealName($name);
$userObj->setToken();
$userObj->saveSettings();


Il sistema è pensato per essere semplice, autonomo, stabile
$services->getDBLoadBalancerFactory()->commitMasterChanges(__METHOD__);
$entry = new \ManualLogEntry('newusers','create');
$entry->setPerformer($userObj);
$entry->setTarget($userObj->getUserPage());
$entry->setComment('Created via LinkedIn SSO');
$id = $entry->insert();
$entry->publish($id);


Funziona anche se MediaWiki è aggiornato o spostato su un nuovo dominio
log_debug("User created: $username");
?>
</syntaxhighlight>


Può essere integrato con dashboard o pannelli membri approvati
'''Funzione''': 
Riceve i dati utente da LinkedIn e crea direttamente un account MediaWiki completo, visibile in Special:Users.


== 🧱 Progetto curato da ==
----
''Gianni Frisardi – Fondatore Masticationpedia''
''in collaborazione con ChatGPT''


== ✅ Riassunto finale ==


* **3 file**: `linkedin-login.php`, `linkedin-callback.php`, `create_mw_user_direct.php`
* **Cartella consigliata**: `/oauth/`
* **Log consigliati**:
  * `linkedin_callback.log`
  * `incoming_user_debug.json`
  * `mw_user_creation.log`


----


---
== 🧪 Debug e test ==


## ✅ PROSSIMI PASSI (subito):
1. Inserisci correttamente i tuoi `client_id` e `client_secret` nei file.
1. Vai su `https://staging.masticationpedia.org/wiki/SSO_LinkedIn_Personalizzato`
2. Collega il pulsante o link di login LinkedIn alla pagina:
2. **Crea la pagina** e incolla tutto il contenuto sopra
  <code>/oauth/linkedin-login.php</code>
3. Salva e usala come **documentazione interna ufficiale**
3. Verifica nei log se la creazione dell’utente avviene correttamente.
4. Controlla la pagina <code>Special:Users</code> per vedere se l’utente è stato registrato.


---
----


🎯 **Hai ora una guida completa**, documentata, funzionante, replicabile.
== 🔐 Sicurezza finale ==


Se vuoi possiamo:
Dopo la messa in produzione:
- Aggiungere un **link rapido alla dashboard**
* Rimuovere le righe `ini_set('display_errors', 1)` e `log_debug(...)`
- Inserire un **pulsante “Richiedi Accesso”** direttamente da lì
* Proteggere l'accesso alla cartella `/oauth/` se non strettamente necessario.
- Oppure generare un PDF per conservarlo anche offline
* Salvare backup regolari di `incoming_user_debug.json` e log.


Dimmi tu!
----

Versione delle 22:41, 4 lug 2025

Documentazione Tecnica SSO LinkedIn per Masticationpedia

Questa pagina descrive la struttura tecnica per integrare il login Single Sign-On (SSO) tramite LinkedIn, con creazione automatica dell’utente su MediaWiki.

Tutti i file sono caricati nella cartella: /oauth/

📁 1. linkedin-login.php

<?php
// linkedin-login.php

$client_id = 'TUO_CLIENT_ID';
$redirect_uri = 'https://staging.masticationpedia.org/oauth/linkedin-callback.php';
$scope = 'openid email profile';
$state = bin2hex(random_bytes(16));

$url = "https://www.linkedin.com/oauth/v2/authorization?response_type=code"
     . "&client_id={$client_id}"
     . "&redirect_uri=" . urlencode($redirect_uri)
     . "&state={$state}"
     . "&scope=" . urlencode($scope);

header("Location: $url");
exit;

Funzione: Genera l'URL di richiesta login e consensi a LinkedIn. Avvia il flusso OAuth2.


📁 2. linkedin-callback.php

<?php
// linkedin-callback.php

ini_set('display_errors', 1);
error_reporting(E_ALL);
$log = __DIR__ . '/linkedin_callback.log';
function log_debug($msg) {
    global $log;
    file_put_contents($log, "[".date('Y-m-d H:i:s')."] $msg\n", FILE_APPEND);
}

if (!isset($_GET['code'], $_GET['state'])) exit('Errore: code/state mancanti');
$code = $_GET['code'];

$token_url = 'https://www.linkedin.com/oauth/v2/accessToken';
$post = [
    'grant_type' => 'authorization_code',
    'code' => $code,
    'redirect_uri' => 'https://staging.masticationpedia.org/oauth/linkedin-callback.php',
    'client_id' => 'TUO_CLIENT_ID',
    'client_secret' => 'TUO_CLIENT_SECRET'
];
$ch = curl_init($token_url);
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query($post),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false
]);
$resp = curl_exec($ch);
curl_close($ch);
$data = json_decode($resp, true);
$token = $data['access_token'] ?? '';
if (!$token) exit('Errore token');

$ch2 = curl_init('https://api.linkedin.com/v2/userinfo');
curl_setopt_array($ch2, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"],
    CURLOPT_SSL_VERIFYPEER => false
]);
$userinfo = curl_exec($ch2);
curl_close($ch2);
$user = json_decode($userinfo, true);
$sub = $user['sub'] ?? '';
$name = $user['name'] ?? '';
$email = $user['email'] ?? '';

if (!$sub || !$name) exit('Errore dati incompleti');

$ch3 = curl_init('https://staging.masticationpedia.org/oauth/create_mw_user_direct.php');
curl_setopt_array($ch3, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'sub' => $sub,
        'name' => $name,
        'email' => $email
    ]),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_SSL_VERIFYPEER => false
]);
curl_exec($ch3);
curl_close($ch3);

header("Location: https://staging.masticationpedia.org/wiki/Main_Page");
exit;
?>

Funzione: Scambia il code con un token, ottiene i dati da LinkedIn e li invia al file che crea l'utente.


📁 3. create_mw_user_direct.php

<?php
use MediaWiki\MediaWikiServices;

ini_set('display_errors', 1);
error_reporting(E_ALL);
$log = __DIR__ . '/mw_user_creation.log';
function log_debug($m) {
    global $log;
    file_put_contents($log, "[".date('Y-m-d H:i:s')."] $m\n", FILE_APPEND);
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    exit('❌ Metodo non consentito.');
}

$sub = $_POST['sub']   ?? 'no-sub';
$name = $_POST['name'] ?? 'Anon';
$email = $_POST['email'] ?? 'noemail@mpedia';

file_put_contents(__DIR__.'/incoming_user_debug.json',
    json_encode(['ts'=>date('c'),'sub'=>$sub,'name'=>$name,'email'=>$email], JSON_PRETTY_PRINT)."\n", FILE_APPEND);

if (!$sub || !$name) {
    log_debug("Missing user data");
    http_response_code(400);
    exit;
}

require_once __DIR__ . '/../includes/WebStart.php';
$services = MediaWikiServices::getInstance();
$userFactory = $services->getUserFactory();
$userLookup = $services->getUserLookup();

$username = substr(preg_replace('/[^A-Za-z0-9]/','_',$name . '_' . substr($sub,0,6)), 0, 50);

$userObj = \User::newFromName($username);
if ($userObj && $userObj->getId() !== 0) {
    log_debug("User exists: $username");
    exit;
}

$userObj = $userFactory->newFromName($username);
$userObj->addToDatabase();
$userObj->setEmail($email);
$userObj->setRealName($name);
$userObj->setToken();
$userObj->saveSettings();

$services->getDBLoadBalancerFactory()->commitMasterChanges(__METHOD__);
$entry = new \ManualLogEntry('newusers','create');
$entry->setPerformer($userObj);
$entry->setTarget($userObj->getUserPage());
$entry->setComment('Created via LinkedIn SSO');
$id = $entry->insert();
$entry->publish($id);

log_debug("User created: $username");
?>

Funzione: Riceve i dati utente da LinkedIn e crea direttamente un account MediaWiki completo, visibile in Special:Users.


✅ Riassunto finale

  • **3 file**: `linkedin-login.php`, `linkedin-callback.php`, `create_mw_user_direct.php`
  • **Cartella consigliata**: `/oauth/`
  • **Log consigliati**:
 * `linkedin_callback.log`
 * `incoming_user_debug.json`
 * `mw_user_creation.log`

🧪 Debug e test

1. Inserisci correttamente i tuoi `client_id` e `client_secret` nei file. 2. Collega il pulsante o link di login LinkedIn alla pagina:

  /oauth/linkedin-login.php

3. Verifica nei log se la creazione dell’utente avviene correttamente. 4. Controlla la pagina Special:Users per vedere se l’utente è stato registrato.


🔐 Sicurezza finale

Dopo la messa in produzione:

  • Rimuovere le righe `ini_set('display_errors', 1)` e `log_debug(...)`
  • Proteggere l'accesso alla cartella `/oauth/` se non strettamente necessario.
  • Salvare backup regolari di `incoming_user_debug.json` e log.