Manual:SessionManager and AuthManager/zh

SessionManager和AuthManager是中引入的两个与身份验证相关的框架，旨在替代“只能有一个！”. （以前的身份验证系统）的性质，无法进行联合登录（例如“使用您的Google帐户登录”）而没有使用各种钩子来绕过MediaWiki对基于密码的身份验证的关注，以及无法进行会话管理 除了cookie以外的任何东西，都无需再次使用各种钩子来破解MediaWiki对基于PHP cookie的会话的依赖.

What do they do?
SessionManager is concerned with taking an incoming request and connecting it with a user-session, i.e. the fact that the request is from a logged-in user (or not) and any data that should persist across requests like PHP's. Usually this means cookies holding the "session key" and the user's ID and token, but it could as easily be based on credentials in OAuth headers or a TLS client certificate.

AuthManager is concerned with logging in by supplying credentials to Special:UserLogin or the like, and with creating accounts with new credentials. This includes the usual username and password form, but also includes mechanisms such as logging in via Google, Facebook, or other third-party authentication service; multi-factor authentication; and other login-time checks such as password resets, CAPTCHAs, and so on.

As a system administrator
The available session providers are configured via. See documentation of that global and of the individual session providers for details.

The available authentication providers are configured via. See documentation of that global and of the individual authentication providers for details.

To handle operations where the user would traditionally be asked to enter their current password for confirmation, AuthManager instead requires that the user have actively logged in recently during the current session. To configure what exactly is meant by "recently", use  to set time limits for each operation. To determine what happens when the user is using something like OAuth where each request is individually authenticated and "log in" isn't possible, use.

Sessions
To access the current session from your code, the most straightforward mechanism is to use the  method on your   object. The resulting  object has methods to access the data that, in the past, you'd have accessed using   and   or by directly accessing.

To test if a session is currently active where you would have previously used code like, fetch the Session object and use its   method. To ensure that the session is persistent, fetch it and use its  method instead of using.

If you need to access session data in a context other than a client request, e.g. if you saved the session ID to run a background job and need to update status in the session data, use.

Authentication
If you're performing a security-sensitive action that would traditionally request the user's password for verification, use  to check whether the user has authenticated recently enough, and if necessary direct them through Special:UserLogin to re-authenticate. can handle this for you if you return non-false from.

Other than that, most "consumer" code won't need to interact with AuthManager directly, since you really shouldn't be implementing your own authentication flow. To determine if a user is logged in, use the usual methods such as the  on an. To see that a user is authenticated, have them log in via the usual method.

But if you really insist: Authentication in AuthManager is a multi-step process that goes something like this:
 * 1) Call $authManager->canAuthenticateNow to determine if authentication is even possible.
 * 2) Call $authManager->getAuthenticationRequests( AuthManager::ACTION_LOGIN ) to determine which AuthenticationRequest subclasses are needed, convert them to form fields, and present them to the user.
 * 3) Once the user submits the form, use AuthenticationRequest::loadRequestsFromSubmission to populate the requests with data and pass them to $authManager->beginAuthentication.
 * 4) Take action depending on the status of the AuthenticationResponse:
 * 5) * For status UI, convert the needed AuthenticationRequests to form fields, present to the user, and on submission use AuthenticationRequest::loadRequestsFromSubmission to populate the new requests with data and pass them to $authManager->continueAuthentication and handle the new response.
 * 6) * For status REDIRECT, redirect the user to the specified URL. Eventually the user should be redirected back, at which point the needed AuthenticationRequests should be populated with data and passed to $authManager->continueAuthentication.
 * 7) * For status PASS, authentication has succeeded.
 * 8) * For status RESTART, authentication with a third-party service has succeeded but those credentials aren't associated with a local account. Treat this as either UI or FAIL.
 * 9) * For status FAIL, authentication has failed. The response might contain an additional AuthenticationRequest that may be passed into $authManager->beginAuthentication or $authManager->beginAccountCreation to preserve state.

The processes for account creation and linking of third-party credentials are similar.

As a provider
If you're writing code that is going to provide new functionality for SessionManager or AuthManager, first you need to decide what kind of thing you're providing:
 * If you're implementing a new way for taking a WebRequest and determining the authenticated user and session data from it, you want to implement.
 * If you're doing something that seems like logging in, a SessionProvider is not what you want. You probably want a PrimaryAuthenticationProvider instead.
 * If you want to generically add additional checks to the process of getting a Session from a WebRequest, you'll likely want to look at the and  hooks. The first allows adding additional metadata to be saved with the session, while the second allows you to look at the metadata when a Session is being loaded and if necessary veto its use.
 * If you're implementing a new way to log in, you want to implement.
 * If your thing isn't so much a way to log in as a new way to just "be" logged in such as OAuth or TLS client certificates, you don't want this. You probably want a SessionProvider instead.
 * If you're implementing some new check that someone has to pass before being able to log in, such as a CAPTCHA, you want to implement.
 * If you're implementing something that should happen after logging in, including testing of a second factor, you want to implement.

In all cases, providers are constructed using  from   or   and then have dependencies injected by setter methods.

SessionProvider
Class documentation: 

There are broadly three types of session providers, based on two abilities:
 * The ability to save any randomly-generated session ID, versus the ID being based on properties of the request that cannot be changed.
 * The ability to be associated with any arbitrary user who logs in, versus having a user that is intrinsically associated with the request.
 * This latter property is sometimes referred to as making the session "mutable" versus "immutable".

