Renoirb has submitted this change and it was merged.

Change subject: *Major refactor*, now supporting OAuth2 RS
......................................................................


*Major refactor*, now supporting OAuth2 RS

More info at: 
http://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension

INFO: This version runs on https://docs.webplatform.org/test/ since three 
months.

* Make MediaWiki to use a deployment of Mozilla Firefox Accounts as Identity 
Provider (IdP)
* Session recovery using hidden iframe
* Require external component: Guzzle that’ll handle async requests
* Queries an OAuth2 Resource Server with minimal profile data
* Reads from profile data e.g. {username: 'Jdoe', fullName: 'John Doe'} and 
creates/use local account
* Overloads Special:UserLogin, Special:UserLogout
* Handling change password, removing from preferences redundant info
* Use memcache to keep state
* Using MW i18n methods, moving messages to it, making up english, then 
translating to french
* Moved validation responsibility to FirefoxAccountsManager
* Wrong re-throwing new exception syntax fix
* Reminder: Hook MUST!!1 return true or false
* Implementing session Recovery
* New behavior between iframe and session recovery

Change-Id: If5d3caec9b05b8362190f8ef30658efd135cdb4c
---
M .gitignore
A README.md
M WebPlatformAuth.php
A composer.json
A composer.lock
M i18n/en.json
M i18n/fr.json
A includes/FirefoxAccountsManager.php
M includes/WebPlatformAuthHooks.php
A includes/WebPlatformAuthPlugin.php
A includes/WebPlatformAuthUserFactory.php
D includes/api/ApiWebPlatformAuth.php
A includes/specials/AccountsHandlerSpecialPage.php
D includes/specials/WPA_RenewSession.php
A includes/specials/WebPlatformAuthLogin.php
A includes/specials/WebPlatformAuthLogout.php
A includes/specials/WebPlatformAuthPassword.php
A vendor/.gitkeep
18 files changed, 1,305 insertions(+), 368 deletions(-)

Approvals:
  Renoirb: Verified; Looks good to me, approved



diff --git a/.gitignore b/.gitignore
index 98b092a..26a012b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
 *~
 *.kate-swp
 .*.swp
+vendor/*
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..eb88de8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,23 @@
+# MediaWiki SSO using Firefox Accounts
+
+See project details on WebPlatform.org wiki 
[WPD:Projects/SSO/MediaWikiExtension](http://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension).
+
+
+## Project repositories
+
+Code is synced in the following repositories:
+
+* https://github.com/webplatform/mediawiki-fxa-sso
+* 
https://gerrit.wikimedia.org/r/#/admin/projects/mediawiki/extensions/WebPlatformAuth
+
+
+## Reference documents
+
+The following were found to be userful in the realization of the
+current fork.
+
+* https://github.com/wikimedia/mediawiki-extensions-Persona
+* http://www.mediawiki.org/wiki/AuthPlugin
+* http://www.mediawiki.org/wiki/Manual:Special_pages
+* https://github.com/yorn/mwSimpleSamlAuth
+* http://www.mediawiki.org/wiki/Extension_talk:CASAuthentication
\ No newline at end of file
diff --git a/WebPlatformAuth.php b/WebPlatformAuth.php
index 8cb538b..3c8033b 100644
--- a/WebPlatformAuth.php
+++ b/WebPlatformAuth.php
@@ -1,53 +1,59 @@
 <?php
-# Alert the user that this is not a valid entry point to MediaWiki if they try 
to access the special pages file directly.
-if (!defined('MEDIAWIKI')) {
-       echo <<<EOT
-To install my extension, put the following line in LocalSettings.php:
-require_once( "$IP/extensions/WebPlatformAuth/WebPlatformAuth.php" );
-EOT;
-       exit( 1 );
+
+/**
+ * MediaWiki SSO using Firefox Accounts
+ *
+ * Project details are available on the WebPlatform wiki
+ * https://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension
+ *
+ * @ingroup Extensions
+ *
+ * @version 2.0-dev
+ */
+
+if ( !defined( 'MEDIAWIKI' ) ) {
+  echo( "Not an entry point." );
+  die( -1 );
 }
 
-$wgExtensionCredits['other'][] = array(
-       'path'           => __FILE__,
-       'name'           => 'WebPlatformAuth',
-       'author'         => '[http://www.hallowelt.biz Hallo Welt! 
Medienwerkstatt GmbH]; Robert Vogel',
-       'url'            => 'http://www.hallowelt.biz',
-       'descriptionmsg' => 'webplatformauth-desc',
-       'version'        => '1.1.0',
-);
+//if ( version_compare( $GLOBALS['wgVersion'], '1.22', '<' ) ) {
+//   die( '<b>Error:</b> This extension requires MediaWiki 1.22 or above' );
+//}
 
 $dir = dirname(__FILE__) . '/';
 
-$wgAutoloadClasses['WebPlatformAuthHooks']   = $dir . 
'includes/WebPlatformAuthHooks.php';
-$wgAutoloadClasses['ApiWebPlatformAuth']     = $dir . 
'includes/api/ApiWebPlatformAuth.php';
-$wgAutoloadClasses['WPARenewSession']        = $dir . 
'includes/specials/WPA_RenewSession.php';
-$wgMessagesDirs['WebPlatformAuth'] = __DIR__ . '/i18n';
-$wgExtensionMessagesFiles['WebPlatformAuth'] = $dir . 
'WebPlatformAuth.i18n.php';
+if ( is_readable( __DIR__ . '/vendor/autoload.php' ) ) {
+  $loader = require( __DIR__ . '/vendor/autoload.php' );
+  $loader->add( 'Guzzle\\', $dir . '/vendor/guzzlehttp/guzzle/src/Guzzle/' );
+} else {
+  die('You MUST install Composer dependencies');
+}
 
