get_api_namespace(), '/auth', [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_auth_payload' ], 'permission_callback' => [ $this, 'validate_permission' ], ] ); } /** * GET /surecookie/v1/auth - returns the form payload the frontend POSTs * to the billing portal (issue #466). Replaces the URL-string response. * * @since 0.0.1-beta.3 * @param WP_REST_Request> $request Request object. * @return void */ public function get_auth_payload( $request ): void { $controller = Controller::get_instance(); if ( $controller->get_auth_status() ) { SendJson::success( [ 'is_authenticated' => true, 'email' => $controller->get_auth_email(), 'message' => __( 'Authentication is already completed.', 'surecookie' ), ] ); return; } $payload = $controller->get_auth_payload(); if ( is_wp_error( $payload ) ) { SendJson::error( [ 'message' => $payload->get_error_message() ] ); return; } SendJson::success( $payload ); } /** * Handle the inbound POST form from the billing portal popup (issue #466). * * Receives `{action, flow_id, access_key}` as form fields, runs save_auth, * and redirects the popup to /wp-admin/admin.php?page=surecookie with a * benign `?auth=success|fail` query - the ciphertext never lands in the * post-OAuth URL. * * @since 0.0.1-beta.3 */ public function handle_inbound_callback(): void { // Cast to string up front - array-typed $_POST values would trigger // sanitize_text_field warnings. flow_id is then validated as UUID v4 // inside save_auth(); access_key is base64-URL-safe ciphertext that // sanitization would corrupt, validated by decrypt. // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Replay protection is via one-time transient in save_auth. flow_id validated as UUID v4 downstream. $flow_raw = isset( $_POST['flow_id'] ) && is_string( $_POST['flow_id'] ) ? wp_unslash( $_POST['flow_id'] ) : ''; $flow_id = sanitize_text_field( (string) $flow_raw ); // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- ciphertext is base64 URL-safe; sanitization would corrupt it. Validated by save_auth. $access_key = isset( $_POST['access_key'] ) && is_string( $_POST['access_key'] ) ? (string) wp_unslash( $_POST['access_key'] ) : ''; $redirect_base = admin_url( 'admin.php?page=surecookie' ); if ( $access_key === '' || $flow_id === '' ) { wp_safe_redirect( add_query_arg( 'auth', 'fail', $redirect_base ) ); exit; } $result = Controller::get_instance()->save_auth( $access_key, $flow_id ); $status = is_wp_error( $result ) ? 'fail' : 'success'; wp_safe_redirect( add_query_arg( 'auth', $status, $redirect_base ) ); exit; } }