PluginProbe ʕ •ᴥ•ʔ
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.3
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.3
2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8 0.0.9 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.1.0 1.1.1 1.1.2 1.10.0 1.10.1 1.11.0 1.12.0 1.12.1 1.12.2 1.12.3 1.13.0 1.13.1 1.13.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.8.0 1.9.0 1.9.1 2.0.0 2.0.1 2.0.2 2.1.0 2.1.1 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.5.0 2.5.2 2.6.0
sureforms / inc / ai-form-builder / ai-auth.php
sureforms / inc / ai-form-builder Last commit date
ai-auth.php 5 months ago ai-form-builder.php 1 month ago ai-helper.php 3 months ago field-mapping.php 2 months ago
ai-auth.php
206 lines
1 <?php
2 /**
3 * SureForms - AI Auth.
4 *
5 * @package sureforms
6 * @since 0.0.8
7 */
8
9 namespace SRFM\Inc\AI_Form_Builder;
10
11 use SRFM\Inc\Helper;
12 use SRFM\Inc\Traits\Get_Instance;
13
14 // Exit if accessed directly.
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 /**
20 * SureForms AI Form Builder Class.
21 */
22 class AI_Auth {
23 use Get_Instance;
24
25 /**
26 * The key for encryption and decryption.
27 *
28 * @since 0.0.8
29 * @var string
30 */
31 private $key = '';
32
33 /**
34 * Initiates the auth process.
35 *
36 * @param \WP_REST_Request $request The request object.
37 * @since 0.0.8
38 * @return void
39 */
40 public function get_auth_url( $request ) {
41
42 $nonce = Helper::get_string_value( $request->get_header( 'X-WP-Nonce' ) );
43
44 if ( ! wp_verify_nonce( sanitize_text_field( $nonce ), 'wp_rest' ) ) {
45 wp_send_json_error( __( 'Nonce verification failed.', 'sureforms' ) );
46 }
47
48 // Generate a random key of 16 characters.
49 $this->key = wp_generate_password( 16, false );
50 // Persist key for decryption in handle_access_key (10 min TTL).
51 set_transient( 'srfm_ai_auth_key_' . get_current_user_id(), $this->key, 10 * MINUTE_IN_SECONDS );
52
53 // Get the source parameter from the query.
54 $source = sanitize_text_field( Helper::get_string_value( $request->get_param( 'source' ) ?? '' ) );
55 switch ( $source ) {
56 case 'onboarding':
57 $redirect_back = site_url() . '/wp-admin/admin.php?page=sureforms_menu#/onboarding/email-delivery';
58 break;
59 default:
60 $redirect_back = site_url() . '/wp-admin/admin.php?page=add-new-form';
61 }
62
63 // Prepare the token data.
64 $token_data = [
65 'redirect-back' => $redirect_back,
66 'key' => $this->key,
67 'site-url' => site_url(),
68 'nonce' => wp_create_nonce( 'ai_auth_nonce' ),
69 ];
70
71 $encoded_token_data = wp_json_encode( $token_data );
72
73 if ( empty( $encoded_token_data ) ) {
74 wp_send_json_error( [ 'message' => __( 'Failed to encode the token data.', 'sureforms' ) ] );
75 }
76
77 // Send the token data to the frontend for redirection.
78 wp_send_json_success( SRFM_BILLING_PORTAL . 'auth/?token=' . base64_encode( $encoded_token_data ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
79 }
80
81 /**
82 * Handles the access key.
83 *
84 * @param \WP_REST_Request $request The request object.
85 * @since 0.0.8
86 * @return void
87 */
88 public function handle_access_key( $request ) {
89
90 $nonce = Helper::get_string_value( $request->get_header( 'X-WP-Nonce' ) );
91
92 if ( ! wp_verify_nonce( sanitize_text_field( $nonce ), 'wp_rest' ) ) {
93 wp_send_json_error( __( 'Nonce verification failed.', 'sureforms' ) );
94 }
95
96 // get body data.
97 $body = json_decode( $request->get_body(), true );
98
99 if ( empty( $body ) ) {
100 wp_send_json_error( [ 'message' => __( 'Error processing Access Key.', 'sureforms' ) ] );
101 }
102
103 // get access key.
104 $access_key = is_array( $body ) && ! empty(
105 $body['accessKey']
106 ) ? Helper::get_string_value( $body['accessKey'] ) : '';
107
108 $stored_key = Helper::get_string_value( get_transient( 'srfm_ai_auth_key_' . get_current_user_id() ) );
109
110 if ( empty( $stored_key ) ) {
111 wp_send_json_error( [ 'message' => __( 'Authentication session expired. Please try again.', 'sureforms' ) ] );
112 }
113
114 // decrypt the access key.
115 if ( ! empty( $access_key ) ) {
116 $this->decrypt_access_key( $access_key, $stored_key );
117 } else {
118 wp_send_json_error( [ 'message' => __( 'No access key provided.', 'sureforms' ) ] );
119 }
120 }
121
122 /**
123 * Decrypts a string using OpenSSL decryption.
124 *
125 * @param string $data The data to decrypt.
126 * @param string $key The encryption key.
127 * @param string $method The encryption method (e.g., AES-256-CBC).
128 * @since 0.0.8
129 * @return string|false The decrypted string or false on failure.
130 */
131 public function decrypt_access_key( $data, $key, $method = 'AES-256-CBC' ) {
132 // Decode the data and split IV and encrypted data.
133 $decoded_data = base64_decode( $data ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
134
135 // if the data is not base64 encoded then return false.
136 if ( empty( $decoded_data ) ) {
137 return false;
138 }
139
140 // Extract the IV and encrypted data (billing portal sends IV::ENCRYPTED_DATA format).
141 $parts = explode( '::', $decoded_data, 2 );
142
143 if ( ! isset( $parts[1] ) ) {
144 wp_send_json_error( [ 'message' => __( 'Invalid access key format.', 'sureforms' ) ] );
145 }
146
147 $iv = $parts[0];
148 $encrypted = $parts[1];
149
150 // Decrypt the data using the server-stored key and the billing-portal-supplied IV.
151 $decrypted = openssl_decrypt( $encrypted, $method, $key, 0, $iv );
152
153 // if the decryption returns false then send error.
154 if ( empty( $decrypted ) ) {
155 wp_send_json_error( [ 'message' => __( 'Failed to decrypt the access key.', 'sureforms' ) ] );
156 }
157
158 // json decode the decrypted data.
159 $decrypted_data_array = json_decode( $decrypted, true );
160
161 if ( ! is_array( $decrypted_data_array ) || empty( $decrypted_data_array ) ) {
162 wp_send_json_error( [ 'message' => __( 'Failed to json decode the decrypted data.', 'sureforms' ) ] );
163 }
164
165 // verify the nonce that comes in $encrypted_email_array.
166 if ( empty( $decrypted_data_array['nonce'] ) || ! wp_verify_nonce( $decrypted_data_array['nonce'], 'ai_auth_nonce' ) ) {
167 wp_send_json_error( [ 'message' => __( 'Nonce verification failed.', 'sureforms' ) ] );
168 }
169
170 // check if the user email is present in the decrypted data.
171 if ( empty( $decrypted_data_array['user_email'] ) ) {
172 wp_send_json_error( [ 'message' => __( 'No user email found in the decrypted data.', 'sureforms' ) ] );
173 }
174
175 // Extract is_subscribed value if present.
176 $is_subscribed = false;
177 if ( isset( $decrypted_data_array['is_subscribed'] ) ) {
178 // Convert string 'true'/'false' to boolean if needed.
179 if ( is_string( $decrypted_data_array['is_subscribed'] ) ) {
180 $is_subscribed = 'true' === $decrypted_data_array['is_subscribed'];
181 } else {
182 $is_subscribed = (bool) $decrypted_data_array['is_subscribed'];
183 }
184
185 // Update the analytics option based on the preference.
186 // Set 'yes' if opted in, empty string if not.
187 $enable_contribution = $is_subscribed ? 'yes' : '';
188 update_option( 'sureforms_usage_optin', $enable_contribution );
189
190 // Remove is_subscribed from the decrypted data.
191 unset( $decrypted_data_array['is_subscribed'] );
192 }
193
194 // remove the nonce from the decrypted data before saving it to the options.
195 unset( $decrypted_data_array['nonce'] );
196
197 // Clean up the auth key transient.
198 delete_transient( 'srfm_ai_auth_key_' . get_current_user_id() );
199
200 // save the user email to the options.
201 update_option( 'srfm_ai_auth_user_email', $decrypted_data_array );
202
203 wp_send_json_success();
204 }
205 }
206