Extension:CentralAuth/control flow

This is (hopefully) accurate as of April 2015, just before the SUL finalization. It was originally written as a set of notes for writing a patch, so it's not reader-friendly, and omits various details (the focus is on how user accounts and authentication data in the DB/sessions/memcache/cookies is handled), but should still be better than no documentation at all.

Registration flow

 * 1) user opens registration form on Special:UserLogin?type=signup; session cookie ({$wiki}Session) and CSRF token are set
 * 2) user posts the form; LoginForm::addNewAccount (or LoginForm::addNewAccountMailPassword) is invoked
 * 3) various checks (form validation, CSRF, IP blacklist, AbortNewAccount hook, throttling...)
 * 4) CentralAuth prevents registration via AbortNewAccount if the global account is not free
 * 5) AuthPlugin::addUser is invoked
 * 6) CentralAuth adds a globaluser record, attaches local account
 * 7) user is written into user table, user_properties rows are added if needed, a bunch of hooks related to validation/change of User fields are invoked in the process)
 * 8) AuthPlugin::initUser is invoked with autocreate=false
 * 9) user table is updated with hook/AuthPlugin changes
 * 10) password or email validation link is mailed, some further tweaking of user preferences might happen
 * 11) if this is a normal registration (not by another user, not with mailed password): user id and name are stored in session and cookie, login token is stored in session and user table, UserSetCookies hook is invoked
 * 12) CentralAuth removes login token from local session, stores global auth token in memcache and the globaluser table, stores session id and user name in second-level domain cookies
 * 13) RequestContext::$user is set
 * 14) AddNewAccount hook is invoked
 * 15) CentralAuth adds user name to localnames table
 * 16) if this is a normal registration and local session cookie is not set (ie. cookies disabled), redirect to Special:UserLogin?wpCookieCheck=new; LoginForm::onCookieRedirectCheck is invoked, forces successful login with cookies enabled before process can continue
 * 17) if this is a normal registration, LoginForm::successfulLogin is invoked, runs UserLoginComplete hooks
 * 18) CentralAuth does the login sequence

Login flow

 * 1) user opens login form on Special:UserLogin; session cookie and CSRF token are set
 * 2) user posts form, LoginForm::processLogin is invoked
 * 3) various checks (CSRF, throttling...)
 * 4) run LoginUserMigrated hook
 * 5) CentralAuth uses this to abort login if the user has been renamed and tries to log in with the old name
 * 6) if there is no local user by that name, but login is valid for the global user, try to autocreate
 * 7) run AbortAutoAccount hook
 * 8) CentralAuth uses this, as above
 * 9) treat this as a registration, save user (as in the registration flow 2.3-2.5)
 * 10) log the user in
 * 11) run AbortLogin hook
 * 12) used by CentralAuth when user is renamed or globally locked
 * 13) more checks (password validation/expiration)
 * 14) run AuthPlugin::updateUser
 * 15) CentralAuth uses this to replace the user object for migrated users and/or update the email address to keep in sync with the global one
 * 16) set RequestContext::$user
 * 17) run AuthPluginAutoCreate hook
 * 18) run LoginAuthenticateAudit hook
 * 19) set cookies (as in registration flow 2.7)
 * 20) renew local session id
 * 21) final steps are same as for registration, from step 3

Logout flow

 * 1) run UserLogout hook
 * 2) CentralAuth clears the cookies on the second-level domain, deletes session from memcache, regenerates global login token in DB and memcache
 * 3) set user id to 0 in session
 * 4) clear session cookies (UserID, Token)
 * 5) run UserLogoutComplete hook
 * 6) CentralAuth displays icons for each second-level domain and for large standalone wikis, pointing to Special:CentralAutoLogin/deleteCookies on those wikis; for each of those requests:
 * 7) clear data for each domain/wiki, as in step 1.1

CentralAuth login sequence

 * 1) invoked after successful logins/registrations from the UserLoginComplete hook, via CentralAuthHooks::doCentralLoginRedirect
 * 2) store a random login attempt secret in the session
 * 3) store the login attempt secret and user details in memcache, under a random key (additional data can be added via CentralAuthLoginRedirectData hook)
 * 4) redirect to Special:CentralLogin/start?token={$key} on loginwiki, SpecialCentralLogin::doLoginStart is invoked
 * 5) retrieves memcache data, looks up user
 * 6) if the user already has a full session on loginwiki, send back to Special:CentralLogin/status on the initiating wiki; SpecialCentralLogin::showLoginStatus is invoked
 * 7) call PostLoginRedirect hook
 * 8) CentralAuth might use this to add a link to Special:SulRenameWarning
 * 9) show login success page with autologin icons
 * 10) create a stub session for the user with name/id, set User cookie, set empty Token cookie
 * 11) store session id and login attempt secret in memcache, under a random key
 * 12) run CentralAuthSilentLoginRedirect hook
 * 13) redirect to Special:CentralLogin/complete?token={$key} on the initiating wiki,
 * 14) fetch memcache data via token, validate login attempt secret, drop temporary session data
 * 15) update global session id in memcache (this will also replace the stub session with a full session, and load the global login token into it) and in second-level domain cookie
 * 16) set RequestContext::$user
 * 17) run CentralAuthPostLoginRedirect hook
 * 18) if this was a registration and centralauth-welcomecreation-msg exists, show it and show autologin icons
 * 19) otherwise set edge login flag in session and redirect to page given by returnTo
 * 20) PostLoginRedirect hook is invoked, can modify redirect target
 * 21) on next page (via onBeforePageDisplay), unset flag and show autologin icons

