Requests for comment/AuthManager

The current authentication and authorization system is very limited and restricted in terms of the allowed customization, for system administrators, core developers, and extension developers.

Current problems
Our current authn system consists of:


 * User::crypt, User::comparePasswords, User::loadFromSession, etc.
 * The AuthPlugin interface and $wgAuth

This combination of completely hard-coded code paths has led to a number of restrictions and problems:
 * Only one external authentication service can be used at a time
 * Users in an external service must be identified by a username and password
 * There is a massive mix of concerns within the User class, which handles model logic as well as authentication and session logic

This RFC hopes to fix just a section of this problem: authentication. This will be done by deprecating the AuthPlugin system and replacing it will a comprehensive authentication layer. For scope, we're defining authentication as "how to get a session cookie for a logged-in session".

Use cases
In preparing this RfC, the following use cases were considered:
 * Local MediaWiki authentication
 * Authentication services like LDAP or CentralAuth
 * Fallback authentication (if user doesn't exist in CentralAuth, look in local database)
 * Federated services like Google, OpenID
 * Additional authentication requirements like two factor
 * Post authentication steps like an audit log (E:AccountAudit)
 * Pre-authentication requirements like rate limiting or captchas
 * Combinations of all of the above!

AuthManager
The AuthManager is a singleton instance that instantiates and maintains auth "modules". It is compromised of the following:

AuthenticationProvider
Something like local MediaWiki password auth, CentralAuth-auth, LDAP, etc. There can be multiple of these installed on a wiki, but only one can be used per authentication request.

If there are multiple primary modules installed, the login form will provide options on which authentication method the user wishes to use (see Phabricator or StackOverflow for an example).

Each primary authentication module:
 * Is a "link" or "create" type (for account creation):
 * A "link" type would have the user authenticate against the backend service, and then store a mapping from the backend-service user to the created User.
 * A "create" type would create a new account in its backend store under the provided username. Would probably need to somehow specify UI fields such as "password".
 * Probably any "create" module could declare itself mandatory, and the UI fields for all mandatory "create" modules would be required. If there are no mandatory "create" modules, the user would have to pick one module ("create" or "link" types).
 * Once the new User is created, offer to link additional modules? Or let the user do that in Special:Preferences later?
 * Provides input requirements (for login form or API action=login)
 * Gets user provided input, returns "PASS" "FAIL" or "REDIRECT"
 * if "REDIRECT", a second call would be made that expects a "PASS" or "FAIL" response

PreAuthenticationFilter
Examples: Captcha checking, or rate limiting. These would also be able to provide their own input requirements and validate them, returning "PASS" or "FAIL".

SecondaryAuthenticationProvider
Examples: two-factor, password expiry/reset, any kind of post-login notification.


 * These are in an ordered list
 * At this point the user would have a partial session, we think we know who they are, but they can't do anything until they pass these modules
 * Respond with "PASS", "FAIL", "UI", "REDIRECT"
 * UI here both applies to API and index.php, and means that there is some interaction needed on the user's part
 * If everything "PASS"es, the session would be finalized and given full access to the account

PostAuthenticationFilter
Probably just a hook that allows extensions to control the redirect flow (eg: GettingStarted).

Requiring re-authentication
Some special pages like Special:ChangeEmail or Special:ResetPassword require re-authentication. The user would go through the normal login flow, except at the end we would check they are the same user. We could implement this by storing in the session when the user last re-authenticated and if it's not recent enough ask them to re-do it. The API would then respond with a "needsReauthentication" error.

Session management

 * Have a stack of "session providers"
 * Near the beginning of each request, go through each one asking if it will provide a session-key
 * Providers can return some indication of priority, including "Use me or throw an error".
 * Then MW can look up the session in the already-existing session storage backend.
 * The session provider that was used will control whether certain session management functions can succeed. For example, a session provided via OAuth wouldn't allow for destroying the session or creating a different session.


 * Note: Tyler recommends looking at what Symfony has:.
 * In general, this seems to match up more-or-less with what we've already designed. In specific, though, it seems that it might not afford the all the features we need to support all of our use-cases.