$email, 'created_at' => $this->clock->now()->format( DATE_ATOM ), ], self::MAGIC_TTL ); /** * Fires when a manage magic-link token is created (outbound send is Milestone 8). * * @param string $email Sanitised email. * @param string $token Plaintext token (listeners must not log it). * @since 0.1.0 */ do_action( 'bookline/manage/magic_link_created', $email, $token ); } return [ 'message' => __( 'If that address is on file, a link has been sent.', 'buildler' ), ]; } /** * Exchange a magic-link token for a customer session token. * * @param string $magic_token Magic-link token. * @return array{token: string, expires_at: string}|null Null when invalid. * * @since 0.1.0 */ public function exchange( string $magic_token ): ?array { $key = self::MAGIC_PREFIX . hash( 'sha256', $magic_token ); $data = get_transient( $key ); delete_transient( $key ); if ( ! is_array( $data ) || ! isset( $data['email'] ) || ! is_string( $data['email'] ) ) { return null; } $session = bin2hex( random_bytes( 32 ) ); $expires = $this->clock->now()->getTimestamp() + self::SESSION_TTL; set_transient( self::SESSION_PREFIX . hash( 'sha256', $session ), [ 'email' => $data['email'], ], self::SESSION_TTL ); return [ 'token' => $session, 'expires_at' => gmdate( 'c', $expires ), ]; } /** * Whether a customer session token is valid. * * @param string $session Session token. * * @since 0.1.0 */ public function is_valid_session( string $session ): bool { $data = get_transient( self::SESSION_PREFIX . hash( 'sha256', $session ) ); return is_array( $data ) && isset( $data['email'] ); } /** * Email bound to a valid session, or null. * * @param string $session Session token. * * @since 0.1.0 */ public function email_for_session( string $session ): ?string { $data = get_transient( self::SESSION_PREFIX . hash( 'sha256', $session ) ); if ( ! is_array( $data ) || ! isset( $data['email'] ) || ! is_string( $data['email'] ) ) { return null; } return $data['email']; } }