PluginProbe
HubSpot All-In-One Marketing – Forms, Popups, Live Chat / 11.3.75
HubSpot All-In-One Marketing – Forms, Popups, Live Chat v11.3.75
11.3.75 11.3.73 11.3.71 11.3.70 11.3.69 11.3.64 11.3.65 11.3.62 11.3.61 11.3.56 11.3.58 11.0.31 11.0.52 11.0.54 11.0.56 11.0.58 11.0.7 11.1.10 11.1.11 11.1.13 11.1.14 11.1.15 11.1.2 11.1.20 11.1.21 All 73 releases
leadin / public / modules / api / class-access-token-api-controller.php

class-access-token-api-controller.php in HubSpot All-In-One Marketing – Forms, Popups, Live Chat 11.3.75, at public/modules/api/class-access-token-api-controller.php

126 lines 3.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Leadin\api;
4
5 use Leadin\api\Base_Api_Controller;
6 use Leadin\auth\OAuth;
7 use Leadin\auth\OAuthCrypto;
8 use Leadin\data\Filters;
9 use Leadin\data\Portal_Options;
10
11 class Access_Token_Api_Controller extends Base_Api_Controller {
12
13 const CACHE_KEY = 'leadin_access_token';
14
15 public function __construct() {
16 // Uses register_leadin_route → edit_posts capability
17 // Safe because only short-lived access token is returned,
18 // NOT the long-lived refresh token
19 self::register_leadin_route(
20 '/access-token',
21 \WP_REST_Server::READABLE,
22 array( $this, 'get_access_token' )
23 );
24 }
25
26 public function get_access_token() {
27 $cached = get_transient( self::CACHE_KEY );
28 if ( ! empty( $cached ) ) {
29 $cached_data = json_decode( $cached, true );
30 if ( ! empty( $cached_data['accessToken'] )
31 && ! empty( $cached_data['expiresAt'] )
32 && $cached_data['expiresAt'] > ( time() + 300 ) ) {
33 return new \WP_REST_Response(
34 array(
35 'accessToken' => $cached_data['accessToken'],
36 'expiresIn' => $cached_data['expiresAt'] - time(),
37 ),
38 200
39 );
40 }
41 }
42
43 $refresh_token = OAuth::get_refresh_token();
44
45 if ( false === $refresh_token ) {
46 return new \WP_REST_Response(
47 array( 'error' => 'decrypt_failed' ),
48 500
49 );
50 }
51
52 if ( empty( $refresh_token ) ) {
53 return new \WP_REST_Response(
54 array( 'error' => 'not_connected' ),
55 403
56 );
57 }
58
59 // Server-side exchange — refresh token never leaves PHP.
60 // The /wordpress/v2/oauth/refresh endpoint takes the token in the POST
61 // body so it does not appear in server access logs.
62 $api_url = Filters::apply_base_api_url_filters() . '/wordpress/v2/oauth/refresh';
63 $response = wp_remote_post(
64 $api_url,
65 array(
66 'headers' => array( 'Content-Type' => 'application/json' ),
67 'body' => json_encode( array( 'refreshToken' => $refresh_token ) ),
68 'timeout' => 15,
69 )
70 );
71
72 if ( is_wp_error( $response ) ) {
73 return new \WP_REST_Response(
74 array( 'error' => 'refresh_failed' ),
75 500
76 );
77 }
78
79 $response_code = wp_remote_retrieve_response_code( $response );
80 if ( 200 !== $response_code ) {
81 return new \WP_REST_Response(
82 array(
83 'error' => 'refresh_failed',
84 'code' => $response_code,
85 ),
86 500
87 );
88 }
89
90 $body = json_decode( wp_remote_retrieve_body( $response ), true );
91
92 if ( empty( $body ) || empty( $body['access_token'] ) ) {
93 return new \WP_REST_Response(
94 array( 'error' => 'invalid_response' ),
95 500
96 );
97 }
98
99 $expires_in = isset( $body['expires_in'] ) ? (int) $body['expires_in'] : 0;
100
101 if ( ! empty( $body['refresh_token'] ) ) {
102 $encrypted_new_token = OAuthCrypto::encrypt( $body['refresh_token'] );
103 Portal_Options::set_refresh_token( $encrypted_new_token );
104 }
105
106 if ( $expires_in > 300 ) {
107 $cache_data = json_encode(
108 array(
109 'accessToken' => $body['access_token'],
110 'expiresAt' => time() + $expires_in,
111 )
112 );
113 set_transient( self::CACHE_KEY, $cache_data, $expires_in - 300 );
114 }
115
116 // Return ONLY the access token — refresh token stays server-side
117 return new \WP_REST_Response(
118 array(
119 'accessToken' => $body['access_token'],
120 'expiresIn' => $expires_in,
121 ),
122 200
123 );
124 }
125 }
126