-$wgSpecialPages['RenewSession'] = 'WPARenewSession';
-//$wgWhitelistRead[] = 'Special:RenewSession';
-
-$wgAPIModules['webplatformauth'] = 'ApiWebPlatformAuth';
-
-$wgAjaxExportList[] = 'WebPlatformAuthHooks::ajaxGetUserInfoById';
-$wgAjaxExportList[] = 'WebPlatformAuthHooks::ajaxGetUserInfoByName';
-
-$wgHooks['UserLogoutComplete'][] = 
'WebPlatformAuthHooks::onUserLogoutComplete';
-$wgHooks['UserLoginComplete'][]  = 'WebPlatformAuthHooks::onUserLoginComplete';
-$wgHooks['UserSetCookies'][]     = 'WebPlatformAuthHooks::onUserSetCookies';
-//$wgHooks['UserLoadFromSession'][]          = 
'WebPlatformAuthHooks::onUserLoadFromSession';
-$wgHooks['UserLoadAfterLoadFromSession'][] = 
'WebPlatformAuthHooks::onUserLoadAfterLoadFromSession';
-//$wgHooks['UserLoadFromDatabase'][]         = 
'WebPlatformAuthHooks::onUserLoadFromDatabase';
-
-/**
- * @deprecated Replaced by $wgWebPlatformAuthSecret, because in WPD setup IP 
addresses ain't predictable enough
- */
-$wgWebPlatformAuthAllowedClients = array(
-       'localhost',
-       '127.0.0.1'
+$wgExtensionCredits['other'][] = array(
+  'name'           => 'WebPlatformAuth',
+  'path'           => __FILE__,
+  'version'        => '2.0-dev',
+  'author'         => array('[https://renoirboulanger.com Renoir Boulanger]'),
+  'url'            => 
'http://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension',
+  'description'    => 'Single Sign On MediaWiki extension',
 );
 
-$wgWebPlatformAuthSecret = 
'NqzdqcdCRWd1JZ1DXSI2Eq5BbjYra40nEguT8654C7eNrMldXuMDs4laHQIppAoc';
+$wgAutoloadClasses['WebPlatformAuthHooks']       = $dir . 
'includes/WebPlatformAuthHooks.php';
 
-$wgCookieDomain = '.webplatform.org'; // --> LocalSettings.php
+$wgAutoloadClasses['AccountsHandlerSpecialPage'] = $dir . 
'includes/specials/AccountsHandlerSpecialPage.php';
+$wgAutoloadClasses['WebPlatformAuthLogin']       = $dir . 
'includes/specials/WebPlatformAuthLogin.php';
+$wgAutoloadClasses['WebPlatformAuthLogout']      = $dir . 
'includes/specials/WebPlatformAuthLogout.php';
+$wgAutoloadClasses['WebPlatformAuthPassword']    = $dir . 
'includes/specials/WebPlatformAuthPassword.php';
+
+$wgMessagesDirs['WebPlatformAuth']           = __DIR__ . '/i18n';
+$wgExtensionMessagesFiles['WebPlatformAuth'] = $dir . 
'WebPlatformAuth.i18n.php';
+
+// Change AccountsHandler for better name later #TODO
+$wgSpecialPages['AccountsHandler'] = 'AccountsHandlerSpecialPage';
+$wgSpecialPages['Userlogin']       = 'WebPlatformAuthLogin';
+$wgSpecialPages['Userlogout']      = 'WebPlatformAuthLogout';
+$wgSpecialPages['ChangePassword']  = 'WebPlatformAuthPassword';
+
+$wgHooks['UserLoadFromSession'][]  = 
'WebPlatformAuthHooks::onUserLoadFromSession';
+$wgHooks['GetPreferences'][]       = 
'WebPlatformAuthHooks::hookLimitPreferences';
+$wgHooks['SpecialPage_initList'][] = 
'WebPlatformAuthHooks::hookInitSpecialPages';
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..b71dcf8
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,51 @@
+{
+  "name": "webplatform/mediawiki-fxa-sso",
+  "type": "mediawiki-extension",
+  "license": "MIT",
+  "description": "WebPlatform Docs SSO Extension communicating with 
WebPlatorm’s own Firefox Accounts server",
+  "keywords": [
+    "MediaWiki",
+    "WebPlatform",
+    "authentication",
+    "Firefox Accounts",
+    "OAuth2",
+    "FxA"
+  ],
+  "homepage": 
"http://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension";,
+  "authors": [
+    {
+      "name": "Renoir Boulanger",
+      "email": "ren...@w3.org",
+      "role": "Maintainer",
+      "homepage": "https://renoirboulanger.com";
+    },
+    {
+      "name": "Doug Schepers",
+      "email": "schep...@w3.org",
+      "role": "Project lead"
+    }
+  ],
+  "support": {
+    "issues": "https://github.com/webplatform/mediawiki-fxa-sso/issues";,
+    "email":  "team-webplatform-syst...@w3.org",
+    "irc":    "irc://irc.freenode.net/webplatform",
+    "source": "https://github.com/webplatform/mediawiki-fxa-sso";
+  },
+  "require": {
+    "guzzlehttp/guzzle": "~3.8"
+  },
+  "autoload": {
+    "files" : [
+      "WebPlatformAuth.php"
+    ],
+    "classmap":[
+      "includes/"
+    ]
+  },
+  "repositories": [
+    {
+      "type": "vcs",
+      "url": "https://github.com/webplatform/mediawiki-fxa-sso";
+    }
+  ]
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..4aba2e3
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,176 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at 
http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file";
+    ],
+    "hash": "c635d4c923295de15a39b2d5cc345712",
+    "packages": [
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "v3.8.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git";,
+                "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/guzzle/guzzle/zipball/4de0618a01b34aa1c8c33a3f13f396dcd3882eba";,
+                "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba",
+                "shasum": ""
+            },
+            "require": {
+                "ext-curl": "*",
+                "php": ">=5.3.3",
+                "symfony/event-dispatcher": ">=2.1"
+            },
+            "replace": {
+                "guzzle/batch": "self.version",
+                "guzzle/cache": "self.version",
+                "guzzle/common": "self.version",
+                "guzzle/http": "self.version",
+                "guzzle/inflection": "self.version",
+                "guzzle/iterator": "self.version",
+                "guzzle/log": "self.version",
+                "guzzle/parser": "self.version",
+                "guzzle/plugin": "self.version",
+                "guzzle/plugin-async": "self.version",
+                "guzzle/plugin-backoff": "self.version",
+                "guzzle/plugin-cache": "self.version",
+                "guzzle/plugin-cookie": "self.version",
+                "guzzle/plugin-curlauth": "self.version",
+                "guzzle/plugin-error-response": "self.version",
+                "guzzle/plugin-history": "self.version",
+                "guzzle/plugin-log": "self.version",
+                "guzzle/plugin-md5": "self.version",
+                "guzzle/plugin-mock": "self.version",
+                "guzzle/plugin-oauth": "self.version",
+                "guzzle/service": "self.version",
+                "guzzle/stream": "self.version"
+            },
+            "require-dev": {
+                "doctrine/cache": "*",
+                "monolog/monolog": "1.*",
+                "phpunit/phpunit": "3.7.*",
+                "psr/log": "1.0.*",
+                "symfony/class-loader": "*",
+                "zendframework/zend-cache": "<2.3",
+                "zendframework/zend-log": "<2.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.8-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Guzzle": "src/",
+                    "Guzzle\\Tests": "tests/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowl...@gmail.com",
+                    "homepage": "https://github.com/mtdowling";
+                },
+                {
+                    "name": "Guzzle Community",
+                    "homepage": "https://github.com/guzzle/guzzle/contributors";
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library and framework 
for building RESTful web service clients",
+            "homepage": "http://guzzlephp.org/";,
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "rest",
+                "web service"
+            ],
+            "time": "2014-01-28 22:29:15"
+        },
+        {
+            "name": "symfony/event-dispatcher",
+            "version": "v2.5.0",
+            "target-dir": "Symfony/Component/EventDispatcher",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/EventDispatcher.git";,
+                "reference": "cb62ec8dd05893fc8e4f0e6e21e326e1fc731fe8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": 
"https://api.github.com/repos/symfony/EventDispatcher/zipball/cb62ec8dd05893fc8e4f0e6e21e326e1fc731fe8";,
+                "reference": "cb62ec8dd05893fc8e4f0e6e21e326e1fc731fe8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "~2.0",
+                "symfony/dependency-injection": "~2.0",
+                "symfony/stopwatch": "~2.2"
+            },
+            "suggest": {
+                "symfony/dependency-injection": "",
+                "symfony/http-kernel": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.5-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Symfony\\Component\\EventDispatcher\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/";,
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fab...@symfony.com",
+                    "homepage": "http://fabien.potencier.org";,
+                    "role": "Lead Developer"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors";
+                }
+            ],
+            "description": "Symfony EventDispatcher Component",
+            "homepage": "http://symfony.com";,
+            "time": "2014-04-29 10:13:57"
+        }
+    ],
+    "packages-dev": [
+
+    ],
+    "aliases": [
+
+    ],
+    "minimum-stability": "stable",
+    "stability-flags": [
+
+    ],
+    "platform": [
+
+    ],
+    "platform-dev": [
+
+    ]
+}
diff --git a/i18n/en.json b/i18n/en.json
index 4f716ca..990c0a5 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -1,8 +1,16 @@
 {
     "@metadata": {
         "authors": [
-            "Robert Vogel"
+            "Robert Vogel",
+            "Renoir Boulanger"
         ]
     },
-    "webplatformauth-desc": "Enables MediaWiki to serve as webplatform.org's 
authentication provider"
-}
+    "webplatformauth-desc": "Enables a MediaWiki instance to read information 
from an OAuth2 protected profile server and handles user accounts. The profile 
server becomes the authority creating SSO for that MediaWiki installation.",
+    "webplatformauth-missing-required-config": "Required configuration for the 
extension to work is missing",
+    "webplatformauth-cannot-repeat-oauth-handshake": "Cannot cannot repeat 
OAuth handshake, it has to be started over",
+    "webplatformauth-exception-oauth-handshake-details-below": "OAuth2 
handshake error, see Exception error message below",
+    "webplatformauth-error-flow-state-data-empty": "Error while attempting to 
resume state prior to the login process",
+    "webplatformauth-exception-cannot-read-profile": "Error while attempting 
to read profile server data.",
+    "webplatformauth-error-user-nodata": "Error while attempting to read 
profile data, there was nothing",
+    "webplatformauth-exception-invalid-user-create-entry": "Invalid user data!"
+}
\ No newline at end of file
diff --git a/i18n/fr.json b/i18n/fr.json
index 7e6750e..5e585b5 100644
--- a/i18n/fr.json
+++ b/i18n/fr.json
@@ -1,8 +1,16 @@
 {
        "@metadata": {
                "authors": [
-                       "Sherbrooke"
+                       "Sherbrooke",
+      "Renoir Boulanger"
                ]
        },
-       "webplatformauth-desc": "Autoriser MediaWiki à devenir un fournisseur 
d'authentification au nom de webplatform.org"
-}
+       "webplatformauth-desc": "Permet à MediaWiki de créer et connecter des 
comptes locaux basé sur les données d’un service de profils protégé par OAuth2 
tels Mozilla Firefox Accounts et celui de WebPlatform.org",
+  "webplatformauth-missing-required-config": "Une ou plusieurs des éléments de 
configuration requises sont manquantes",
+  "webplatformauth-cannot-repeat-oauth-handshake": "Impossible de répéter le 
processus d’authorisation OAuth2. Il doit être répété à partir du début",
+  "webplatformauth-exception-oauth-handshake-details-below": "Erreur lors du 
processus d’authorisation OAuth2, voir message de l’erreur",
+  "webplatformauth-error-flow-state-data-empty": "Erreur lors de la tentative 
de restaurer l’état de votre session avant la récente tentative de connexion",
+  "webplatformauth-exception-cannot-read-profile": "Erreur survenue lors de la 
tentative de lire les données du serveur de profil, est-il fonctionnel?",
+  "webplatformauth-error-user-nodata": "Erreur survenue lorsque lors de la 
tentative de lire les données du serveur de profile, il n’a rien retourné",
+  "webplatformauth-exception-invalid-user-create-entry": "Données 
d’utilisateur reçues sont dans une format incompréhensible"
+}
\ No newline at end of file
diff --git a/includes/FirefoxAccountsManager.php 
b/includes/FirefoxAccountsManager.php
new file mode 100644
index 0000000..b96eb8f
--- /dev/null
+++ b/includes/FirefoxAccountsManager.php
@@ -0,0 +1,266 @@
+<?php
+
+/**
+ * MediaWiki SSO using Firefox Accounts
+ *
+ * Project details are available on the WebPlatform wiki
+ * https://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension
+ **/
+
+// Guzzle classes
+use Guzzle\Http\Client;
+
+// Guzzle Exceptions
+use Guzzle\Http\Exception\ClientErrorResponseException;
+
+class FirefoxAccountsManager
+{
+
+  const NO_RESULT_YET     = 0;
+
+  const ALREADY_CONNECTED = 2;
+
+  const SUCCESSFUL        = 4;
+
+  const USER_CREATED      = 8;
+
+  const TTL = 3600;
+
+  const MAX_CLIENT_TIMEOUT_SECONDS = 3;
+
+  private $config = null;
+
+  private $profile_data;
+
+  public function __construct($config)
+  {
+    if ( !$this->hasAllRequiredConfiguration( $config ) ) {
+      throw new Exception( 'Required configuration is missing' );
+    }
+
+    $this->config   = $config;
+
+    // THIS SHOULD MOVE SOON
+    $this->memcache = $GLOBALS['wgMemc'];
+
+    return $this;
+  }
+
+  /** THIS CLASS SHOULD BE HANDLING ONLY WITH OAUTH, NOT MEMCACHE
+      BELOW THIS LINE WILL HAVE TO MOVE OUTSIDE OF THIS CLASS **/
+
+  /**
+   * Serialize and save data to memcache
+   *
+   * Note that it also sets a time to live for the
+   * cached version set to self::TTL
+   *
+   * @param string $cacheKey Cache key
+   * @param mixed  $data     Data to send to memcached, will use serialize();
+   *
+   * @return null
+   */
+  private function memcacheSave($data)
+  {
+    $url_friendly_key = MWCryptRand::generateHex( 16 );
+
+    $key = wfMemcKey( $url_friendly_key , 'wpdsso' );
+
+    $this->memcache->set( $key , json_encode( $data ) , self::TTL );
+
+    return $url_friendly_key;
+  }
+
+  /**
+   * Delete entry from memcache from given cache key
+   *
+   * @param  string $cacheKey Cache key
+   *
+   * @return null
+   */
+  private function memcacheRemove($cacheKey)
+  {
+    $regen_key = wfMemcKey( $cacheKey , 'wpdsso' );
+
+    $this->memcache->delete( $regen_key );
+  }
+
+  /**
+   * Read entry from memcache from given cache key
+   *
+   * @param  string $cacheKey Cache key
+   */
+  private function memcacheRead($cacheKey)
+  {
+    $regen_key = wfMemcKey( $cacheKey , 'wpdsso' );
+
+    return $this->memcache->get( $regen_key );
+  }
+
+  /**
+   * Give a key, get associated state data
+   *
+   * @param  string $state_key
+   *
+   * @return array  Data in the same state as when sent to stateStash
+   */
+  public function stateRetrieve($state_key)
+  {
+    $data = $this->memcacheRead( $state_key );
+
+    return json_decode( $data , 1);
+  }
+
+  public function stateDeleteKey($state_key)
+  {
+    $this->memcacheRemove( $state_key );
+  }
+
+  public function stateStash($data_array)
+  {
+    $key = $this->memcacheSave( $data_array );
+
+    return $key;
+  }
+
+  /* END   -    THIS CLASS SHOULD BE...  */
+
+  /**
+   * Ask FxA to get a Bearer Token
+   *
+   * Typical return array is:
+   * <code>
+   *   array(
+   *       "access_token" => '...'
+   *       "token_type" => 'bearer'
+   *       "scope" => 'profile'
+   *   )
+   * </code>
+   *
+   * @param  string $code received from POST on FxA OAuth authorize endpiont
+   *
+   * @return array  Recieved JSON and converted to PHP array
+   */
+  public function getBearerToken($code)
+  {
+    $m = $this->config['methods'];
+    $e = $this->config['endpoints'];
+
+    $uri = $e['fxa_oauth'].$m['token'];
+    $packageData['client_id'] = $this->config['client']['id'];
+    $packageData['client_secret'] = $this->config['client']['secret'];
+    $packageData['code'] = $code;
+    $postBody = json_encode( $packageData );
+
+    try {
+      $client = new Client();
+      $client->setDefaultOption('timeout', self::MAX_CLIENT_TIMEOUT_SECONDS);
+      $subreq = $client->createRequest( 'POST' , $uri , null , $postBody );
+      $subreq->setHeader( 'Content-type' , 'application/json' );
+
+      $r = $client->send( $subreq );
+    } catch ( ClientErrorResponseException $e ) {
+      throw $e;
+    }
+
+    return $r->json();
+  }
+
+  /**
+   * Build OAuth provider URI
+   *
+   * @return string URI to the OAuth signin action on the FxA server
+   */
+  public function initHandshake($return_to=null, $signup=false)
+  {
+    $m = $this->config['methods'];
+    $e = $this->config['endpoints'];
+
+    if( $return_to !== null ) {
+      $stateData['return_to'] = $return_to;
+    }
+
+    $stateData['scopes'] = array( 'session' );
+
+    $state_key = $this->stateStash( $stateData ); // Returns uuid
+
+    $query_params['client_id'] = $this->config['client']['id'];
+    $query_params['state'] = $state_key;
+    // Space separated list of scopes keys
+    $query_params['scope'] = implode( '+' , $stateData['scopes'] );
+
+    if ( $signup === true ) {
+      $query_params['action'] = 'signup';
+    }
+
+    return $e['fxa_oauth']
+            . $m['authorize']
+            . '?'
+            . http_build_query( $query_params );
+  }
+
+  /**
+   * Retrieve profile data from FxA profile server
+   *
+   * @param  array $token Token array recieved from OAuth handshake
+   *
+   * @return array profile data as an array
+   */
+  public function getProfile($token)
+  {
+    $m = $this->config['methods'];
+    $e = $this->config['endpoints'];
+
+    /**
+     * $token has the following keys:
+     * array(
+     *  "access_token" => "..."
+     *  "token_type" => "bearer"
+     *  "scope" => "profile"
+     * )
+     *
+     * At the moment, note that token_type is ONLY of type bearer.
+     *
+     * Make $token a strong typed Token class, and enforce at method setter 
#IMPROVEMENT
+     */
+    $token_value = $token['access_token'];
+    $uri = $e['fxa_profile'] . 'session/read';
+
+    //$GLOBALS['poorman_logging'][] = 'Bearer token read : '.$token_value; // 
DEBUG
+
+    try {
+      $client = new Client();
+      $client->setDefaultOption('timeout', self::MAX_CLIENT_TIMEOUT_SECONDS);
+      $subreq = $client->createRequest( 'GET' , $uri );
+      $subreq->setHeader( 'Authorization' , 'Bearer ' .  $token_value );
+      $r = $client->send( $subreq );
+    } catch ( Exception $e ) {
+      throw new Exception('ProfileReaderException: Cannot get user profile', 
null, $e);
+    }
+
+    return $r->json();
+  }
+
+  /**
+   * Internal check to see if we have all required configuration
+   *
+   * @param  array  $config Constructor injected configuration to validate
+   *
+   * @return boolean        Whether it has what we need or not
+   */
+  private function hasAllRequiredConfiguration($config)
+  {
+    if ( !is_array( $config ) ) {
+      return false;
+    }
+
+    return isset(
+      $config['client']['id'],
+      $config['client']['secret'],
+      $config['endpoints']['fxa_oauth'],
+      $config['endpoints']['fxa_profile'],
+      $config['methods']['authorize'],
+      $config['methods']['token']
+    );
+  }
+}
diff --git a/includes/WebPlatformAuthHooks.php 
b/includes/WebPlatformAuthHooks.php
index e2aca8b..dcec798 100644
--- a/includes/WebPlatformAuthHooks.php
+++ b/includes/WebPlatformAuthHooks.php
@@ -1,184 +1,407 @@
 <?php
 
