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 |