Auth systems/SUL2

Current System

 * When a global user logs in to a local wiki, CentralAuth will inject images on the result page to attempt to log the user into other WMF projects
 * Images for each wiki in $wgCentralAuthAutoLoginWikis are generated
 * The images call Special:AutoLogin with a token, which is used to setup the session associated with this user
 * The user gets a top-level-domain cookie for each wiki, which expires in 1 day
 * On logout, the session are deleted

Current Issues

 * In the existing scheme, many mobile browsers (70%) do not accept the cookie for the foreign wikis when the user hasn't visited that wiki directly
 * Firefox 22 will block third party cookies as well
 * At minimum, users need to be logged into commons.wikimedia.org and wikidata.org to take advantage of mobile and visualeditor features

Proposed Solution

 * A central domain where global users would have a session / cookie
 * After logging in on the local wiki, CentralAuth would redirect the user to the centraldomain to set the cookie
 * On each wiki, anonymous users would have some javascrpt that contacts the central domain to determine if the user is logged in
 * If the user is logged in, update the UI or redirect the user to finish building their session
 * If the user is not logged in, set a cookie/local storage so the wiki doesn't attempt the check again
 * Since each user will call this service once per session, we can estimate a load of about 193 calls/second
 * Special:UserLogin will always check the central domain for a session, and show a successful login message if the user is logged in
 * Local wikis will also provide an api where a user can request a short-lived token, which can be used for authentication to another wiki's api. This will allow users to talk to the api of other wikis in the cluster as the global user.
 * To limit the potential for abuse, the token should not live more than a few seconds, will only be valid for a single target wiki, and will expire after use.
 * So this is pretty much like what Special:AutoLogin does? And I'd guess that the client would be having to get another token for every request to the other wiki, since XMLHTTPRequest's attempt to set cookies probably will be blocked too. How would that interact with CORS preflight OPTION requests, if bug 41731 ever gets fixed?
 * Pretty close to Special:AutoLogin, although I'd like the expiration to be a few seconds instead of minutes. We can make sure it's long enough that the client can do a preflight and the request before it times out. And yes, the client would need to request a new token per call.
 * If the token expires after a single use, won't the preflight will "eat" the token so it won't be valid for the actual flight?
 * I don't think the token will be sent in the OPTION call (assuming your doing a post, and that is why you need the preflight), so I don't think it would be consumed at that point. But that's just from reading the spec and doing a little playing around in firefox. We could probably allow it to be used twice if it's an issue.
 * That depends if it's a GET or POST, of course, and note the API already requires the "origin" parameter be included in the query string even for a POST. I guess the question there is whether the preflight also would ever need to be authenticated.
 * The preflight in this case is really only answering if it's ok for the UA to talk the method between the origin and this domain. I really can't think of a case where we would want to change that, based on the whether or not the user is authenticated. Unless there is a reason to allow CORS for some users but not others?

Design

 * WIP. These should be finished before coding.

Redirect on Login

 * User logs in to local wiki
 * During login, if CentralAuth detects that this is a global account
 * Setup/initialize the local wiki session
 * Generate and keep a secret, used to sign the redirect response
 * Generate a token (t1), which identifies the user to the central domain
 * Redirect user to https://centraldomain/Special:CentralLogin/login?token=
 * Special:CentralLogin will check for an existing session
 * If there is an existing session that does not match the one reference by the token, show an error message, including link back to original site, and stop
 * If there is an existing session, and the names match, redirect back to original wiki and show success message (do not return a token, and do not update central session)
 * If there is no existing session
 * set a central session cookie, with a "pending_name"
 * delete the memcache object the token references
 * Give the user a page with a form that will post back to original wiki (Special:CentralLogin/result) with a token (t2) and a signature for that token
 * Page has javascript to submit the form automatically
 * On the local wiki, check the token+signature:
 * If the signature on the token is not verified, show an error message
 * Otherwise the local wiki completes the session setup for the central session referenced by t2 (at minimum, this includes 'user' and 'token')
 * (for now) Show the SUL icons to attempt the normal autologin process?

Notes:
 * In the blog post by Jonathan Mayer, he recommends for setting 3rd party cookies: "The most transparent practice is for you to redirect the user through your origin. You could also use a non-cookie storage technology, though alternatives may be limited by this policy in future."
 * It appears that Safari will handle cookie sets on a redirect done in javascript
 * The central domain should only be accessed over SSL
 * On the central domain, only very highly trusted user can modify any javascript. No user script should be allowed.
 * The central domain pages should not be iframe-able

Local wiki Javascript Auth check

 * (C1) On each local wiki, if the user is not logged in, and does not have a cookie/local storage specifying they are anonymous, use javscript (using CORS, or write an iframe that redirects to the next stage) to check the authentication status on the central domain
 * Do the check, even if the anonymous cookie is set, if the user is on Special:UserLogin
 * The check should only be made from other WMF sites. It should not be possible to determine the user’s logged-in status from other websites.
 * The check returns the user's gu_id associated with the session
 * If the user is not authenticated on the central domain, set a cookie (and local storage key?) indicating the user is anonymous
 * Local storage would be nice, so we’re not adding yet another cookie to send back and forth. However, IE6+7 is 2.5% of traffic; and have no local storage
 * (L1) If the user is logged in, the browser contacts Special:CentralLogin?startsession=true&id= to start a session
 * Can be called with ajax, or with a redirect
 * If local wiki has a session, abort with Error
 * The local wiki will store the gu_id in the user's session, Generate and store a secret (S1) in the session, generate a token (T1) that can reference the session data and also store T1 in the session. The local wiki returns T1 to the user. Server reply includes session_id cookie.
 * (C2) The Browser then contacts the central domain with T1, and the wiki identifier
 * Central domain references session for that wiki id, using the token
 * If gu_id in session (referenced by the token) does not match gu_id of current user fail with error
 * Otherwise, return Sig = HMAC( "gu_id:T1", S1 )
 * (L2) Browser sends Sig to local wiki
 * Local wiki calculates Sig' = HMAC("gu_id:T1", S1 ) with T1, gu_id, and S1 from session
 * If Sig' == Sig, setup session; otherwise fail with error

Local Wiki	   	       		Browser	   				Central Domain

(C1) → logged in,  → ← / no, gu_id ←

← guid, start sessio ← (L1) → session_id, T1 →

(C2) → T1, wiki  → ← Sig ←

← Sig ← (L2) → Ok/error →

Local wiki token for cross-wiki api
(Brad wrote this)
 * Use the ApiTokensGetTokenTypes hook to add "centralauthtoken" as an option to action=tokens
 * Use the APIGetAllowedParams and APIGetParamDescription hooks to add "centralauthtoken" as an option to ApiMain.
 * Somehow, when "centralauthtoken" is given to the API, use the associated user data when executing the request. But where?
 * In CentralAuthHooks::onUserLoadFromSession when MW_API is defined? In this case we need to make sure not to invalidate the token for OPTIONS queries.
 * In some later hook, e.g. ApiCheckCanExecute? In this case, it seems possible that some things could be checking rights and such on the old, probably-anonymous user before it reaches the point of calling the API hook.

Task List

 * Special:UserLogin redirect to central domain
 * Javascript check on local wiki to get central domain's auth status
 * Api for getting a token on local wiki (or central?) good for authenticated call to another wiki

Rollout Plan

 * Setup the central wiki
 * Modify centralauth's interaction with the login to redirect to central wiki and set the central cookie; continue to use Special:AutoLogin images temporarily.
 * Enable autologin javascript on all wikis
 * Disable Special:AutoLogin