PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.3.0
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.3.0
3.3.0 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 trunk 1.0.4 1.1.0 1.1.0.1 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 All 162 releases
cartflows / modules / content-generation / class-cartflows-ai-auth.php

class-cartflows-ai-auth.php in CartFlows – Funnel Builder & Checkout Plugin for WooCommerce 3.3.0, at modules/content-generation/class-cartflows-ai-auth.php

205 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * AI Authentication.
4 *
5 * @package cartflows
6 */
7
8 // Exit if accessed directly.
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12 /**
13 * AI Authentication.
14 *
15 * @since x.x.x
16 */
17 class Cartflows_Ai_Auth {
18
19 /**
20 * Member Variable
21 *
22 * @var object instance
23 */
24 private static $instance;
25
26 /**
27 * Encryption key.
28 *
29 * @since x.x.x
30 * @var string
31 */
32 public $key;
33
34 /**
35 * Initiator
36 */
37 public static function get_instance() {
38
39 if ( ! isset( self::$instance ) ) {
40 self::$instance = new self();
41 }
42
43 return self::$instance;
44 }
45
46 /**
47 * Constructor
48 */
49 public function __construct() {}
50
51 /**
52 * Get Auth URL.
53 *
54 * @since x.x.x
55 * @param string $redirect_back Absolute URL the auth flow should return to. Falls back to the CartFlows dashboard.
56 * @return string|WP_Error
57 */
58 public function get_auth_url( $redirect_back = '' ) {
59 // Generate a random key of 16 characters.
60 $this->key = wp_generate_password( 16, false );
61
62 $default_redirect = admin_url( 'admin.php?page=cartflows' );
63
64 // Only allow same-site URLs — the portal appends the access key here, so an external URL would leak it.
65 $redirect_back = ! empty( $redirect_back ) ? wp_validate_redirect( $redirect_back, $default_redirect ) : $default_redirect;
66
67 // Prepare the token data.
68 $token_data = array(
69 'redirect-back' => $this->sanitize_redirect_back( $redirect_back ),
70 'key' => $this->key,
71 'site-url' => site_url(),
72 'nonce' => wp_create_nonce( 'cartflows_ai_auth_nonce' ),
73 );
74
75 $encoded_token_data = wp_json_encode( $token_data );
76
77 if ( empty( $encoded_token_data ) ) {
78 return new WP_Error( 'failed_to_encode_token_data', __( 'Failed to encode the token data.', 'cartflows' ) );
79 }
80
81 return CARTFLOWS_SERVER_URL . 'auth/?token=' . base64_encode( $encoded_token_data );
82 }
83
84 /**
85 * Get Auth status.
86 *
87 * @since x.x.x
88 * @return bool
89 */
90 public function get_auth_status() {
91 $auth_status = get_option( 'cartflows_auth', false );
92 return ! empty( $auth_status );
93 }
94
95 /**
96 * Save Auth.
97 *
98 * @since x.x.x
99 * @param string $data Data to save.
100 * @param string $key Key to use for encryption.
101 * @param string $method Encryption method. Default is AES-256-CBC.
102 * @return bool|WP_Error
103 */
104 public function save_auth( $data, $key, $method = 'AES-256-CBC' ) {
105
106 // Decode the data and split IV and encrypted data.
107 $decoded_data = base64_decode( $data ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
108
109 // if the data is not base64 encoded then return false.
110 if ( empty( $decoded_data ) ) {
111 return new WP_Error( 'failed_to_decode', __( 'Failed to decode the access key.', 'cartflows' ) );
112 }
113
114 // split the key and encrypted data.
115 list( $key, $encrypted ) = explode( '::', $decoded_data, 2 );
116
117 // Decrypt the data using the key.
118 $decrypted = openssl_decrypt( $encrypted, $method, $key, 0, $key );
119
120 // if the decryption returns false then send error.
121 if ( empty( $decrypted ) ) {
122 return new WP_Error( 'failed_to_decrypt', __( 'Failed to decrypt the access key.', 'cartflows' ) );
123 }
124
125 // json decode the decrypted data.
126 $decrypted_data_array = json_decode( $decrypted, true );
127
128 if ( ! is_array( $decrypted_data_array ) || empty( $decrypted_data_array ) ) {
129 return new WP_Error( 'failed_to_json_decode', __( 'Failed to json decode the decrypted data.', 'cartflows' ) );
130 }
131
132 // The nonce is mandatory — the key travels in the payload, so it is the only proof this site started the flow.
133 if ( empty( $decrypted_data_array['nonce'] ) || ! wp_verify_nonce( $decrypted_data_array['nonce'], 'cartflows_ai_auth_nonce' ) ) {
134 return new WP_Error( 'nonce_verification_failed', __( 'Nonce verification failed.', 'cartflows' ) );
135 }
136
137 // check if the user email is present in the decrypted data.
138 if ( empty( $decrypted_data_array['user_email'] ) ) {
139 return new WP_Error( 'no_user_email', __( 'No user email found in the decrypted data.', 'cartflows' ) );
140 }
141
142 // Extract is_subscribed value if present.
143 $is_subscribed = false;
144 if ( isset( $decrypted_data_array['is_subscribed'] ) ) {
145 // Convert string 'true'/'false' to boolean if needed.
146 if ( is_string( $decrypted_data_array['is_subscribed'] ) ) {
147 $is_subscribed = 'true' === $decrypted_data_array['is_subscribed'];
148 } else {
149 $is_subscribed = (bool) $decrypted_data_array['is_subscribed'];
150 }
151
152 // Update the analytics option based on the preference.
153 // Set 'yes' if opted in, empty string if not.
154 $enable_contribution = $is_subscribed ? 'yes' : '';
155 update_option( 'cartflows_usage_optin', $enable_contribution );
156
157 // Remove is_subscribed from the decrypted data.
158 unset( $decrypted_data_array['is_subscribed'] );
159 }
160
161 // remove the nonce from the decrypted data before saving it to the options.
162 unset( $decrypted_data_array['nonce'] );
163
164 // save the user email to the options.
165 update_option( 'cartflows_auth', $decrypted_data_array );
166
167 // One-shot transient so the next admin page load can show a success banner.
168 set_transient( 'cartflows_auth_connected_notice', 1, MINUTE_IN_SECONDS );
169
170 return true;
171 }
172
173 /**
174 * Restrict the redirect-back URL to the current admin host so a hijacked
175 * request can't bounce users to an arbitrary site after auth completes.
176 *
177 * @since x.x.x
178 * @param string $url Candidate redirect URL.
179 * @return string
180 */
181 private function sanitize_redirect_back( $url ) {
182 $default = admin_url( 'admin.php?page=cartflows' );
183
184 if ( empty( $url ) || ! is_string( $url ) ) {
185 return $default;
186 }
187
188 $candidate = esc_url_raw( $url );
189 $admin_host = wp_parse_url( admin_url(), PHP_URL_HOST );
190 $url_host = wp_parse_url( $candidate, PHP_URL_HOST );
191
192 if ( '' === $candidate || $admin_host !== $url_host ) {
193 return $default;
194 }
195
196 return $candidate;
197 }
198
199 }
200
201 /**
202 * Kicking this off by calling 'get_instance()' method
203 */
204 Cartflows_Ai_Auth::get_instance();
205