WC_Auth

The WooCommerce WC Auth class.

Defined (1)

The class is defined in the following location(s).

/includes/class-wc-auth.php  
  1. class WC_Auth { 
  2.  
  3. /** 
  4. * Version. 
  5. * @var int 
  6. */ 
  7. const VERSION = 1; 
  8.  
  9. /** 
  10. * Setup class. 
  11. * @since 2.4.0 
  12. */ 
  13. public function __construct() { 
  14. // Add query vars 
  15. add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 ); 
  16.  
  17. // Register auth endpoint 
  18. add_action( 'init', array( __CLASS__, 'add_endpoint' ), 0 ); 
  19.  
  20. // Handle auth requests 
  21. add_action( 'parse_request', array( $this, 'handle_auth_requests' ), 0 ); 
  22.  
  23. /** 
  24. * Add query vars. 
  25. * @since 2.4.0 
  26. * @param array $vars 
  27. * @return string[] 
  28. */ 
  29. public function add_query_vars( $vars ) { 
  30. $vars[] = 'wc-auth-version'; 
  31. $vars[] = 'wc-auth-route'; 
  32. return $vars; 
  33.  
  34. /** 
  35. * Add auth endpoint. 
  36. * @since 2.4.0 
  37. */ 
  38. public static function add_endpoint() { 
  39. add_rewrite_rule( '^wc-auth/v([1]{1})/(.*)?', 'index.php?wc-auth-version=$matches[1]&wc-auth-route=$matches[2]', 'top' ); 
  40.  
  41. /** 
  42. * Get scope name. 
  43. * @since 2.4.0 
  44. * @param string $scope 
  45. * @return string 
  46. */ 
  47. protected function get_i18n_scope( $scope ) { 
  48. $permissions = array( 
  49. 'read' => __( 'Read', 'woocommerce' ),  
  50. 'write' => __( 'Write', 'woocommerce' ),  
  51. 'read_write' => __( 'Read/Write', 'woocommerce' ),  
  52. ); 
  53.  
  54. return $permissions[ $scope ]; 
  55.  
  56. /** 
  57. * Return a list of permissions a scope allows. 
  58. * @since 2.4.0 
  59. * @param string $scope 
  60. * @return array 
  61. */ 
  62. protected function get_permissions_in_scope( $scope ) { 
  63. $permissions = array(); 
  64. switch ( $scope ) { 
  65. case 'read' : 
  66. $permissions[] = __( 'View coupons', 'woocommerce' ); 
  67. $permissions[] = __( 'View customers', 'woocommerce' ); 
  68. $permissions[] = __( 'View orders and sales reports', 'woocommerce' ); 
  69. $permissions[] = __( 'View products', 'woocommerce' ); 
  70. break; 
  71. case 'write' : 
  72. $permissions[] = __( 'Create webhooks', 'woocommerce' ); 
  73. $permissions[] = __( 'Create coupons', 'woocommerce' ); 
  74. $permissions[] = __( 'Create customers', 'woocommerce' ); 
  75. $permissions[] = __( 'Create orders', 'woocommerce' ); 
  76. $permissions[] = __( 'Create products', 'woocommerce' ); 
  77. break; 
  78. case 'read_write' : 
  79. $permissions[] = __( 'Create webhooks', 'woocommerce' ); 
  80. $permissions[] = __( 'View and manage coupons', 'woocommerce' ); 
  81. $permissions[] = __( 'View and manage customers', 'woocommerce' ); 
  82. $permissions[] = __( 'View and manage orders and sales reports', 'woocommerce' ); 
  83. $permissions[] = __( 'View and manage products', 'woocommerce' ); 
  84. break; 
  85. return apply_filters( 'woocommerce_api_permissions_in_scope', $permissions, $scope ); 
  86.  
  87. /** 
  88. * Build auth urls. 
  89. * @since 2.4.0 
  90. * @param array $data 
  91. * @param string $endpoint 
  92. * @return string 
  93. */ 
  94. protected function build_url( $data, $endpoint ) { 
  95. $url = wc_get_endpoint_url( 'wc-auth/v' . self::VERSION, $endpoint, home_url( '/' ) ); 
  96.  
  97. return add_query_arg( array( 
  98. 'app_name' => wc_clean( $data['app_name'] ),  
  99. 'user_id' => wc_clean( $data['user_id'] ),  
  100. 'return_url' => urlencode( $this->get_formatted_url( $data['return_url'] ) ),  
  101. 'callback_url' => urlencode( $this->get_formatted_url( $data['callback_url'] ) ),  
  102. 'scope' => wc_clean( $data['scope'] ),  
  103. ), $url ); 
  104.  
  105. /** 
  106. * Decode and format a URL. 
  107. * @param string $url 
  108. * @return array 
  109. */ 
  110. protected function get_formatted_url( $url ) { 
  111. $url = urldecode( $url ); 
  112.  
  113. if ( ! strstr( $url, '://' ) ) { 
  114. $url = 'https://' . $url; 
  115.  
  116. return $url; 
  117.  
  118. /** 
  119. * Make validation. 
  120. * @since 2.4.0 
  121. */ 
  122. protected function make_validation() { 
  123. $params = array( 
  124. 'app_name',  
  125. 'user_id',  
  126. 'return_url',  
  127. 'callback_url',  
  128. 'scope',  
  129. ); 
  130.  
  131. foreach ( $params as $param ) { 
  132. if ( empty( $_REQUEST[ $param ] ) ) { 
  133. /** translators: %s: parameter */ 
  134. throw new Exception( sprintf( __( 'Missing parameter %s', 'woocommerce' ), $param ) ); 
  135.  
  136. if ( ! in_array( $_REQUEST['scope'], array( 'read', 'write', 'read_write' ) ) ) { 
  137. /** translators: %s: scope */ 
  138. throw new Exception( sprintf( __( 'Invalid scope %s', 'woocommerce' ), wc_clean( $_REQUEST['scope'] ) ) ); 
  139.  
  140. foreach ( array( 'return_url', 'callback_url' ) as $param ) { 
  141. $param = $this->get_formatted_url( $_REQUEST[ $param ] ); 
  142.  
  143. if ( false === filter_var( $param, FILTER_VALIDATE_URL ) ) { 
  144. /** translators: %s: url */ 
  145. throw new Exception( sprintf( __( 'The %s is not a valid URL', 'woocommerce' ), $param ) ); 
  146.  
  147. $callback_url = $this->get_formatted_url( $_REQUEST['callback_url'] ); 
  148.  
  149. if ( 0 !== stripos( $callback_url, 'https://' ) ) { 
  150. throw new Exception( __( 'The callback_url need to be over SSL', 'woocommerce' ) ); 
  151.  
  152. /** 
  153. * Create keys. 
  154. * @since 2.4.0 
  155. * @param string $app_name 
  156. * @param string $app_user_id 
  157. * @param string $scope 
  158. * @return array 
  159. */ 
  160. protected function create_keys( $app_name, $app_user_id, $scope ) { 
  161. global $wpdb; 
  162.  
  163. /** translators: 1: app name 2: scope 3: date 4: time */ 
  164. $description = sprintf( 
  165. __( '%1$s - API %2$s (created on %3$s at %4$s).', 'woocommerce' ),  
  166. wc_clean( $app_name ),  
  167. $this->get_i18n_scope( $scope ),  
  168. date_i18n( wc_date_format() ),  
  169. date_i18n( wc_time_format() ) 
  170. ); 
  171. $user = wp_get_current_user(); 
  172.  
  173. // Created API keys. 
  174. $permissions = ( in_array( $scope, array( 'read', 'write', 'read_write' ) ) ) ? sanitize_text_field( $scope ) : 'read'; 
  175. $consumer_key = 'ck_' . wc_rand_hash(); 
  176. $consumer_secret = 'cs_' . wc_rand_hash(); 
  177.  
  178. $wpdb->insert( 
  179. $wpdb->prefix . 'woocommerce_api_keys',  
  180. array( 
  181. 'user_id' => $user->ID,  
  182. 'description' => $description,  
  183. 'permissions' => $permissions,  
  184. 'consumer_key' => wc_api_hash( $consumer_key ),  
  185. 'consumer_secret' => $consumer_secret,  
  186. 'truncated_key' => substr( $consumer_key, -7 ),  
  187. ),  
  188. array( 
  189. '%d',  
  190. '%s',  
  191. '%s',  
  192. '%s',  
  193. '%s',  
  194. '%s',  
  195. ); 
  196.  
  197. return array( 
  198. 'key_id' => $wpdb->insert_id,  
  199. 'user_id' => $app_user_id,  
  200. 'consumer_key' => $consumer_key,  
  201. 'consumer_secret' => $consumer_secret,  
  202. 'key_permissions' => $permissions,  
  203. ); 
  204.  
  205. /** 
  206. * Post consumer data. 
  207. * @since 2.4.0 
  208. * @param array $consumer_data 
  209. * @param string $url 
  210. * @return bool 
  211. * @throws Exception 
  212. */ 
  213. protected function post_consumer_data( $consumer_data, $url ) { 
  214. $params = array( 
  215. 'body' => json_encode( $consumer_data ),  
  216. 'timeout' => 60,  
  217. 'headers' => array( 
  218. 'Content-Type' => 'application/json;charset=' . get_bloginfo( 'charset' ),  
  219. ),  
  220. ); 
  221.  
  222. $response = wp_safe_remote_post( esc_url_raw( $url ), $params ); 
  223.  
  224. if ( is_wp_error( $response ) ) { 
  225. throw new Exception( $response->get_error_message() ); 
  226. } elseif ( 200 != $response['response']['code'] ) { 
  227. throw new Exception( __( 'An error occurred in the request and at the time were unable to send the consumer data', 'woocommerce' ) ); 
  228.  
  229. return true; 
  230.  
  231. /** 
  232. * Handle auth requests. 
  233. * @since 2.4.0 
  234. */ 
  235. public function handle_auth_requests() { 
  236. global $wp; 
  237.  
  238. if ( ! empty( $_GET['wc-auth-version'] ) ) { 
  239. $wp->query_vars['wc-auth-version'] = $_GET['wc-auth-version']; 
  240.  
  241. if ( ! empty( $_GET['wc-auth-route'] ) ) { 
  242. $wp->query_vars['wc-auth-route'] = $_GET['wc-auth-route']; 
  243.  
  244. // wc-auth endpoint requests 
  245. if ( ! empty( $wp->query_vars['wc-auth-version'] ) && ! empty( $wp->query_vars['wc-auth-route'] ) ) { 
  246. $this->auth_endpoint( $wp->query_vars['wc-auth-route'] ); 
  247.  
  248. /** 
  249. * Auth endpoint. 
  250. * @since 2.4.0 
  251. * @param string $route 
  252. */ 
  253. protected function auth_endpoint( $route ) { 
  254. ob_start(); 
  255.  
  256. $consumer_data = array(); 
  257.  
  258. try { 
  259. if ( 'yes' !== get_option( 'woocommerce_api_enabled' ) ) { 
  260. throw new Exception( __( 'API disabled!', 'woocommerce' ) ); 
  261.  
  262. $route = strtolower( wc_clean( $route ) ); 
  263. $this->make_validation(); 
  264.  
  265. // Login endpoint 
  266. if ( 'login' == $route && ! is_user_logged_in() ) { 
  267. wc_get_template( 'auth/form-login.php', array( 
  268. 'app_name' => $_REQUEST['app_name'],  
  269. 'return_url' => add_query_arg( array( 'success' => 0, 'user_id' => wc_clean( $_REQUEST['user_id'] ) ), $this->get_formatted_url( $_REQUEST['return_url'] ) ),  
  270. 'redirect_url' => $this->build_url( $_REQUEST, 'authorize' ),  
  271. ) ); 
  272.  
  273. exit; 
  274.  
  275. // Redirect with user is logged in 
  276. } elseif ( 'login' == $route && is_user_logged_in() ) { 
  277. wp_redirect( esc_url_raw( $this->build_url( $_REQUEST, 'authorize' ) ) ); 
  278. exit; 
  279.  
  280. // Redirect with user is not logged in and trying to access the authorize endpoint 
  281. } elseif ( 'authorize' == $route && ! is_user_logged_in() ) { 
  282. wp_redirect( esc_url_raw( $this->build_url( $_REQUEST, 'login' ) ) ); 
  283. exit; 
  284.  
  285. // Authorize endpoint 
  286. } elseif ( 'authorize' == $route && current_user_can( 'manage_woocommerce' ) ) { 
  287. wc_get_template( 'auth/form-grant-access.php', array( 
  288. 'app_name' => $_REQUEST['app_name'],  
  289. 'return_url' => add_query_arg( array( 'success' => 0, 'user_id' => wc_clean( $_REQUEST['user_id'] ) ), $this->get_formatted_url( $_REQUEST['return_url'] ) ),  
  290. 'scope' => $this->get_i18n_scope( wc_clean( $_REQUEST['scope'] ) ),  
  291. 'permissions' => $this->get_permissions_in_scope( wc_clean( $_REQUEST['scope'] ) ),  
  292. 'granted_url' => wp_nonce_url( $this->build_url( $_REQUEST, 'access_granted' ), 'wc_auth_grant_access', 'wc_auth_nonce' ),  
  293. 'logout_url' => wp_logout_url( $this->build_url( $_REQUEST, 'login' ) ),  
  294. 'user' => wp_get_current_user(),  
  295. ) ); 
  296. exit; 
  297.  
  298. // Granted access endpoint 
  299. } elseif ( 'access_granted' == $route && current_user_can( 'manage_woocommerce' ) ) { 
  300. if ( ! isset( $_GET['wc_auth_nonce'] ) || ! wp_verify_nonce( $_GET['wc_auth_nonce'], 'wc_auth_grant_access' ) ) { 
  301. throw new Exception( __( 'Invalid nonce verification', 'woocommerce' ) ); 
  302.  
  303. $consumer_data = $this->create_keys( $_REQUEST['app_name'], $_REQUEST['user_id'], $_REQUEST['scope'] ); 
  304. $response = $this->post_consumer_data( $consumer_data, $this->get_formatted_url( $_REQUEST['callback_url'] ) ); 
  305.  
  306. if ( $response ) { 
  307. wp_redirect( esc_url_raw( add_query_arg( array( 'success' => 1, 'user_id' => wc_clean( $_REQUEST['user_id'] ) ), $this->get_formatted_url( $_REQUEST['return_url'] ) ) ) ); 
  308. exit; 
  309. } else { 
  310. throw new Exception( __( 'You do not have permissions to access this page!', 'woocommerce' ) ); 
  311. } catch ( Exception $e ) { 
  312. $this->maybe_delete_key( $consumer_data ); 
  313.  
  314. /** translators: %s: error messase */ 
  315. wp_die( sprintf( __( 'Error: %s.', 'woocommerce' ), $e->getMessage() ), __( 'Access denied', 'woocommerce' ), array( 'response' => 401 ) ); 
  316.  
  317. /** 
  318. * Maybe delete key. 
  319. * @since 2.4.0 
  320. * @param array $key 
  321. */ 
  322. private function maybe_delete_key( $key ) { 
  323. global $wpdb; 
  324.  
  325. if ( isset( $key['key_id'] ) ) { 
  326. $wpdb->delete( $wpdb->prefix . 'woocommerce_api_keys', array( 'key_id' => $key['key_id'] ), array( '%d' ) );