CentralAuth autologin icons

 * 1) invoked via CentralAuthHooks::getDomainAutoLoginHtml (visible favicons) or CentralAuthHooks::getEdgeLoginHTML (invisible pixels)
 * 2) create a bunch of images referencing Special:CentralAutoLogin/start?from={$current_wiki} for each second-level domain (.wikipedia.org, .wikibooks.org etc) and for big multilang sites like meta, mw.org, Commons; for each of those requests:
 * 3) redirect to Special:CentralAutoLogin/checkLoggedIn?wikiid={$current_wiki} on loginwiki
 * 4) store global user id under a random memcache key (to do that, it will create the user object from the loginwiki session, thus verifying logged-in status)
 * 5) redirect to Special:CentralAutoLogin/createSession?token={$key} on the initiating wiki
 * 6) replace global user id in memcache with guid+wikiid under a new random key, store that key in the session
 * 7) redirect to Special:CentralAutoLogin/validateSession?token={$key}&wikiid={$current_wiki} on loginwiki
 * 8) pop guid+wikiid from memcache, validate them
 * 9) write user name, auth token, "central session id" (memcache key for auth data, stored in cookie) and misc data into memcache, under {$key}
 * 10) redirect to Special:CentralAutoLogin/setCookies on the initiating wiki
 * 11) get key from session which was stored in createSession step, use it to pop data from memcached
 * 12) verify guid-username match, validate auth token
 * 13) reset session, set username and global auth token in memcached, set username and maybe (depending on "remember me" pref) global auth token in second-level domain cookies
 * 14) set edge login flag in session (???)
 * 15) actually return the icon/pixel
 * 16) create a similar image for Special:CentralAutoLogin/refreshCookies?wikiid={$current_wiki} on loginwiki
 * 17) fetch "central session" data from memcached
 * 18) write the data into the second-level domain cookies

Loading user data

 * from DB: when creating a UserArray, CentralAuth uses the UserArrayFromResult hook to extend the results; it looks up the users in the globaluser table, and sets $user->centralAuthObj to the global user object
 * from session (invoked when RequestContext::$user is lazy-loaded): CentralAuth uses the UserLoadFromSession hook to replace normal logic
 * try to get username and auth token from centralauth cookie, fall back to "central session" key in memcache; if both unsuccessful, abort and fall back to normal User logic
 * load central user data from DB or memcache; if it does not exist/global auth token does not validate, abort and fall back to normal User logic
 * load local user id
 * abort and fall back to normal User logic if local id exists but local user is not attached
 * if local user does not exist, try to autocreate it:
 * check conditions (blocked, etc)
 * run AbortAutoAccount hook
 * add user to local DB
 * run AuthPlugin::initUser
 * run AuthPluginAutoCreate hook
 * CentralAuth will attach the local user to the global one
 * otherwise load local user data from DB
 * set up the session
 * store the global auth token in the session; if it had a different token before, touch the user and invalidate its cache
 * set $user->centralAuthObj to the central user object

CentralAuth actions on every pageview (via BeforePageDisplay)

 * for anons with non-JS browsers: send a request to Special:CentralAutoLogin/start on the current wiki via an invisible autologin icon
 * for anons with normal browsers:
 * skip if CentralAuthAnon cookie or localStorage flag is set
 * load a script from Special:CentralAutoLogin/checkLoggedIn?type=script&wikiid={$current_wiki} on loginwiki
 * this will go through the steps of the CentralAuth autologin sequence until /setCookies on the local wiki, but return and execute some JS code instead of an icon
 * if the login failed, set CentralAuthAnon localStorage or cookie flag
 * try to autocreate the user (as in onUserLoadFromSession 5.1)
 * if the local user is not attached, set CentralAuthAnon localStorage or cookie flag
 * if there is a returnto url param, clear CentralAuthAnon from cookie and localstorage and redirect via JS
 * otherwise, manipulate the DOM to make the page appear logged-in without a page reload, and add autologin icons for other sites
 * for logged-in users:
 * clear CentralAuthAnon from cookie and localstorage
 * display autologin icons if the edge login session flag is set