|
10 | 10 |
|
11 | 11 | namespace WCPOS\WooCommercePOS\API; |
12 | 12 |
|
| 13 | +use WCPOS\WooCommercePOS\Services\Auth as AuthService; |
13 | 14 | use const WCPOS\WooCommercePOS\SHORT_NAME; |
14 | 15 | use WP_REST_Controller; |
15 | 16 | use WP_REST_Request; |
@@ -48,6 +49,24 @@ public function register_routes(): void { |
48 | 49 | 'permission_callback' => '__return_true', // Public endpoint - no authentication required |
49 | 50 | ) |
50 | 51 | ); |
| 52 | + |
| 53 | + // Refresh access token using refresh token |
| 54 | + register_rest_route( |
| 55 | + $this->namespace, |
| 56 | + '/' . $this->rest_base . '/refresh', |
| 57 | + array( |
| 58 | + 'methods' => WP_REST_Server::CREATABLE, |
| 59 | + 'callback' => array( $this, 'refresh_token' ), |
| 60 | + 'permission_callback' => '__return_true', // Public endpoint - validates refresh token internally |
| 61 | + 'args' => array( |
| 62 | + 'refresh_token' => array( |
| 63 | + 'description' => __( 'The refresh token to use for generating a new access token.', 'woocommerce-pos' ), |
| 64 | + 'type' => 'string', |
| 65 | + 'required' => true, |
| 66 | + ), |
| 67 | + ), |
| 68 | + ) |
| 69 | + ); |
51 | 70 | } |
52 | 71 |
|
53 | 72 |
|
@@ -106,4 +125,62 @@ public function test_authorization( WP_REST_Request $request ): WP_REST_Response |
106 | 125 |
|
107 | 126 | return rest_ensure_response( $response_data ); |
108 | 127 | } |
| 128 | + |
| 129 | + /** |
| 130 | + * Refresh access token using a valid refresh token. |
| 131 | + * |
| 132 | + * This endpoint allows clients to obtain a new access token using a valid refresh token. |
| 133 | + * Compatible with the axios-auth-refresh library and follows OAuth 2.0 refresh token flow. |
| 134 | + * |
| 135 | + * @param WP_REST_Request $request The REST request object. |
| 136 | + * |
| 137 | + * @return WP_REST_Response |
| 138 | + */ |
| 139 | + public function refresh_token( WP_REST_Request $request ): WP_REST_Response { |
| 140 | + $refresh_token = $request->get_param( 'refresh_token' ); |
| 141 | + |
| 142 | + if ( empty( $refresh_token ) ) { |
| 143 | + return rest_ensure_response( array( |
| 144 | + 'error' => 'invalid_request', |
| 145 | + 'error_description' => 'Missing refresh_token parameter', |
| 146 | + ), 400 ); |
| 147 | + } |
| 148 | + |
| 149 | + $auth_service = AuthService::instance(); |
| 150 | + $result = $auth_service->refresh_access_token( $refresh_token ); |
| 151 | + |
| 152 | + if ( is_wp_error( $result ) ) { |
| 153 | + $error_code = $result->get_error_code(); |
| 154 | + $error_msg = $result->get_error_message(); |
| 155 | + $status = $result->get_error_data()['status'] ?? 400; |
| 156 | + |
| 157 | + // Map error codes to OAuth 2.0 standard error responses |
| 158 | + $oauth_error = 'invalid_grant'; // Default OAuth error for refresh token issues |
| 159 | + |
| 160 | + if ( false !== strpos( $error_code, 'invalid_token' ) || false !== strpos( $error_code, 'revoked' ) ) { |
| 161 | + $oauth_error = 'invalid_grant'; |
| 162 | + } elseif ( false !== strpos( $error_code, 'user_not_found' ) ) { |
| 163 | + $oauth_error = 'invalid_grant'; |
| 164 | + } |
| 165 | + |
| 166 | + return rest_ensure_response( array( |
| 167 | + 'error' => $oauth_error, |
| 168 | + 'error_description' => $error_msg, |
| 169 | + ), $status ); |
| 170 | + } |
| 171 | + |
| 172 | + // Calculate expires_in for axios-auth-refresh compatibility |
| 173 | + $current_time = time(); |
| 174 | + $expires_in = max( 0, $result['expires_at'] - $current_time ); |
| 175 | + |
| 176 | + // Return response in format compatible with axios-auth-refresh |
| 177 | + $response_data = array( |
| 178 | + 'access_token' => $result['access_token'], |
| 179 | + 'token_type' => $result['token_type'], |
| 180 | + 'expires_in' => $expires_in, |
| 181 | + 'expires_at' => $result['expires_at'], |
| 182 | + ); |
| 183 | + |
| 184 | + return rest_ensure_response( $response_data ); |
| 185 | + } |
109 | 186 | } |
0 commit comments