SSO LinkedIn Personalizzato: differenze tra le versioni
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: | ||
= | = Documentazione Tecnica SSO LinkedIn per Masticationpedia = | ||
Questa | 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: <code>/oauth/</code> | |||
== 📁 1. linkedin-login.php == | |||
<syntaxhighlight lang="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; | |||
</syntaxhighlight> | |||
'''Funzione''': | |||
Genera l'URL di richiesta login e consensi a LinkedIn. Avvia il flusso OAuth2. | |||
---- | |||
== 📁 2. linkedin-callback.php == | |||
<syntaxhighlight lang="php"> | |||
<?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'])) exit(' | 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'; | ||
$ | $post = [ | ||
'grant_type' => 'authorization_code', | 'grant_type' => 'authorization_code', | ||
'code' => $code, | 'code' => $code, | ||
'redirect_uri' => | 'redirect_uri' => 'https://staging.masticationpedia.org/oauth/linkedin-callback.php', | ||
'client_id' => | 'client_id' => 'TUO_CLIENT_ID', | ||
'client_secret' => $ | 'client_secret' => 'TUO_CLIENT_SECRET' | ||
])); | ]; | ||
$ | $ch = curl_init($token_url); | ||
$ | curl_setopt_array($ch, [ | ||
if (!$ | 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; | |||
?> | |||
</syntaxhighlight> | |||
'''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 == | |||
<syntaxhighlight lang="php"> | |||
<?php | |||
use MediaWiki\MediaWikiServices; | 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"); | |||
?> | |||
</syntaxhighlight> | |||
'''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. | |||
1. | 2. Collega il pulsante o link di login LinkedIn alla pagina: | ||
<code>/oauth/linkedin-login.php</code> | |||
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. | |||
--- | ---- | ||
== 🔐 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. | |||
---- |
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.