PageRenderTime 69ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/library/oauth/SocialAuth.php

https://github.com/onliners/Goteo
PHP | 607 lines | 392 code | 74 blank | 141 comment | 63 complexity | 658cc5cdebde6bde30c4c8c6cf8bbdc1 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /*
  3. ini_set('display_errors', 1);
  4. ini_set('log_errors', 1);
  5. error_reporting(E_ALL);
  6. //*/
  7. //Includes all necessary files for oAuth
  8. $dir = dirname(__FILE__);
  9. include_once("$dir/epioauth/EpiCurl.php");
  10. include_once("$dir/epioauth/EpiOAuth.php");
  11. include_once("$dir/epioauth/EpiTwitter.php");
  12. include_once("$dir/linkedinoauth.php");
  13. include_once("$dir/facebook.class.php");
  14. include_once("$dir/openid.php");
  15. /**
  16. * Suportat:
  17. * OAuth o similar: twitter, facebook, linkedin
  18. * OpenId: google
  19. *
  20. * identities:
  21. * Google : https://www.google.com/accounts/o8/id
  22. * Google profile : http://www.google.com/profiles/~YOURUSERNAME
  23. * Yahoo : https://me.yahoo.com
  24. * AOL : https://www.aol.com
  25. * WordPress : http://YOURBLOG.wordpress.com
  26. * LiveJournal : http://www.livejournal.com/openid/server.bml
  27. * */
  28. class SocialAuth {
  29. public $host;
  30. public $callback_url;
  31. public $provider;
  32. public $original_provider;
  33. public $last_error = '';
  34. //datos que se recopilan
  35. public $user_data = array('username' => null, 'name' => null, 'email' => null, 'profile_image_url' => null, 'website' => null, 'about' => null, 'location'=>null,'twitter'=>null,'facebook'=>null,'google'=>null,'identica'=>null,'linkedin'=>null);
  36. //datos que se importaran (si se puede) a la tabla 'user'
  37. public $import_user_data = array('name', 'about', 'location', 'twitter', 'facebook', 'google', 'identica', 'linkedin');
  38. public $tokens = array('twitter'=>array('token'=>'','secret'=>''), 'facebook'=>array('token'=>'','secret'=>''), 'linkedin'=>array('token'=>'','secret'=>''), 'openid'=>array('token'=>'','secret'=>'')); //secretos generados en el oauth
  39. protected $twitter_id;
  40. protected $twitter_secret;
  41. protected $facebook_id;
  42. protected $facebook_secret;
  43. protected $linkedin_id;
  44. protected $linkedin_secret;
  45. protected $openid_secret;
  46. protected $openid_server;
  47. public $openid_public_servers = array(
  48. "Google" => "https://www.google.com/accounts/o8/id",
  49. "Yahoo" => "https://me.yahoo.com",
  50. "myOpenid" => "http://myopenid.com/",
  51. "AOL" => "https://www.aol.com",
  52. "Ubuntu" => "https://login.ubuntu.com",
  53. "LiveJournal" => "http://www.livejournal.com/openid/server.bml",
  54. );
  55. /**
  56. * @param $provider : 'twitter', 'facebook', 'linkedin', 'any_openid_server'
  57. * */
  58. function __construct($provider='') {
  59. $this->host = SITE_URL;
  60. $this->callback_url = SITE_URL . '/user/oauth?return=' . $provider;
  61. $this->twitter_id = OAUTH_TWITTER_ID;
  62. $this->twitter_secret = OAUTH_TWITTER_SECRET;
  63. $this->facebook_id = OAUTH_FACEBOOK_ID;
  64. $this->facebook_secret = OAUTH_FACEBOOK_SECRET;
  65. $this->linkedin_id = OAUTH_LINKEDIN_ID;
  66. $this->linkedin_secret = OAUTH_LINKEDIN_SECRET;
  67. $this->openid_secret = OAUTH_OPENID_SECRET;
  68. if(in_array($provider,array('twitter', 'facebook', 'linkedin'))) {
  69. $this->provider = $provider;
  70. $this->original_provider = $provider;
  71. }
  72. else {
  73. //OpenId providers
  74. $this->openid_server = $this->openid_public_servers[$provider];
  75. if(empty($this->openid_server)) $this->openid_server = $provider;
  76. $this->original_provider = $provider;
  77. $this->provider = 'openid';
  78. }
  79. }
  80. /**
  81. * conecta con el servicio de oauth, redirecciona a la pagina para la autentificacion
  82. * */
  83. public function authenticate() {
  84. switch ($this->provider) {
  85. case 'twitter':
  86. return $this->authenticateTwitter();
  87. break;
  88. case 'facebook':
  89. return $this->authenticateFacebook();
  90. break;
  91. case 'linkedin':
  92. return $this->authenticateLinkedin();
  93. break;
  94. case 'openid':
  95. return $this->authenticateOpenid();
  96. break;
  97. default:
  98. $this->last_error = 'oauth-unknown-provider';
  99. return false;
  100. }
  101. return true;
  102. }
  103. /**
  104. * Autentica con twitter, redirige a Twitter para que el usuario acepte
  105. * */
  106. public function authenticateOpenid() {
  107. try {
  108. $openid = new \LightOpenID($this->host);
  109. $openid->identity = $this->openid_server;
  110. //standard data provided
  111. $openid->required = array(
  112. 'namePerson/friendly',
  113. 'namePerson',
  114. 'namePerson/first',
  115. 'namePerson/last',
  116. 'contact/email',
  117. 'contact/country/home',
  118. //'pref/language'
  119. );
  120. $openid->returnUrl = $this->callback_url;
  121. $url = $openid->authUrl();
  122. header("Location: $url");
  123. exit;
  124. }
  125. catch(Exception $e){
  126. $this->last_error = $e->getMessage()." 1/ ".get_class($e);
  127. return false;
  128. }
  129. return true;
  130. }
  131. /**
  132. * Autentica con twitter, redirige a Twitter para que el usuario acepte
  133. * */
  134. public function authenticateTwitter() {
  135. try {
  136. $twitterObj = new \EpiTwitter($this->twitter_id, $this->twitter_secret);
  137. $url = $twitterObj->getAuthenticateUrl(null,array('oauth_callback' => $this->callback_url));
  138. header("Location: $url");
  139. exit;
  140. }
  141. catch(Exception $e){
  142. $this->last_error = $e->getMessage()." 1/ ".get_class($e);
  143. return false;
  144. }
  145. return true;
  146. }
  147. /**
  148. * Autentica con Facebook, redirige a Facebook para que el usuario acepte
  149. * */
  150. public function authenticateFacebook() {
  151. try {
  152. $obj = new \Facebook($this->facebook_id, $this->facebook_secret,$this->callback_url);
  153. $url = $obj->start(true,"email"); //Permisos que se solicitan, por ejemplo: user_about_me,email,offline_access
  154. header("Location: $url");
  155. exit;
  156. }
  157. catch(Exception $e){
  158. $this->last_error = $e->getMessage()." 1/ ".get_class($e);
  159. return false;
  160. }
  161. return true;
  162. }
  163. /**
  164. * Autentica con LinkedIn, redirige a LinkedIn para que el usuario acepte
  165. * */
  166. public function authenticateLinkedin() {
  167. try {
  168. //do the authentication:
  169. //get public tokens
  170. $to = new \LinkedInOAuth($this->linkedin_id, $this->linkedin_secret);
  171. // This call can be unreliable for some providers if their servers are under a heavy load, so
  172. // retry it with an increasing amount of back-off if there's a problem.
  173. $maxretrycount = 1;
  174. $retrycount = 0;
  175. while ($retrycount<$maxretrycount) {
  176. $tok = $to->getRequestToken($this->callback_url);
  177. if (isset($tok['oauth_token']) && isset($tok['oauth_token_secret']))
  178. break;
  179. $retrycount += 1;
  180. sleep($retrycount*5);
  181. }
  182. if(empty($tok['oauth_token']) || empty($tok['oauth_token_secret'])) {
  183. $this->last_error = "oauth-token-request-error";
  184. return false;
  185. }
  186. //en linkedin hay que guardar los token de autentificacion para usarlos
  187. //despues para obtener los tokens de acceso,
  188. $_SESSION['linkedin_token'] = $tok;
  189. //set URL
  190. $url = $to->getAuthorizeURL($tok['oauth_token']);
  191. header("Location: $url");
  192. exit;
  193. }
  194. catch(Exception $e){
  195. $this->last_error = $e->getMessage()." 1/ ".get_class($e);
  196. return false;
  197. }
  198. return true;
  199. }
  200. /**
  201. * obtención de datos en los proveedores de oauth mediante login con los tokens que se obtienen al retornar del authenticate
  202. * */
  203. public function login() {
  204. switch ($this->provider) {
  205. case 'twitter':
  206. return $this->loginTwitter();
  207. break;
  208. case 'facebook':
  209. return $this->loginFacebook();
  210. break;
  211. case 'linkedin':
  212. return $this->loginLinkedin();
  213. break;
  214. case 'openid':
  215. return $this->loginOpenid();
  216. break;
  217. }
  218. }
  219. /**
  220. * Login con facebook
  221. * */
  222. public function loginFacebook() {
  223. try {
  224. $obj = new \Facebook($this->facebook_id, $this->facebook_secret,$this->callback_url);
  225. $token = $obj->callback();
  226. if(!$token) {
  227. $this->last_error = "oauth-facebook-access-denied";
  228. return false;
  229. }
  230. $this->tokens['facebook']['token'] = $token;
  231. //print_R($token);
  232. //echo 'facebook_access_token: ' . $token;
  233. //guardar los tokens en la base datos si se quieren usar mas adelante!
  234. //con los tokens podems acceder a la info del user, hay que recrear el objecto con los tokens privados
  235. $res = json_decode($obj->makeRequest($token,"https://graph.facebook.com/me","GET"));
  236. if($res->error) {
  237. $this->last_error = $res->error->message;
  238. return false;
  239. }
  240. //ver todos los datos disponibles:
  241. //print_r($res);die;
  242. $this->user_data['name'] = $res->name;
  243. if($res->username) $this->user_data['username'] = $res->username;
  244. if($res->email) $this->user_data['email'] = $res->email;
  245. if($res->website) $this->user_data['website'] = $res->website; //ojo, pueden ser varias lineas con varias webs
  246. if($res->about) $this->user_data['about'] = $res->about;
  247. if($res->location->name) $this->user_data['location'] = $res->location->name;
  248. if($res->id) $this->user_data['profile_image_url'] = "http://graph.facebook.com/".$res->id."/picture?type=large";
  249. //facebook link
  250. if($res->link) $this->user_data['facebook'] = $res->link;
  251. return true;
  252. }
  253. catch(Exception $e){
  254. $this->last_error = $e->getMessage()." 1/ ".get_class($e);
  255. return false;
  256. }
  257. return true;
  258. }
  259. /**
  260. * Login con linkedin
  261. * */
  262. public function loginLinkedin() {
  263. try {
  264. //recuperar tokens de autentificacion
  265. $tok = $_SESSION['linkedin_token'];
  266. $to = new \LinkedInOAuth($this->linkedin_id, $this->linkedin_secret,$tok['oauth_token'],$tok['oauth_token_secret']);
  267. //obtenemos los tokens de acceso
  268. $tok = $to->getAccessToken($_GET['oauth_verifier']);
  269. //borramos los tokens de autentificacion de la session, ya no nos sirven
  270. //unset($_SESSION['linkedin_token']);
  271. if(empty($tok['oauth_token']) || empty($tok['oauth_token_secret'])) {
  272. $this->last_error = "oauth-linkedin-access-denied";
  273. return false;
  274. }
  275. //guardar los tokens en la base datos si se quieren usar mas adelante!
  276. //con los tokens podems acceder a la info del user, hay que recrear el objecto con los tokens privados
  277. $this->tokens['linkedin']['token'] = $tok['oauth_token'];
  278. $this->tokens['linkedin']['secret'] = $tok['oauth_token_secret'];
  279. $profile_result = $to->oAuthRequest('http://api.linkedin.com/v1/people/~:(id,first-name,last-name,summary,public-profile-url,picture-url,headline,interests,twitter-accounts,member-url-resources:(url),positions:(company),location:(name))');
  280. $profile_data = simplexml_load_string($profile_result);
  281. $this->user_data['name'] = trim($profile_data->{"first-name"} . " " . $profile_data->{"last-name"});
  282. if($profile_data->{"public-profile-url"}) {
  283. //linkedin link
  284. $this->user_data['linkedin'] = current($profile_data->{"public-profile-url"});
  285. //username from url
  286. $this->user_data['username'] = basename($this->user_data['linkedin']);
  287. }
  288. if($profile_data->{"member-url-resources"}->{"member-url"}) {
  289. $urls = array();
  290. foreach($profile_data->{"member-url-resources"}->{"member-url"} as $url) {
  291. $urls[] = current($url->url);
  292. }
  293. $this->user_data['website'] .= implode("\n",$urls);
  294. }
  295. if($profile_data->headline) $this->user_data['about'] = current($profile_data->headline);
  296. if($profile_data->location->name) $this->user_data['location'] = current($profile_data->location->name);
  297. if($profile_data->{"picture-url"}) $this->user_data['profile_image_url'] = current($profile_data->{"picture-url"});
  298. //si el usuario tiene especificada su cuenta twitter
  299. if($profile_data->{"twitter-accounts"}->{"twitter-account"}) $this->user_data['twitter'] = 'http://twitter.com/' . current($profile_data->{"twitter-accounts"}->{"twitter-account"}->{"provider-account-name"});
  300. //ver todos los datos disponibles:
  301. //print_r($profile_data);print_r($this->user_data);die;
  302. return true;
  303. }
  304. catch(Exception $e){
  305. $this->last_error = $e->getMessage()." 1/ ".get_class($e);
  306. return false;
  307. }
  308. return true;
  309. }
  310. /**
  311. * Login con twitter
  312. * */
  313. public function loginTwitter() {
  314. if($_GET['denied']) {
  315. //comprovar si el retorno contiene la variable de denegación
  316. $this->last_error = "oauth-twitter-access-denied";
  317. return false;
  318. }
  319. try {
  320. $twitterObj = new \EpiTwitter($this->twitter_id, $this->twitter_secret);
  321. $twitterObj->setToken($_GET['oauth_token']);
  322. $token = $twitterObj->getAccessToken();
  323. //print_R($token);
  324. //echo 'twitter_oauth_token: ' . $token->oauth_token . ' / twitter_oauth_token_secret: ' . $token->oauth_token_secret;
  325. //guardar los tokens en la base datos si se quieren usar mas adelante!
  326. //con los tokens podems acceder a la info del user, hay que recrear el objecto con los tokens privados
  327. $twitterObj = new \EpiTwitter($this->twitter_id, $this->twitter_secret,$token->oauth_token,$token->oauth_token_secret);
  328. $this->tokens['twitter']['token'] = $token->oauth_token;
  329. $this->tokens['twitter']['secret'] = $token->oauth_token_secret;
  330. $userInfo = $twitterObj->get_accountVerify_credentials();
  331. //Twitter NO RETORNA el email!!!
  332. $this->user_data['username'] = $userInfo->screen_name;
  333. $this->user_data['name'] = $userInfo->name;
  334. $this->user_data['profile_image_url'] = str_replace("_normal","",$userInfo->profile_image_url);
  335. //twitter link
  336. $this->user_data['twitter'] = 'http://twitter.com/'.$userInfo->screen_name;
  337. if($userInfo->url) $this->user_data['website'] = $userInfo->url;
  338. if($userInfo->location) $this->user_data['location'] = $userInfo->location;
  339. if($userInfo->description) $this->user_data['about'] = $userInfo->description;
  340. return true;
  341. }
  342. catch(Exception $e){
  343. $this->last_error = $e->getMessage()." 1/ ".get_class($e);
  344. return false;
  345. }
  346. return true;
  347. }
  348. /**
  349. * Login con openid
  350. * */
  351. public function loginOpenid() {
  352. $openid = new \LightOpenID($this->host);
  353. if($openid->mode) {
  354. if ($openid->mode == 'cancel') {
  355. $this->last_error = "oauth-openid-access-denied";
  356. return false;
  357. } elseif($openid->validate()) {
  358. $data = $openid->getAttributes();
  359. //print_r($data);print_r($openid);print_r($openid->identity);die;
  360. /*
  361. //por seguridad no aceptaremos conexions de OpenID que no nos devuelvan el email
  362. if(!Goteo\Library\Check::mail($data['contact/email'])) {
  363. $this->last_error = "oauth-openid-email-required";
  364. return false;
  365. }*/
  366. $this->user_data['email'] = $data['contact/email'];
  367. $this->user_data['username'] = $data['namePerson/friendly'];
  368. $this->user_data['name'] = $data['namePerson'];
  369. if(empty($this->user_data['name'])) $this->user_data['name'] = trim($data['namePerson/first'] . " " . $data['namePerson/last']);
  370. if($data['contact/country/home']) $this->user_data['location'] = $data['contact/country/home'];
  371. //no se usan tokens para openid, guardamos el servidor como token
  372. $this->tokens['openid']['token'] = $this->openid_server;
  373. //como secreto usaremos un hash basado an algo que sea unico para cada usuario (la identidad openid es una URL única)
  374. //$this->tokens['openid']['secret'] = sha1($this->openid_server.$this->openid_secret.$data['contact/email']);
  375. $this->tokens['openid']['secret'] = $openid->identity;
  376. return true;
  377. }
  378. else {
  379. $this->last_error = "oauth-openid-not-logged";
  380. return false;
  381. }
  382. }
  383. $this->last_error = "oauth-openid-not-logged";
  384. return false;
  385. }
  386. /**
  387. * Hace el login en goteo si es posible (existen tokens o el email es el mismo)
  388. * Guarda los tokens si se encuentra el usuario
  389. *
  390. * @param $force_login logea en goteo sin comprovar que la contraseña esté vacía o que el usuario este activo
  391. * */
  392. public function goteoLogin($force_login = false) {
  393. /*****
  394. * POSIBLE PROBLEMA:
  395. * en caso de que ya se haya dado permiso a la aplicación goteo,
  396. * el token da acceso al login del usuario aunque este haya cambiado el email en goteo.org
  397. * es un problema? o da igual...
  398. *****/
  399. //Comprovar si existe el mail en la base de datos
  400. $username = "";
  401. //comprovar si existen tokens
  402. $query = Goteo\Core\Model::query('SELECT id FROM user WHERE id = (SELECT user FROM user_login WHERE provider = :provider AND oauth_token = :token AND oauth_token_secret = :secret)', array(':provider' => $this->provider, ':token' => $this->tokens[$this->provider]['token'], ':secret' => $this->tokens[$this->provider]['secret']));
  403. $username = $query->fetchColumn();
  404. if(empty($username)) {
  405. //no existen tokens, comprovamos si existe el email
  406. /**
  407. * Problema de seguridad, si el proveedor openid nos indica un mail que no pertenece al usuario
  408. * da un método para acceder a los contenidos de cualquier usuario
  409. * por tanto, en caso de que no existan tokens, se deberá preguntar la contraseña al usuario
  410. * si el usuario no tiene contraseña, podemos permitir el acceso directo o denegarlo (mas seguro)
  411. * */
  412. $query = Goteo\Core\Model::query('SELECT id,password FROM user WHERE email = ?', array($this->user_data['email']));
  413. if($user = $query->fetchObject()) {
  414. $username = $user->id;
  415. //sin no existe contraseña permitimos acceso
  416. //if(!empty($user->password) && !$force_login) {
  417. //No permitimos acceso si no existe contraseña
  418. if(!$force_login) {
  419. //con contraseña lanzamos un error de usuario existente, se usará para mostrar un formulario donde preguntar el password
  420. $this->user_data['username'] = $username;
  421. $this->last_error = "oauth-goteo-user-password-exists";
  422. return false;
  423. }
  424. }
  425. else {
  426. //El usuario no existe
  427. //redirigir a user/confirm para mostrar un formulario para que el usuario compruebe/rellene los datos que faltan
  428. $this->last_error = "oauth-goteo-user-not-exists";
  429. return false;
  430. }
  431. }
  432. //si el usuario existe, actualizar o crear los tokens
  433. $this->saveTokensToUser($username);
  434. //actualizar la imagen de avatar si no tiene!
  435. if($this->user_data['profile_image_url']) {
  436. $query = Goteo\Core\Model::query('SELECT id FROM image WHERE id = (SELECT avatar FROM user WHERE id = ?)', array($username));
  437. if(!($query->fetchColumn())) {
  438. $img = new Goteo\Model\Image($this->user_data['profile_image_url']);
  439. $img->save();
  440. if($img->id) {
  441. Goteo\Core\Model::query("REPLACE user_image (user, image) VALUES (:user, :image)", array(':user' => $username, ':image' => $img->id));
  442. Goteo\Core\Model::query("UPDATE user SET avatar = :avatar WHERE id = :user", array(':user'=>$username,':avatar'=>$img->id));
  443. }
  444. }
  445. }
  446. //el usuario existe, creamos el objeto
  447. $user = Goteo\Model\User::get($username);
  448. //actualizar datos de usuario si no existen:
  449. $update = array();
  450. $data = array(':user' => $username);
  451. foreach($this->import_user_data as $key) {
  452. if(empty($user->$key) && $this->user_data[$key]) {
  453. $update[] = "$key = :$key";
  454. $data[":$key"] = $this->user_data[$key];
  455. }
  456. }
  457. if($update) {
  458. Goteo\Core\Model::query("UPDATE user SET ".implode(", ",$update)." WHERE id = :user", $data);
  459. //rebuild user object
  460. $user = Goteo\Model\User::get($username);
  461. }
  462. //actualizar las webs
  463. if($this->user_data['website']) {
  464. $current_webs = array();
  465. if(is_array($user->webs)) {
  466. foreach($user->webs as $k => $v)
  467. $current_webs[] = strtolower($v->url);
  468. }
  469. $webs = array();
  470. preg_match_all("/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/", $this->user_data['website'], $webs);
  471. if($webs[0] && is_array($webs[0])) {
  472. $updated = false;
  473. foreach($webs[0] as $web) {
  474. $web = strtolower($web);
  475. if(!in_array($web,$current_webs)) {
  476. Goteo\Core\Model::query("INSERT user_web (user, url) VALUES (:user, :url)", array(':user' => $username, ':url' => $web));
  477. $updated = true;
  478. }
  479. }
  480. //rebuild user object
  481. if($updated) $user = Goteo\Model\User::get($username);
  482. }
  483. }
  484. //Si no tiene imagen, importar de gravatar.com?
  485. if(!$user->avatar || $user->avatar->id == 1) {
  486. $query = Goteo\Core\Model::query('SELECT id FROM image WHERE id = (SELECT avatar FROM user WHERE id = ?)', array($username));
  487. if(!($query->fetchColumn())) {
  488. $url = "http://www.gravatar.com/avatar/" . md5(strtolower(trim($user->email)));
  489. $url .= "?d=404";
  490. $img = new Goteo\Model\Image( $url );
  491. $img->save();
  492. if($img->id) {
  493. Goteo\Core\Model::query("REPLACE user_image (user, image) VALUES (:user, :image)", array(':user' => $username, ':image' => $img->id));
  494. Goteo\Core\Model::query("UPDATE user SET avatar = :avatar WHERE id = :user", array(':user'=>$username,':avatar'=>$img->id));
  495. $user = Goteo\Model\User::get($username);
  496. }
  497. }
  498. }
  499. //CAMBIADO A: siempre login, aunque no esté activo el usuario
  500. //Iniciar sessión i redirigir
  501. $_SESSION['user'] = $user;
  502. //Guardar en una cookie la preferencia de "login with"
  503. //no servira para mostrar al usuario primeramente su opcion preferida
  504. setcookie("goteo_oauth_provider",$this->original_provider,time() + 3600*24*365);
  505. if (!empty($_POST['return'])) {
  506. throw new Goteo\Core\Redirection($_POST['return']);
  507. } elseif (!empty($_SESSION['jumpto'])) {
  508. $jumpto = $_SESSION['jumpto'];
  509. unset($_SESSION['jumpto']);
  510. throw new Goteo\Core\Redirection($jumpto);
  511. } else {
  512. throw new Goteo\Core\Redirection('/dashboard');
  513. }
  514. }
  515. /**
  516. * Guarda los tokens generados en el usuario
  517. * */
  518. public function saveTokensToUser($goteouser) {
  519. $query = Goteo\Core\Model::query('SELECT id FROM user WHERE id = ?', array($goteouser));
  520. if($id = $query->fetchColumn()) {
  521. foreach($this->tokens as $provider => $token) {
  522. if($token['token']) {
  523. $query = Goteo\Core\Model::query("REPLACE user_login (user,provider,oauth_token,oauth_token_secret) VALUES (:user,:provider,:token,:secret)",array(':user'=>$goteouser,':provider'=>$provider,':token'=>$token['token'],':secret'=>$token['secret']));
  524. }
  525. }
  526. }
  527. else {
  528. $this->last_error = "oauth-goteo-user-not-exists";
  529. return false;
  530. }
  531. }
  532. }
  533. ?>