-class WebPlatformAuthHooks {
+/**
+ * MediaWiki SSO using Firefox Accounts
+ *
+ * Project details are available on the WebPlatform wiki
+ * https://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension
+ **/
 
-       /**
-        * 
-        * @param User $user
-        * @param string $inject_html
-        * @return boolean
-        */
-       public static function onUserLoginComplete( $user, &$inject_html ) {
-               $_SESSION['wsUserEmail']             = $user->getEmail();
-               //We've got to flatten the effective groups because MWs 
MemchacheD 
-               //session handler does not serialize $_SESSION correctly 
-               // -> inludes/MemcachedClient.php:993
-               $_SESSION['wsUserEffectiveGroups']   = implode( ',', 
$user->getEffectiveGroups() );
-               //$_SESSION['wsUserRealName']        = $user->getRealName();
-               $_SESSION['wsUserPageURL']           = 
$user->getUserPage()->getFullURL();
+// FIXME, Loader.. :(
+require_once( dirname( __FILE__ ) . '/WebPlatformAuthUserFactory.php' );
+require_once( dirname( __FILE__ ) . '/FirefoxAccountsManager.php' );
 
-               self::writeDataToMemcache( $user );
+// Guzzle Exceptions
+use Guzzle\Http\Exception\ClientErrorResponseException;
 
-               self::checkReturnTo();
-               
-               return true;
-       }
+class WebPlatformAuthHooks
+{
+  /**
+   * Disable redundant Special pages
+   *
+   * Some pages aren’t needed while using an external authentication
+   * source.
+   *
+   * Explictly disabling local pages:
+   * * Password change,
+   * * e-mail confirmation disabled when autoconfirm is disabled.
+   *
+   * They will be handled by our external provider anyway
+   *
+   * Blantly copied from SimpleSamlAuth::hookInitSpecialPages()
+   * @link https://github.com/yorn/mwSimpleSamlAuth
+   *
+   * @link http://www.mediawiki.org/wiki/Manual:Hooks/SpecialPage_initList
+   *
+   * @param $pages string[] List of special pages in MediaWiki
+   *
+   * @return boolean|string true on success, false on silent error, string on 
verbose error
+   */
+  public static function hookInitSpecialPages( &$pages ) {
+    unset( $pages['PasswordReset'] );
+    unset( $pages['ConfirmEmail'] );
+    unset( $pages['ChangeEmail'] );
 
-       /**
-        * 
-        * @param User $user
-        * @param string $inject_html
-        * @param string $oldName
-        * @return boolean
-        */
-       public static function onUserLogoutComplete($user, $inject_html, 
$oldName) {
-               //TODO: Maybe
-               session_destroy();
-               self::checkReturnTo();
+    // Those are overriden
+    //unset( $pages['ChangePassword'] );
+    //unset( $pages['Userlogout'] );
+    //unset( $pages['Userlogin'] );
 
-               return true;
-       }
-       
-       /**
-        * 
-        * @param User $user User object
-        * @param array $session session array, will be added to $_SESSION
-        * @param array $cookies cookies array mapping cookie name to its value
-        * @return boolean
-        */
-       public static function onUserSetCookies( $user, &$session, &$cookies ) {
-               $session['wsUserEmail']             = $user->getEmail();
-               $session['wsUserEffectiveGroups']   = 
implode(',',$user->getEffectiveGroups());
-               $session['wsUserPageURL']           = 
$user->getUserPage()->getFullURL();
-               
-               self::writeDataToMemcache( $user );
-               return true;
-       }
+    return true;
+  }
 
-       /**
-        * 
-        * @param User $user User object
-        * @return boolean
-        */
-       public static function onUserLoadAfterLoadFromSession( $user ) {
-               $_SESSION['wsUserEmail']             = $user->getEmail();
-               $_SESSION['wsUserEffectiveGroups']   = 
implode(',',$user->getEffectiveGroups());
-               $_SESSION['wsUserPageURL']           = 
$user->getUserPage()->getFullURL();
+  /**
+   * Disable redundant preferences
+   *
+   * Since an external system is taking care of those, lets
+   * remove them from the special pages.
+   *
+   * Blantly copied from SimpleSamlAuth::hookLimitPreferences()
+   * @link https://github.com/yorn/mwSimpleSamlAuth
+   *
+   * @link http://www.mediawiki.org/wiki/Manual:Hooks/GetPreferences
+   *
+   * @param $user User User whose preferences are being modified.
+   *                   ignored by this method because it checks the SAML 
assertion instead.
+   * @param &$preferences Preferences description array, to be fed to an 
HTMLForm object.
+   *
+   * @return boolean|string true on success, false on silent error, string on 
verbose error
+   */
+  public static function hookLimitPreferences( $user, &$preferences ) {
+    unset( $preferences['password'] );
+    unset( $preferences['rememberpassword'] );
+    unset( $preferences['emailaddress'] );
 
-               self::writeDataToMemcache( $user );
-               return true;
-       }
-       
-       /**
-        * 
-        * @global WebRequest $wgRequest
-        * @global OutputPage $wgOut
-        */
-       public static function checkReturnTo() {
-               global $wgRequest;
-               $returnTo = $wgRequest->getVal('returnto');
-               if (!is_null($returnTo) && in_array( substr($returnTo, 0, 3), 
array( 'qa|', 'wp|' ) ) ) {
-                       //We have to exit() here because otherwise we would be 
redirected to a MW page
-                       header('Location: ' . substr($returnTo, 3));
-                       exit();
-               }
-       }
+    // Should disable realname here and have
+    // FxA do the handling for us
+    //unset( $preferences['realname'] );
 
-       /**
-        * 
-        * @param string $userIds Comma seperated list of user ids
-        * @param string $secret A secret key to avoid unauthorized use of the 
ajax interface
-        * @return string JSON encoded list of requested user information
-        */
-       public static function ajaxGetUserInfoById($userIds, $secret ) {
-               global $wgWebPlatformAuthSecret;
-               if( $secret != $wgWebPlatformAuthSecret ) {
-                       return FormatJson::encode( new stdClass() );
-               }
+    return true;
+  }
 
-               $userIds = explode(',', $userIds);
-               $users = UserArray::newFromIDs($userIds);
+  /**
+   * Load session from user
+   *
+   * At this time here, we can be in two situations. Either we are an
+   * anonymous user (user object here has most likely no name set we assume)
+   * but we also might happen to just be back from our trip to the OAuth
+   * Resource server.
+   *
+   * Documentation says we should read cookies and just pop in that user object
+   * the name coming from the cookies. I expect that just breaks any security
+   * steps we’ve taken so far.
+   *
+   * Since we came from the OAuth Resource server and the user had a successful
+   * authentication exchange, the Request URI should have TWO properties
+   *
+   * - code
+   * - state
+   *
+   * The Code will be used right after to get a bearer token, so, its safe
+   * to assume that we can start that validation here instead than later in the
+   * execution flow.
+   *
+   * If that step was successful, we trust that we already saved
+   * a state object in the Memcache server. Lets use that as a way to check
+   * **before any html has been given to the browser** to validate that user.
+   *
+   * We already might got cookies:
+   *
+   * - (.*)Token,
+   * - (.*)UserID
+   * - (.*)UserName
+   *
+   * Since we already can know expectable data from the resource server,
+   * use this hook as an event handler to actually do the validation.
+   * from our OAuth Resource server, and nothing else should be done
+   *
+   * @param  [type] $user   [description]
+   * @param  [type] $result [description]
+   * @return [type]         [description]
+   */
+  public static function onUserLoadFromSession( $user, &$result )
+  {
+    $GLOBALS['poorman_logging'] = array();
 
-               $response = array();
-               foreach ($users as $user) {
-                       $response[$user->getId()] = array(
-                               'user_name'      => $user->getName(),
-                               'user_real_name' => $user->getRealName(),
-                               'user_email'     => $user->getEmail(),
-                               'user_page_url'  => 
$user->getUserPage()->getFullURL()
-                       );
-               }
+    $diagnosis['session']  = 
RequestContext::getMain()->getRequest()->getCookie('_session');
+    $diagnosis['username'] = 
RequestContext::getMain()->getRequest()->getCookie('UserName');
+    $diagnosis['user_id']  = 
RequestContext::getMain()->getRequest()->getCookie('UserID');
+    if(isset($_COOKIE['wpdSsoUsername'])) $diagnosis['wpdSsoUsername'] = 
$_COOKIE['wpdSsoUsername'];
+    if(isset($_POST['recoveryPayload'])) $diagnosis['recoveryPayload'] = 
$_POST['recoveryPayload'];
 
-               //In MW there is no user "0", but in Q2A
-               if( in_array( 0, $userIds ) ) {
-                       $user = User::newFromId(1); //WikiSysop;
-                       $response[0] = array(
-                               'user_name'      => $user->getName(),
-                               'user_real_name' => $user->getRealName(),
-                               'user_email'     => $user->getEmail(),
-                               'user_page_url'  => 
$user->getUserPage()->getFullURL()
-                       );
-               }
+    //header("X-WebPlatform-Debug: ".substr(str_replace('"','', 
json_encode($diagnosis,true)),1,-1));
+    //header("X-WebPlatform-Debug-Cookie: ".substr(str_replace('"','', 
json_encode($_COOKIE,true)),1,-1));
 
