CaseWiki:External Authentication
This article serves as the base for MetaWikiPedia:Auto Login via REMOTE USER, although the authors of the two pages are completely different. If you experience trouble with the directions below, please consult the aforementioned article before posting a question on the discussion page.
Contents |
[edit] About
It is possible to configure MediaWiki to use an external source for authentication. All that you need is a way for PHP to be able to read the username that you wish to use. It is possible to obtain the username from the REMOTE_USER variable and use any Apache module to handle authentication. It is also possible to use cookies. Almost any mechanism can work.
This wiki originally used Pubcookie for authentication. When the university transitioned to CAS in September 2005, so did this wiki.
[edit] Modifying MediaWiki
It is not possible to use just a custom $wgAuth plugin with external authentication. The reason is that external authentication is handled at the server level, not the software level.
[edit] Remove Access to the Login Page
If you are using external as the sole method for authenticating, there is no more need for the login page. You can use mod_rewrite to redirect requests for this page elsewhere. You can also modify includes/templates/userlogin.php to remove the login form.
[edit] Add Some Settings to LocalSettings.php
Add the following variables to LocalSettings.php
$wgLoginFormKey = "<insert random string here>";
For example:
$wgLoginFormKey = "ouhad9fg8h2397fg239";
[edit] Create Your Custom AuthPlugin Extension
MediaWiki allows you to use your own authorization and user population extension ($wgAuth). See includes/AuthPlugin.php for instructions. When using external authentication, you probably will want your custom authorization class to look similar to this:
<?php require_once('AuthPlugin.php'); class MyAuthPlugin extends AuthPlugin { //return whether $username is a valid username function userExists($username) { //since the username will be passed from our external source, this will probably always be true //however, the security paranoid says to check the data //you could do an LDAP verify here, just to be safe return true; //or return false if the username is invalid } //whether the given username and password authenticate function authenticate($username, $password) { //the external authentication actually handles this part, but we still need a security check //this form element will be set by our login script. this security check is important! global $wgLoginFormKey; return isset($_POST[$wgLoginFormKey]); } //The authorization is external, so autocreate accounts as necessary function autoCreate() { return true; } //tell MediaWiki to not look in its database for user authentication and that our authentication method is all that counts function strict() { return true; } //this function gets called when the user is created //$user is an instance of the User class (see includes/User.php) function initUser(&$user) { //unless you want the person to be nameless, you should probably populate info about this user here //we do some LDAP queries to populate their name and e-mail $user->setRealName("John Smith"); $user->setEmail("john.smith@case.edu"); //if using MediaWiki 1.5, we can set some e-mail options $user->mEmailAuthenticated = wfTimestampNow(); //turn on e-mail notifications by default $user->setOption('enotifwatchlistpages', 1); $user->setOption('enotifusertalkpages', 1); $user->setOption('enotifminoredits', 1); $user->setOption('enotifrevealaddr', 1); } //if using MediaWiki 1.5, we have a new function to modify the UI template! function modifyUITemplate(&$template) { //disable the mail new password box $template->set("useemail", false); //disable 'remember me' box $template->set("remember", false); $template->set("create", false); $template->set("domain", true); } } ?>
[edit] Create Your Custom Login Scripts
[edit] Initial Login Script
This script should be the one that the login page is transparently redirected to. It should not be protected by any type of authentication. It simply gathers some information and redirects the user to the protected script
<?php //this is the part where we fake out media wiki to accept logins session_start(); define('MEDIAWIKI', true); //uncomment the following line if running MediaWiki 1.8.0 or greater //require_once('../StartProfiler.php'); require_once('../includes/Defines.php'); require_once('../LocalSettings.php'); require_once('../includes/Setup.php'); if (isset($_SERVER['HTTP_REFERER'])) { $_SESSION['http_referrer'] = $_SERVER['HTTP_REFERER']; } else if (!array_key_exists('http_referrer', $_SESSION)) { $defaultUrl = Title::newFromText('Main Page'); $_SESSION['http_referrer'] = $defaultUrl->getFullURL(); } $loginUrl = $wgServer.$wgScriptPath.'/login/index3.php'; header("Location: $loginUrl"); ?>
[edit] Authentication Protected Page
This script is behind a protected realm. That is, it requires the user to be authenticated to access it. This script relies on the fact that the REMOTE_USER variable is set accordingly. It sends a fake form submission to MediaWiki and logs the user in. It then takes the cookies for that login and transfers them to the user. It requires PEAR's HTTP Request package to be installed.
This script could also be modified so that the user is obtained during execution of the script. This would be useful for a single sign-on service, such as CAS or Shibboleth. The important part about this script is it can somehow obtain the username of the person you wish to log in.
<?php define('MEDIAWIKI', true); //needed to include LocalSettings.php without MediaWiki spazzing out //we need to bring in $wgLoginFormKey require_once('../LocalSettings.php'); /**this script must be run behind some type of Apache user authorization * fail immediately if it isn't * * This is also where you would go log the user into an external site and return the username */ if (!isset($_SERVER['REMOTE_USER'])) { die("The remote user variable is not set"); } //set the login username //on our wiki, we actual do some voodoo and rename the user to something else because our network usernames are not supposed to be published //we do that in this script before it even hits MediaWiki $username = $_SERVER['REMOTE_USER']; session_start(); $redirect = $_SESSION['http_referrer']; unset($_SESSION['http_referrer']); require_once "HTTP/Request.php"; $req = null; $loginTitle = Title::newFromText('Userlogin', NS_SPECIAL); $loginUrl = $loginTitle->getFullURL('action=submitlogin'); $req = new HTTP_Request($loginUrl, array('method'=>'POST')); global $wgLoginFormKey; //set up the request with fake data $req->addPostData("wpName", $username); $req->addPostData("wpPassword", "password"); //we could change "password" to some value and double check in our custom login extension if we wanted added security $req->addPostdata("wpLoginattempt", "Log in"); $req->addPostdata($wgLoginFormKey, "value here doesn't matter"); $req->sendRequest(); //uncomment to debug mediawiki response //print_r($req->getResponseCookies()); //print_r($req->getResponseHeader()); //print_r($req->getResponseBody()); //die(); //intercept the cookies sent by mediawiki and set them for the client //you could change the expiration time of the cookies for added security foreach ($req->getResponseCookies() as $c) { if ($c['name'] == "PHPSESSID") { setcookie("PHPSESSID", $c['value'], null, '/'); } else { setcookie($c['name'], $c['value'], null, $c['path']); } } //finally redirect the user back to his original page header("Location: $redirect"); ?>
[edit] Using Hooks to Automatically Log in Users
Case's CAS SSO service stores a domain-wide cookie that can be used as a helper to tell if a user is probably logged in to the SSO service. We created a method for the 'AutoAuthenticate' hook to invoke our automatic log on procedure if this cookie is detected. Therefore, if a user is logged in to CAS and visits the wiki, he or she will be automatically logged in to the Case Wiki. Everything is perfectly transparent.
We created a file for this hook and included it in LocalSettings.php.
<?php //$wgExtensionFunctions[] = 'MediaWiki_CaseWikiHooks'; $wgHooks['AutoAuthenticate'][] = 'MediaWiki_CaseWikiAutoAuthenticate'; function MediaWiki_CaseWikiAutoAuthenticate(&$user) { global $wgServer, $wgScriptPath, $wgCookiePrefix; //we go to great lengths to make sure the login code isn't called when not necessary if ( isset($_COOKIE['CaseSSO']) && ($_COOKIE['CaseSSO'] == 'loggedin') && (!isset($_COOKIE[$wgCookiePrefix.'UserID'])) && (!isset($_COOKIE[$wgCookiePrefix.'LoggedOut'])) && (strstr($_SERVER['REQUEST_URI'], $wgScriptPath.'/login/') === false) ) { //let's try and automagically log in the user require_once('Case/Authn/CAS.php'); $cas = new Case_Authn_CAS(); $cas->checkAuthentication(); //if the CAS session can be verified, we invoke the scripts from above. No need to duplicate code here. Slower, yes, but easier to maintain. if ($cas->isLoggedIn()) { header('Location: '.$wgServer.$wgScriptPath.'/login/'); exit(); } } return true; } ?>
[edit] Intercepting Calls to Special:Userlogin
Since we only want users to log in through our special script, Special:Userlogin is no longer needed. The following mod_rewrite rule intercepts calls to this page and redirects the user to our login script:
RewriteRule ^/Special:Userlogin https://%{SERVER_NAME}/login/ [R,L] RewriteCond %{REQUEST_URI} ^/index.php$ RewriteCond %{QUERY_STRING} ^title=Special:Userlogin RewriteCond %{REQUEST_METHOD} ^GET$ RewriteRule ^(.*)$ /login/ [R,L]
Depending on your MediaWiki rewrite rules already in place, you may want to slightly modify the above rules.
Case Referrers
Other Sites
- Site Search - Case New Student Orientation (1 referral)
- http://start.case.edu/ (7 referral)
