3 * Abstract class for managing user session tokens.
7 abstract class WP_Session_Tokens {
19 * Protected constructor.
23 * @param int $user_id User whose session to manage.
25 protected function __construct( $user_id ) {
26 $this->user_id = $user_id;
30 * Get a session token manager instance for a user.
32 * This method contains a filter that allows a plugin to swap out
33 * the session manager for a subclass of WP_Session_Tokens.
39 * @param int $user_id User whose session to manage.
41 final public static function get_instance( $user_id ) {
43 * Filter the session token manager used.
47 * @param string $session Name of class to use as the manager.
48 * Default 'WP_User_Meta_Session_Tokens'.
50 $manager = apply_filters( 'session_token_manager', 'WP_User_Meta_Session_Tokens' );
51 return new $manager( $user_id );
55 * Hashes a session token for storage.
60 * @param string $token Session token to hash.
61 * @return string A hash of the session token (a verifier).
63 final private function hash_token( $token ) {
64 // If ext/hash is not present, use sha1() instead.
65 if ( function_exists( 'hash' ) ) {
66 return hash( 'sha256', $token );
68 return sha1( $token );
73 * Get a user's session.
78 * @param string $token Session token
79 * @return array User session
81 final public function get( $token ) {
82 $verifier = $this->hash_token( $token );
83 return $this->get_session( $verifier );
87 * Validate a user's session token as authentic.
89 * Checks that the given token is present and hasn't expired.
94 * @param string $token Token to verify.
95 * @return bool Whether the token is valid for the user.
97 final public function verify( $token ) {
98 $verifier = $this->hash_token( $token );
99 return (bool) $this->get_session( $verifier );
103 * Generate a session token and attach session information to it.
105 * A session token is a long, random string. It is used in a cookie
106 * link that cookie to an expiration time and to ensure the cookie
107 * becomes invalidated upon logout.
109 * This function generates a token and stores it with the associated
110 * expiration time (and potentially other session information via the
111 * `attach_session_information` filter).
116 * @param int $expiration Session expiration timestamp.
117 * @return string Session token.
119 final public function create( $expiration ) {
121 * Filter the information attached to the newly created session.
123 * Could be used in the future to attach information such as
124 * IP address or user agent to a session.
128 * @param array $session Array of extra data.
129 * @param int $user_id User ID.
131 $session = apply_filters( 'attach_session_information', array(), $this->user_id );
132 $session['expiration'] = $expiration;
135 if ( !empty( $_SERVER['REMOTE_ADDR'] ) ) {
136 $session['ip'] = $_SERVER['REMOTE_ADDR'];
140 if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
141 $session['ua'] = wp_unslash( $_SERVER['HTTP_USER_AGENT'] );
145 $session['login'] = time();
147 $token = wp_generate_password( 43, false, false );
149 $this->update( $token, $session );
155 * Update a session token.
160 * @param string $token Session token to update.
161 * @param array $session Session information.
163 final public function update( $token, $session ) {
164 $verifier = $this->hash_token( $token );
165 $this->update_session( $verifier, $session );
169 * Destroy a session token.
174 * @param string $token Session token to destroy.
176 final public function destroy( $token ) {
177 $verifier = $this->hash_token( $token );
178 $this->update_session( $verifier, null );
182 * Destroy all session tokens for this user,
183 * except a single token, presumably the one in use.
188 * @param string $token_to_keep Session token to keep.
190 final public function destroy_others( $token_to_keep ) {
191 $verifier = $this->hash_token( $token_to_keep );
192 $session = $this->get_session( $verifier );
194 $this->destroy_other_sessions( $verifier );
196 $this->destroy_all_sessions();
201 * Determine whether a session token is still valid,
202 * based on expiration.
207 * @param array $session Session to check.
208 * @return bool Whether session is valid.
210 final protected function is_still_valid( $session ) {
211 return $session['expiration'] >= time();
215 * Destroy all session tokens for a user.
220 final public function destroy_all() {
221 $this->destroy_all_sessions();
225 * Destroy all session tokens for all users.
231 final public static function destroy_all_for_all_users() {
232 $manager = apply_filters( 'session_token_manager', 'WP_User_Meta_Session_Tokens' );
233 call_user_func( array( $manager, 'drop_sessions' ) );
237 * Retrieve all sessions of a user.
242 * @return array Sessions of a user.
244 final public function get_all() {
245 return array_values( $this->get_sessions() );
249 * This method should retrieve all sessions of a user, keyed by verifier.
254 * @return array Sessions of a user, keyed by verifier.
256 abstract protected function get_sessions();
259 * This method should look up a session by its verifier (token hash).
264 * @param string $verifier Verifier of the session to retrieve.
265 * @return array|null The session, or null if it does not exist.
267 abstract protected function get_session( $verifier );
270 * This method should update a session by its verifier.
272 * Omitting the second argument should destroy the session.
277 * @param string $verifier Verifier of the session to update.
279 abstract protected function update_session( $verifier, $session = null );
282 * This method should destroy all session tokens for this user,
283 * except a single session passed.
288 * @param string $verifier Verifier of the session to keep.
290 abstract protected function destroy_other_sessions( $verifier );
293 * This method should destroy all sessions for a user.
298 abstract protected function destroy_all_sessions();
301 * This static method should destroy all session tokens for all users.
307 public static function drop_sessions() {}
311 * Meta-based user sessions token manager.
315 class WP_User_Meta_Session_Tokens extends WP_Session_Tokens {
318 * Get all sessions of a user.
323 * @return array Sessions of a user.
325 protected function get_sessions() {
326 $sessions = get_user_meta( $this->user_id, 'session_tokens', true );
328 if ( ! is_array( $sessions ) ) {
332 $sessions = array_map( array( $this, 'prepare_session' ), $sessions );
333 return array_filter( $sessions, array( $this, 'is_still_valid' ) );
337 * Converts an expiration to an array of session information.
339 * @param mixed $session Session or expiration.
340 * @return array Session.
342 protected function prepare_session( $session ) {
343 if ( is_int( $session ) ) {
344 return array( 'expiration' => $session );
351 * Retrieve a session by its verifier (token hash).
356 * @param string $verifier Verifier of the session to retrieve.
357 * @return array|null The session, or null if it does not exist
359 protected function get_session( $verifier ) {
360 $sessions = $this->get_sessions();
362 if ( isset( $sessions[ $verifier ] ) ) {
363 return $sessions[ $verifier ];
370 * Update a session by its verifier.
375 * @param string $verifier Verifier of the session to update.
376 * @param array $session Optional. Session. Omitting this argument destroys the session.
378 protected function update_session( $verifier, $session = null ) {
379 $sessions = $this->get_sessions();
382 $sessions[ $verifier ] = $session;
384 unset( $sessions[ $verifier ] );
387 $this->update_sessions( $sessions );
391 * Update a user's sessions in the usermeta table.
396 * @param array $sessions Sessions.
398 protected function update_sessions( $sessions ) {
400 update_user_meta( $this->user_id, 'session_tokens', $sessions );
402 delete_user_meta( $this->user_id, 'session_tokens' );
407 * Destroy all session tokens for a user, except a single session passed.
412 * @param string $verifier Verifier of the session to keep.
414 protected function destroy_other_sessions( $verifier ) {
415 $session = $this->get_session( $verifier );
416 $this->update_sessions( array( $verifier => $session ) );
420 * Destroy all session tokens for a user.
425 protected function destroy_all_sessions() {
426 $this->update_sessions( array() );
430 * Destroy all session tokens for all users.
436 public static function drop_sessions() {
437 delete_metadata( 'user', 0, 'session_tokens', false, true );