-               return FormatJson::encode($response);
-       }
-       
-       /**
-        * 
-        * @param string $userNames Comma seperated list of user names
-        * @param string $secret A secret key to avoid unauthorized use of the 
ajax interface
-        * @return string JSON encoded list of requested user information
-        */
-       public static function ajaxGetUserInfoByName($userNames, $secret) {
-               global $wgWebPlatformAuthSecret;
-               if( $secret != $wgWebPlatformAuthSecret ) {
-                       return FormatJson::encode( new stdClass() );
-               }
+    //if(isset($_POST['recoveryPayload'])){
+    //  header("X-WebPlatform-Debug-Edgecase2: recoveryPayload is present");
+    //}
 
-               $userNames = explode(',', $userNames);
-               $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select(
-                               'user',
-                               'user_id',
-                               array( 'user_name' => $userNames )
-               );
+    // Use Native PHP way to check REQUEST params
+    $state_key = (isset($_GET['state']))?$_GET['state']:null;
+    $code = (isset($_GET['code']))?$_GET['code']:null;
+    $bearer_token = null;
+    $profile = null;
+    $site_root = str_replace('$1','', $GLOBALS['wgArticlePath']);
+    //$registeredCookieWithUsername = 
RequestContext::getMain()->getRequest()->getCookie( 'UserName' );
+    //$GLOBALS['poorman_logging'][] = 'Registered cookie username: 
'.print_r($registeredCookieWithUsername, 1);
 
-               $response = array();
-               foreach ( $res as $row ) {
-                       $user = User::newFromId($row->user_id);
-                       $response[$user->getId()] = array(
-                               'user_name'      => $user->getName(),
-                               'user_real_name' => $user->getRealName(),
-                               'user_email'     => $user->getEmail(),
-                               'user_page_url'  => 
$user->getUserPage()->getFullURL()
-                       );
-               }
+    $sessionToken = ( isset( $_POST['recoveryPayload'] ) ) ? 
$_POST['recoveryPayload'] : null;
+    if ( is_string( $sessionToken ) ) {
+      header('Cache-Control: no-store, no-cache, must-revalidate');
 
-               return FormatJson::encode($response);
-       }
-       
-       protected static function writeDataToMemcache( $user ) {
-               global $wgMemc;
+      // TODO: Do some more validation with this, see
+      //   notes at 
http://docs.webplatform.org/wiki/WPD:Projects/SSO/Improvements_roadmap#Recovering_session_data
+      try {
+        $uri = 'https://profile.accounts.webplatform.org/v1/session/recover';
+        $client = new Guzzle\Http\Client();
+        $client->setDefaultOption( 'timeout', 22 );
+        $subreq = $client->get($uri);
+        $subreq->setHeader( 'Content-type', 'application/json' );
+        $subreq->setHeader( 'Authorization', 'Session ' . $sessionToken );
 
-               if ( !is_object($user) ) return true;
+        $r = $client->send( $subreq );
+      } catch ( Guzzle\Http\Exception\ClientErrorResponseException $e ) {
+        $clientErrorStatusCode = $e->getResponse()->getStatusCode();
+        $clientErrorReason = $e->getResponse()->getReasonPhrase();
 
-               $sesskey = false;
-               if ( isset( $_COOKIE[ 'wpwiki_session' ] ) ) {
-                       $sesskey = $_COOKIE[ 'wpwiki_session' ];
-               }
-               if ( !$sesskey ) return true;
+        // We are considering that wgUser id 0 means anonymous
+        if($clientErrorStatusCode === 401 && $GLOBALS['wgUser']->getId() !== 
0) {
+          $GLOBALS['wgUser']->logout();
+          header("HTTP/1.1 401 Unauthorized");
+          header("X-WebPlatform-Outcome-1: Session closed, closing local too");
 
-               $memcAlternateSessionKey = 'wpwiki:altsession:'.$sesskey;
-               #error_log( "Docs Alternate Session Key: ". 
$memcAlternateSessionKey );
-               #error_log( "UserId". $user->getId() );
+        // 401 AND uid 0 means we have nothing to do
+        } elseif($clientErrorStatusCode === 401 && $GLOBALS['wgUser']->getId() 
=== 0) {
+          header("HTTP/1.1 204 No Content");
+          header("X-WebPlatform-Outcome-2: Session closed both local and 
accounts");
+        }
 
-               $data = array();
-               $data['wsUserID'] = $user->getId();
-               $data['wsUserName'] = $user->getName();
-               $data['wsUserEmail']             = $user->getEmail();
-               $data['wsUserEffectiveGroups']   = 
implode(',',$user->getEffectiveGroups());
-               $data['wsUserPageURL']           = 
$user->getUserPage()->getFullURL();
+        return true;
 
-               $wgMemc->add( $memcAlternateSessionKey, serialize( $data ) );
-       }
-}
\ No newline at end of file
+      } catch ( Guzzle\Http\Exception\CurlException $e ) {
+        header("X-WebPlatform-Outcome-3: CurlException");
+        header("HTTP/1.1 400 Bad Request");
+        echo($e->getMessage());
+
+        return true;
+      }
+
+      try {
+        $data = $r->json();
+      } catch(Exception $e) {
+        header("HTTP/1.1 400 Bad Request");
+        header("X-WebPlatform-Outcome-4: Profile refused communication");
+        echo "Profile server refused communication";
+
+        return true;
+      }
+
+      # 20140807
+      $wgUserName = 
(is_object($GLOBALS['wgUser']))?$GLOBALS['wgUser']->getName():null;
+      if(isset($data['username']) && strtolower($wgUserName) === 
strtolower($data['username'])) {
+        header("X-WebPlatform-Outcome-5: " . strtolower($wgUserName) . " is " 
. strtolower($data['username']));
+        header("HTTP/1.1 204 No Content");
+
+        return true; // All is good
+      }
+
+      $tempUser = WebPlatformAuthUserFactory::prepareUser( $data );
+      wfSetupSession();
+      if( $tempUser->getId() === 0 ){
+        // No user exists whatsoever, create and make current user
+        $tempUser->ConfirmEmail();
+        $tempUser->setEmailAuthenticationTimestamp( time() );
+        $tempUser->setPassword( User::randomPassword() );
+        $tempUser->setToken();
+        $tempUser->setOption( "rememberpassword" , 0 );
+        $tempUser->addToDatabase();
+        $GLOBALS['poorman_logging'][] = sprintf( 'User %s created' , 
$tempUser->getName() ) ;
+      } else {
+        // User exist in database, load it
+        $tempUser->loadFromDatabase();
+        $GLOBALS['poorman_logging'][] = sprintf( 'Session for %s started' , 
$tempUser->getName() ) ;
+      }
+      $GLOBALS['poorman_logging'][] = $tempUser->getId();
+      $GLOBALS['wgUser'] = $tempUser;
+      $tempUser->saveSettings();
+      $tempUser->setCookies();
+
+      // Ideally, the first false below should be true! But we need SSL at the 
top level domain
+      setcookie('wpdSsoUsername', $data['username'], time()+60*60*7, '/', 
'.webplatform.org', false, true);
+
+      if (isset($_GET['username'])) {
+        header("X-WebPlatform-Username: ".$_GET['username']);
+      }
+      #header("X-WebPlatform-Recovery: " . urlencode(json_encode($data)) );
+
+      header("HTTP/1.0 201 Created");
+
+      return true;
+    }
+    if ( is_string( $state_key ) && is_string( $code ) ) { // START IF HAS 
STATE AND CODE
+      // WE HAVE STATE AND CODE, NOW ARE THEY JUNK?
+
+      //$GLOBALS['poorman_logging'][] = 'About to retrieve data: 
'.(($user->isLoggedIn())?'logged in':'not logged in'); // DEBUG
+
+      // Since we DO have what we need to get
+      // to our validation server, please do not cache.
+      // ... and since WE DIDN’t send any HTML, yet (good boy)
+      // we can actually do that.
+      header('Cache-Control: no-store, no-cache, must-revalidate');
+
+      try {
+        $apiHandler = new FirefoxAccountsManager( 
$GLOBALS['wgWebPlatformAuth'] );
+        // $code can be used ONLY ONCE!
+        //$GLOBALS['poorman_logging'][] = 'Code: '.print_r($code,1); // DEBUG
+        $bearer_token = $apiHandler->getBearerToken( $code );
+
+      } catch ( ClientErrorResponseException $e ) {
+
+        $msg  = 'Could not get authorization token';
+
+        $GLOBALS['poorman_logging'][] = $msg;
+
+        $msg .= ', returned after FirefoxAccountsManager::getBearerToken(), it 
said:' . $e->getMessage();
+        $obj = json_decode($e->getResponse()->getBody(true), true);
+        $msg .= (isset($obj['reason'])) ? ', reason: ' . $obj['reason'] : null;
+        $msg .= (isset($obj['message'])) ? ', message: '.$obj['message'] : 
null;
+
+        error_log($msg);
+
+        //header('Location: '.$site_root);
+
+        return true;
+      } catch ( Exception $e ) {
+        // Other error: e.g. config, or other Guzzle call not expected.
+        $msg = 'Unknown error, Could not get authorization token';
+        $GLOBALS['poorman_logging'][] = $msg;
+
+        $msg .= ', returned a "' . get_class($e);
+        $msg .= '" after FirefoxAccountsManager::getBearerToken(), it said: 
'.$e->getMessage();
+
+        error_log($msg);
+
+        //header('Location: '.$site_root);
+
+        return true;
+      }
+
+      //$GLOBALS['poorman_logging'][] = 'Bearer token: 
'.print_r($bearer_token,1); // DEBUG
+
+      // FirefoxAccountsManager::getBearerToken()
+      // returns an array.
+      if ( is_array( $bearer_token ) ) {
+        try {
+          $profile = $apiHandler->getProfile( $bearer_token );
+
+          //$GLOBALS['poorman_logging'][] = 'Profile: '.print_r($profile,1); 
// DEBUG
+
+          $tempUser = WebPlatformAuthUserFactory::prepareUser( $profile );
+        } catch ( ClientErrorResponseException $e ) {
+
+          $msg = 'Could not retrieve profile data';
+          $GLOBALS['poorman_logging'][] = $msg;
+
+          $msg .= ', returned a "' . get_class($e);
+          $msg .= '" after calling getProfile(), it said: '.$e->getMessage();
+
+          $obj = json_decode($e->getResponse()->getBody(true), true);
+          $msg .= (isset($obj['reason'])) ? ', with reason: ' . $obj['reason'] 
: null;
+          $msg .= (isset($obj['message'])) ? ', message: '.$obj['message'] : 
null;
+
+          error_log($msg);
+
+          return true;
+        } catch ( Exception $e ) {
+          $msg = 'Unknown error, Could not get profile data or create new 
user';
+
+          $GLOBALS['poorman_logging'][] = $msg;
+
+          $msg .= ', returned a "' . get_class($e);
+          $msg .= '" after FirefoxAccountsManager::getProfile(), it said: 
'.$e->getMessage();
+
+          error_log($msg);
+
+          return true;
+        }
+
+        // Note that, HERE, whether we use $GLOBALS['wgUser']
+        // or $user (passed in this function call from the hook)
+        // or EVEN the one passed to WebPlatformAuthUserFactory::prepareUser()
+        // it should be the same. It is assumed that in prepareUser() it the 
call
+        // to MW User::loadDefaults($username) makes that binding.
+        // #DOUBLECHECKLATER
+
+        // Let’s be EXPLICIT
+        //
+        // Note that MW User::isLoggedIn() is not **only** checking
+        // whether the user is logged in per se. But rather do both;
+        // checking if the user exists in the database. Doesn’t mean
+        // the session is bound, yet.
+        wfSetupSession();
+        if( $tempUser->getId() === 0 ){
+          // No user exists whatsoever, create and make current user
+          $tempUser->ConfirmEmail();
+          $tempUser->setEmailAuthenticationTimestamp( time() );
+          $tempUser->setPassword( User::randomPassword() );
+          $tempUser->setToken();
+          $tempUser->setOption( "rememberpassword" , 0 );
+          $tempUser->addToDatabase();
+          $GLOBALS['poorman_logging'][] = sprintf( 'User %s created' , 
$tempUser->getName() ) ;
+        } else {
+          // User exist in database, load it
+          $tempUser->loadFromDatabase();
+          $GLOBALS['poorman_logging'][] = sprintf( 'Session for %s started' , 
$tempUser->getName() ) ;
+        }
+        $GLOBALS['poorman_logging'][] = $tempUser->getId();
+        $GLOBALS['wgUser'] = $tempUser;
+        $tempUser->saveSettings();
+        $tempUser->setCookies();
+
+        //$GLOBALS['poorman_logging'][] = 
($GLOBALS['wgUser']->isLoggedIn())?'logged in':'not logged in'; // DEBUG
+        //$GLOBALS['poorman_logging'][] = $tempUser->getId(); // DEBUG
+        $state_data = $apiHandler->stateRetrieve( $state_key );
+
+        if ( is_array($state_data) && isset( $state_data['return_to'] )) {
+          $apiHandler->stateDeleteKey( $state_key );
+          $GLOBALS['poorman_logging'][] = 'State data: '.print_r($state_data, 
1);
+
+          header('Location: ' . $state_data['return_to'] );
+
+          return true; // Even though it might just be sent elsewhere, making 
sure.
+        }
+      } else {
+        $GLOBALS['poorman_logging'][] = 'No bearer tokens';
+
+        header('Location: '.$site_root);
+      }
+    }
+
+    //$GLOBALS['poorman_logging'][] = 
($GLOBALS['wgUser']->isLoggedIn())?'logged in':'not logged in';
+
+    /**
+     * I can put true or false because we wont be using local authentication
+     * whatsoever. Hopefully that’s the way to do.
+     *
+     * Quoting the doc
+     *
+     *   "When the authentication should continue undisturbed
+     *    after the hook was executed, do not touch $result. When
+     *    the normal authentication should not happen
+     *    (e.g., because $user is completely initialized),
+     *    set $result to any boolean value."
+     *
+     *    -- 2014-05-22 
http://www.mediawiki.org/wiki/Manual:Hooks/UserLoadFromSession
+     *
+     * But, if I set $result to either true or false, it doesn’t make the UI to
+     * act as if you are logged in, AT ALL. Even though I created
+     * the user and set it to the global object. I’d like to investigate on 
why we cannot
+     * set either true or false here because it is unclear what it means 
undisturbed... we are
+     * creating local users, based on remote data, but authentication implies 
password, we arent using
+     * local ones, what gives? #DOUBLECHECKLATER
+     */
+    //$result = false; // Doesn’t matter true or false, and its passed here 
by-reference.
+
+    return true; // Hook MUST return true if it was as intended, was it? 
(hopefully so far)
+  }
+}
diff --git a/includes/WebPlatformAuthPlugin.php 
b/includes/WebPlatformAuthPlugin.php
new file mode 100644
index 0000000..7fc84a3
--- /dev/null
+++ b/includes/WebPlatformAuthPlugin.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * MediaWiki SSO using Firefox Accounts
+ *
+ * Project details are available on the WebPlatform wiki
+ * https://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension
+ **/
+
+require_once( dirname( __FILE__ ) . '/WebPlatformAuthHooks.php' );
+require_once( dirname( __FILE__ ) . '/FirefoxAccountsManager.php' );
+
+// Guzzle Exceptions
+use Guzzle\Http\Exception\ClientErrorResponseException;
+
+/**
+ * Extending MediaWiki Authentication
+ *
+ * Based on GodAuth MediaWiki Extension
+ *
+ * @link https://github.com/iamcal/MediaWiki-SSO/blob/master/GodAuth.php
+ */
+class WebPlatformAuthPlugin extends AuthPlugin
+{
+    protected $apiHandler;
+
+    protected function _init()
+    {
+        try {
+            $this->apiHandler = new FirefoxAccountsManager( 
$GLOBALS['wgWebPlatformAuth'] );
+        } catch( Exception $e ) {
+            error_log('Problem initiating MediaWiki WebPlatformAuth AuthPlugin 
handler:' . $e->getMessage() );
+
+            throw $e;
+        }
+    }
+}
\ No newline at end of file
diff --git a/includes/WebPlatformAuthUserFactory.php 
b/includes/WebPlatformAuthUserFactory.php
new file mode 100644
index 0000000..5ea8bfd
--- /dev/null
+++ b/includes/WebPlatformAuthUserFactory.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * MediaWiki SSO using Firefox Accounts
+ *
+ * Project details are available on the WebPlatform wiki
+ * https://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension
+ **/
+
+class WebPlatformAuthUserFactory
+{
+  /**
+   * Return a user object
+   *
+   * Doesn’t add to the database, to keep an instance
+   * you have to call addToDatabase(); to your new user.
+   *
+   *
+   * <code>
+   * // Expected entered array format
+   * $user_array = array(
+   *                 'fullName'=>'John Doe',
+   *                 'username'=>'jdoe',
+   *                 'email'=>'j...@doe.name');
+   *
+   * // Check if the user can be created (i.e. has no entry in DB).
+   *
+   * // Then...
+   * $user = self::prepareUser($GLOBALS['wgUser'], $user_array);
+   *
+   * // (poor man) persist —current author badly miss Doctrine2—
+   * $user->addToDatabase();
+   *
+   * // Flag confirmation
+   * $user->ConfirmEmail();
+   *
+   * // When making changes
+   * $user->saveSettings();
+   *
+   * // Replace global scope object with our new one
+   * // .. start session ...
+   * $GLOBALS['wgUser'] = $user;        // Yep, like that :/
+   * $GLOBALS['wgUser']->setCookies();  // ^
+   * </code>
+   *
+   * @param User  &$user      MediaWiki User instance passed as reference
+   * @param array $user_array Array of provided by our profile server data to 
use inside our local user
+   *
+   * @return void
+   */
+  public static function prepareUser( $user_array )
+  {
+    $desired_keys = array( 'fullName' , 'email' , 'username' );
+    $input_keys = array_keys( $user_array );
+    $diff = array_diff( $desired_keys , $input_keys );
+
+    if ( count( $diff ) >= 1 ) {
+      throw new Exception( sprintf( 'Recieved data has required keys that are 
missing:  %s' , implode( ', ' , $diff ) ) );
+    }
+
+    $username = ucfirst( $user_array['username'] );
+
+    if ( !User::isUsableName( $username ) ) {
+      throw new Exception( sprintf( 'Username %s has invalid characters' , 
$username ) );
+    }
+
+    /*
+     * Based off of UserLoadFromSession Talk page documentation
+     * http://www.mediawiki.org/wiki/Manual_talk:Hooks/UserLoadFromSession
+     */
+    $user = User::newFromName( $username );
+    $user->setRealName( $user_array['fullName'] );
+    $user->setEmail( $user_array['email'] );
+
+    return $user;
+  }
+}
\ No newline at end of file
diff --git a/includes/api/ApiWebPlatformAuth.php 
b/includes/api/ApiWebPlatformAuth.php
deleted file mode 100644
index dd7ceb7..0000000
--- a/includes/api/ApiWebPlatformAuth.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-/**
- *
- *
- * Created on Sep 29, 2012
- *
- * API module for MediaWiki's WebPlatformSearchAutocomplete extension
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * @ingroup WebPlatformAuth
- */
-class ApiWebPlatformAuth extends ApiBase {
-
-       public function __construct( $main, $action ) {
-               parent::__construct( $main, $action );
-       }
-
-       public function getCustomPrinter() {
-               return $this->getMain()->createPrinterByName( 'json' );
-       }
-
-       public function execute() {
-               global $wgSearchSuggestCacheExpiry, $wgWebPlatformAuthSecret;
-               $params = $this->extractRequestParams();
-
-               $command  = $params['command'];
-               $users    = $params['users'];
-               $secret   = $params['secret'];
-               
-               $result = $this->getResult();
-               
-               if( $secret != $wgWebPlatformAuthSecret ) {
-                       $result->addValue( null, 'result', new stdClass() );
-                       return;
-               }
-               
-               $users = explode(',', $users);
-               $userlist = array();
-
-               if( $command == 'GetUsersById' ) {
-                       $userlist = UserArray::newFromIDs($users);
-               }
-               else if( $command == 'GetUsersByName' ) {
-                       $res = $this->getDB()->select(
-                               'user',
-                               'user_id',
-                               array( 'user_name' => $users )
-                       );
-                       foreach ( $res as $row ) {
-                               $userlist[] = User::newFromId($row->user_id);
-                       }
-               }
-
-               $response = array();
-               foreach( $userlist as $user ) {
-                       $response[$user->getId()] = array(
-                           'user_id'        => $user->getId(),
-                               'user_name'      => $user->getName(),
-                               'user_real_name' => $user->getRealName(),
-                               'user_email'     => $user->getEmail(),
-                               'user_page_url'  => 
$user->getUserPage()->getFullURL()
-                       );
-               }
-               
-               //In MW there is no user "0", but in Q2A
-               if( in_array( 0, $users ) ) {
-                       $user = User::newFromId(1); //WikiSysop;
-                       $response[0] = array(
-                           'user_id'        => 0,
-                               'user_name'      => $user->getName(),
-                               'user_real_name' => $user->getRealName(),
-                               'user_email'     => $user->getEmail(),
-                               'user_page_url'  => 
$user->getUserPage()->getFullURL()
-                       );
-               }
-
-               // Open search results may be stored for a very long time
-               $this->getMain()->setCacheMaxAge( $wgSearchSuggestCacheExpiry );
-               $this->getMain()->setCacheMode( 'public' );
-
-               // Set top level elements
-               $result->addValue( null, 'users', $response );
-       }
-       
-       public function isReadMode() { //Needed to be always available, even if 
read-api is not allowed
-               return false;
-       }
-
-       public function getAllowedParams() {
-               return array(
-                       'command' => null,
-                       'users'  => null,
-                       'secret' => null,
-               );
-       }
-
-       public function getParamDescription() {
-               return false;
-               return array(
-                       'command' => 'GetUsersById|GetUsersByName',
-                       'users' => 'Comma seperated list of either user names 
or user ids. Depends on given command.',
-               );
-       }
-
-       public function getDescription() {
-               return false;
-               return 'User information provider for the webplatform.org 
applications';
-       }
-
-       public function getExamples() {
-               return false;
-               return array(
-                       
'api.php?action=webplatformauth&command=GetUsersById&users=45,23,56'
-               );
-       }
-
-       public function getHelpUrls() {
-               return 'https://www.webplatform.org';
-       }
-}
diff --git a/includes/specials/AccountsHandlerSpecialPage.php 
b/includes/specials/AccountsHandlerSpecialPage.php
new file mode 100644
index 0000000..ad8ac9b
--- /dev/null
+++ b/includes/specials/AccountsHandlerSpecialPage.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * MediaWiki SSO using Firefox Accounts
+ *
+ * Project details are available on the WebPlatform wiki
+ * https://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension
+ **/
+
+// FIXME, Loader.. :(
+require_once( dirname( dirname( __FILE__ ) ) . '/FirefoxAccountsManager.php' );
+require_once( dirname( dirname( __FILE__ ) ) . 
'/WebPlatformAuthUserFactory.php' );
+
+// Guzzle Exceptions
+//use Guzzle\Http\Exception\ClientErrorResponseException;
+
+/**
+ * Accounts Handler Special Page
+ *
+ * A "Controller" (but in MediaWiki) that binds MediaWiki specific
+ * to other decoupled moving parts.
+ *
+ * Related documentation:
+ * * http://www.mediawiki.org/wiki/Manual:Special_pages
+ */
+class AccountsHandlerSpecialPage extends UnlistedSpecialPage
+{
+  /**
+   * FxA API handler
+   *
+   * @var FirefoxAccountsManager
+   */
+  private $apiHandler;
+
+  /**
+   * Constructor following MW initialization convention
+   */
+  public function __construct()
+  {
+    try {
+      $apiHandler = new FirefoxAccountsManager( $GLOBALS['wgWebPlatformAuth'] 
);
+    } catch ( Exception $e ) {
+      $this->getOutput()->showErrorPage( 'error' , $e->getMessage() );
+
+      return;
+    }
+
+    $this->apiHandler    = $apiHandler;
+
+    parent::__construct( 'AccountsHandler' );
+  }
+
+  public function execute($par)
+  {
+    $this->setHeaders();
+    $this->getOutput()->setPageTitle( 'webplatformauth-main-specialpage-title' 
);
+
+    switch ( $par ) {
+      case 'start':
+        $this->_start();
+      break;
+      case 'signup':
+        $this->_signup();
+      break;
+      case 'logout':
+        $this->_logout();
+      break;
+      case 'callback': // should we keep that? or have a callback url and a 
default?
+        $this->_callback();
+      break;
+      default:
+        $this->_default();
+      break;
+    }
+  }
+
+  private function _callback()
+  {
+    $this->getOutput()->addWikiText('== Execution messages 
=='.PHP_EOL.implode(PHP_EOL.'* ', $GLOBALS['poorman_logging']));
+  }
+
+  private function _default()
+  {
+    $this->getOutput()->redirect( $this->_getRefererUri() );
+  }
+
+  private function _signup()
+  {
+    $goto = $this->apiHandler->initHandshake( $this->_getRefererUri() , true );
+    $this->getOutput()->redirect( $goto );
+  }
+
+  private function _start()
+  {
+    $goto = $this->apiHandler->initHandshake( $this->_getRefererUri() );
+    $this->getOutput()->redirect( $goto );
+  }
+
+  private function _logout()
+  {
+    $GLOBALS['wgUser']->logout();
+    unset($_COOKIES);
+    $this->getOutput()->addWikiText( 'Logout method' );
+
+    $this->getOutput()->redirect( $this->_getRefererUri() );
+  }
+
+  private function _getRefererUri()
+  {
+    // Generally the wgArticlePagh ends with .../$1
+    $path = str_replace('$1','', $GLOBALS['wgArticlePath']);
+    $h = $this->getRequest()->getAllHeaders();
+
+    return ( isset( $h['REFERER'] ) ) ? $h['REFERER'] : $path;
+  }
+}
diff --git a/includes/specials/WPA_RenewSession.php 
b/includes/specials/WPA_RenewSession.php
deleted file mode 100644
index 3f18a80..0000000
--- a/includes/specials/WPA_RenewSession.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-class WPARenewSession extends UnlistedSpecialPage {
-
-       /**
-        * Constructor
-        */
-       function __construct() {
-               parent::__construct( 'RenewSession' );
-       }
-       
-       function execute( $par ) {
-               global $wgUser;
-               $_SESSION['wsUserEmail']             = $wgUser->getEmail();
-               $_SESSION['wsUserEffectiveGroups']   = 
implode(',',$wgUser->getEffectiveGroups());
-               $_SESSION['wsUserPageURL']           = 
$wgUser->getUserPage()->getFullURL();
-               
-               $this->getOutput()->redirect( $par );
-       }
-}
\ No newline at end of file
diff --git a/includes/specials/WebPlatformAuthLogin.php 
b/includes/specials/WebPlatformAuthLogin.php
new file mode 100644
index 0000000..ae9df5e
--- /dev/null
+++ b/includes/specials/WebPlatformAuthLogin.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * MediaWiki SSO using Firefox Accounts
+ *
+ * Project details are available on the WebPlatform wiki
+ * https://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension
+ **/
+
+/**
+ * Extend original MediaWiki Special:UserLogin
+ *
+ * Expected result redirect to FxA appropriate pages:
+ * * login
+ * * create account
+ *
+ * Related documentation:
+ * * http://www.mediawiki.org/wiki/Manual:Special_pages
+ */
+class WebPlatformAuthLogin extends LoginForm
+{
+
+  public function execute($subPage)
+  {
+    // Generally the wgArticlePagh ends with .../$1
+    $path = str_replace( '$1' , '' , $GLOBALS['wgArticlePath'] );
+    $type = $this->getRequest()->getVal( 'type' );
+
+    // Change AccountsHandler for better name later
+    $urls['signin'] = $path.'Special:AccountsHandler/start';
+    $urls['signup'] = $path.'Special:AccountsHandler/signup';
+
+    $this->setHeaders();
+
+    if ( $subPage === 'signup' ) {
+      $selectedUri = $urls['signup'];
+    } elseif ( $type === 'signup' ) {
+      $selectedUri = $urls['signup'];
+    } else {
+      $selectedUri = $urls['signin'];
+    }
+
+    $this->getOutput()->redirect( $selectedUri );
+  }
+}
diff --git a/includes/specials/WebPlatformAuthLogout.php 
b/includes/specials/WebPlatformAuthLogout.php
new file mode 100644
index 0000000..4c7f46c
--- /dev/null
+++ b/includes/specials/WebPlatformAuthLogout.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * MediaWiki SSO using Firefox Accounts
+ *
+ * Project details are available on the WebPlatform wiki
+ * https://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension
+ **/
+
+/**
+ * Extend original MediaWiki Special:UserLogout
+ *
+ * Expected result redirect to FxA appropriate pages:
+ * * logout
+ *
+ * Related documentation:
+ * * http://www.mediawiki.org/wiki/Manual:Special_pages
+ */
+class WebPlatformAuthLogout extends SpecialUserlogout
+{
+  public function execute($subPage)
+  {
+    // Generally the wgArticlePagh ends with .../$1
+    $path = str_replace( '$1' , '' , $GLOBALS['wgArticlePath'] );
+    $selectedUri = $path . 'Special:AccountsHandler/logout';
+
+    // TODO, delete local session!
+    // ... iframe with click event on #signout ?
+    // 
https://github.com/mozilla/fxa-content-server/blob/master/app/scripts/views/settings.js#L34
+
+    $this->getOutput()->redirect( $selectedUri );
+  }
+}
diff --git a/includes/specials/WebPlatformAuthPassword.php 
b/includes/specials/WebPlatformAuthPassword.php
new file mode 100644
index 0000000..a00c143
--- /dev/null
+++ b/includes/specials/WebPlatformAuthPassword.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * MediaWiki SSO using Firefox Accounts
+ *
+ * Project details are available on the WebPlatform wiki
+ * https://docs.webplatform.org/wiki/WPD:Projects/SSO/MediaWikiExtension
+ **/
+
+/**
+ * Extend original MediaWiki Special:ChangePassword
+ *
+ * Related documentation:
+ * * http://www.mediawiki.org/wiki/Manual:Special_pages
+ */
+class WebPlatformAuthPassword extends SpecialChangePassword
+{
+
+  public function execute($subPage)
+  {
+    // Make Non Hardcoded! #IMPROVEMENT
+    $this->getOutput()->redirect( 'https://accounts.webplatform.org/settings' 
);
+  }
+}
diff --git a/vendor/.gitkeep b/vendor/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vendor/.gitkeep

-- 
To view, visit https://gerrit.wikimedia.org/r/136152
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: If5d3caec9b05b8362190f8ef30658efd135cdb4c
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/extensions/WebPlatformAuth
Gerrit-Branch: master
Gerrit-Owner: Renoirb <ren...@w3.org>
Gerrit-Reviewer: Aaron Schulz <asch...@wikimedia.org>
Gerrit-Reviewer: Reedy <re...@wikimedia.org>
Gerrit-Reviewer: Renoirb <ren...@w3.org>
Gerrit-Reviewer: Siebrand <siebr...@kitano.nl>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to