The resulting three types are:
 * Providers that can save any randomly-generated session ID, and can also be associated with any arbitrary user. A prime example of these are the traditional cookie-based sessions.
 * It's not necessary that the provider actually does anything to save the user's identity; as long as the session ID is provided, the user can be determined from the saved metadata.
 * Providers that have a user and session ID intrinsically associated with the request. Good examples here are OAuth and TLS client certificates: the OAuth headers or TLS client certificate are associated with a particular user and you can't just arbitrarily change them to be associated with a different user.
 * Providers that have a user intrinsically associated with the request, but can save any randomly-generated session ID. An example might be HTTP Digest authentication using the "opaque" field to hold the session ID.
 * You can often take a method with an intrinsic user and no ability to save randomly-generated session IDs and use a cookie to give it that ability; the  base class implements the cookie portion of such a provider.

It's also possible to have a provider with the ability to be associated with any arbitrary user but lacking the ability to save a randomly-generated session ID. This doesn't seem to make any sense, though.

A session provider "provides" sessions by returning a  object from either   (for the session associated with a WebRequest) or   (for a new session not yet associated with any WebRequest). The properties of the SessionInfo are:
 * The priority of the SessionInfo: if by some chance multiple providers find session data in the same request, the one with the highest priority is used. If there is a tie, SessionManager will throw an error. Generally a provider will either always use SessionInfo::MAX_PRIORITY (i.e. "you must use this SessionInfo") or will take the desired priority as a parameter to its constructor.
 * The session ID. It's ok for a provider to provide a SessionInfo without providing an ID if it does identify a user.
 * Whether the request identifies a user, and if that user is authenticated or just named. Using cookie-based sessions as an example,
 * "No user" results when the UserID or UserName cookie isn't present in the request. The user will be determined from the saved session metadata.
 * "Unauthenticated user" results when the UserID or UserName cookie is present, but the "Token" cookie is missing. Any user's ID or name is publicly available, so without the token we can't know that this isn't a spoofing attempt.
 * This is still worthwhile, since we can check that the saved session metadata identifies the same user to prevent someone from using a session ID that has expired and been reassigned to a different user.
 * "Authenticated user" results when the UserID or UserName cookie is present and the "Token" cookie is also present, and  returns true. Since the token is supposed to be secret, we can assume that a correct token means this is the known user.
 * Tokens that come in from the WebRequest must always be checked using  to avoid timing attacks.
 * There's no case where the UserID or UserName cookie and the "Token" cookie are present, but  returns false. If this happens, the provider must not provide any SessionInfo.
 * Whether various bits were actually found in the WebRequest: the session ID, the user's token, and the forceHTTPS flag.
 * Any additional metadata the provider wants to associate with the session. This is often useful if the provider does authorization (e.g. OAuth grants) in addition to authentication.

The session provider is also responsible for "persisting" the session to a WebResponse, which is done on a call to. If the provider has the ability to save randomly-generated session IDs, it does so here. If it has the ability to independently save the user identity, it should always save the user's ID or name but must only save the user's token if the session's  method returns true.

For proper caching behavior, the session provider needs to use  and   to indicate which request headers and cookies it uses to determine the SessionInfo. It also needs to provide messages to identify its sessions in error messages, and to provide a hint as to why sessions aren't being persisted when they should be (e.g. that the browser is set to reject cookies).

PreAuthenticationProvider
Class documentation:, 

A "pre"-authentication provider can provide fields for the login or account creation form, and perform checks on a login or account creation attempt before any actual authentication happens. For example, it might throttle login attempts by IP address or present a CAPTCHA that must be solved in order to create the account.

PrimaryAuthenticationProvider
Class documentation:, 

A "primary" authentication provider is responsible for actual user authentication:
 * Taking input from the login form, determining which user is trying to log in, and actually authenticating that user.
 * Ensuring that the user being created will be able to log in later.
 * Testing whether a user name already exists in the authentication backend storage.
 * Changing the authentication data associated with an existing user, or adding and removing associations with third-party authentication provider accounts.

AuthManager may be configured to use multiple primary authentication providers. On the initial form submission each primary provider in turn is tried, and if it returns an ABSTAIN result the next in line is tried until either one returns a non-ABSTAIN response or it runs out of providers (in which case it generates a FAIL).

The authentication process for a provider is also multi-step: non-ABSTAIN responses include the usual PASS and FAIL, but also UI to present an additional input form for the user to fill out and REDIRECT to send the browser to some third party which should eventually redirect back to continue the authentication process.

SecondaryAuthenticationProvider
Class documentation:, 

A "secondary" authentication provider is called after the user has been authenticated by a "primary". For example, it might request a second factor for multi-factor authentication, prompt the user to change their password, or check that the user isn't blocked. Secondary providers are also called on account creation so they can do things such as prompt to set up multi-factor authentication.

AuthManager may be configured with multiple secondary authentication providers, in which case all are run and must PASS or ABSTAIN for the authentication process to succeed. Like primary providers, they may respond with UI and REDIRECT to interact with the user.

Inter-authentication-provider communication
AuthManager provides support for communication between provider modules during the authentication process. For example, password-based primary authentication providers that have the concept of password expiry can call, which will cause the secondary authentication provider   to prompt the user to change their password. It's up to each provider to document which authentication data "keys" it uses and what data it needs to be provided to do its job.

參見

 * Launch task: T89459, and original RFC.
 * Launch task: T89459, and original RFC.
 * Launch task: T89459, and original RFC.
 * Launch task: T89459, and original RFC.
 * Launch task: T89459, and original RFC.
 * Launch task: T89459, and original RFC.