generateToken succeeds, but token not being accepted when in use in Esri-Leaflet

3012
3
Jump to solution
06-24-2019 03:46 PM
SimonWebster
Occasional Contributor

This question probably doesn't belong to one single developer group, but the Esri-Leaflet implementation is the ultimate end goal, and probably has developers most familiar with the stack - so here goes:

 

I have a standalone ArcGIS server situation, and am wanting to access secure feature services. My site runs on PHP 7.3, and has access to cURL. Ideally I'd like to collect the token using credentials stored in PHP, using cURL, and then deliver that token to Esri-Leaflet when I do an addtomap, thus keeping the credentials secure. 

 

The error that I'm facing at the response to the request is the rather generic:

{"error":{"code":498,"message":"Invalid Token","details":[]}}

 

Temporarily there is a url here:

http://52.65.144.150/esri-js-demo-page/

 

So, what have I done so far? Lets use a secured esri demo service to illustrate, noting that I've left in commented out sections where I've either:

  • Tried to set the referer manually so that cURL is using the same referer as the referer being passed out from the site (hopefully). This included the page calling the map, as seen by lookign at the requests referer being passing in chrome, and also by ensuring that the referer being dumped out by php matched that which chrome was sending. Overall I don't want to use this approach as I'd like to have multiple pages use this bit of php.
  • Tried to use the visitors IP addresses instead. This generated a token but resulted in the same invalid token issue. My organisation can force requests out from different endpoints too, so it's a possible point of instability.
  • Tried to set the referer using other PHP methods such as $_SERVER['HTTP_REFERER'];
  • You can also see my regex for parsing the tokens - this just strips the rest of the response so that all I'm left with is the token string. {".,}. I'm thus not passing an expiry back to the server during the layer request - I cannot see in any documentation that it's required.

 

 

  <?php	

	//Check to see if the request came from the proper application

		//The url to the token endpoint of the sever containing the secure service
		$url = "https://sampleserver6.arcgisonline.com/arcgis/tokens/";
		
	    //What is the actual referer - dump it into the page for debugging purposes
		echo $referer = $_SERVER['HTTP_REFERER'];
	    
	    //The fields included in the request
		$fields = array(
			'request' => 'getToken',
			'username' => 'user1',
			'password' => 'user1',
			'expiration' => 120,
			'clientid' => 'ref.' . $_SERVER['HTTP_REFERER'],
			//'clientid' => 'ref.' . $_SERVER['HTTP_REFERER'],
			//'clientid' => 'ip.' . $_SERVER['HTTP_CLIENT_IP'],
			//'clientid' => 'ip',
			//'ip' => $_SERVER['REMOTE_ADDR'],
			'f' => 'json'
		);
		$fields_string = "";
		foreach($fields as $key=>$value) {
			$fields_string .= $key.'='.$value.'&'; 
		}
		//Instantiate Curl
		$ch = curl_init($url);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ch,CURLOPT_POST,count($fields));
		curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	    //curl_setopt($ch, CURLOPT_REFERER, $referer);
		$result = curl_exec($ch);
		
		//If Curl was not instantiated successfully
		if (!curl_exec($ch)) {
			echo 'An error has occurred: ' . curl_error($ch);
		}
		//Otherwise return the result
		else {
			preg_match('/:.*?,/', $result, $token);
			$token = str_replace(':', "", $token);
			$token = str_replace('.",', '"', $token);
			//print_r($token);
			echo '<script type="text/javascript">';
			echo 'var val = '.$token[0];
			echo '</script>';
			//echo $result;
		}
		//Close Curl
		curl_close($ch);
  }
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

 

Now, reload the page that is doing the calling, see that the token is brought in via javascript successfully:

<script type="text/javascript">var val = "9VOz2HXe_mfOXJSVSFr4cgNL-5k6fIMfqTOYytKEefs"</script>‍

 

Request the service layer:

var pedestrianDistricts2 = L.esri.featureLayer({
      url: 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/Wildfire_secure_ac/FeatureServer/2', 
token: val
    }).addTo(map);‍‍‍‍

 

The request from the network tab of Chrome looks like:

https://sampleserver6.arcgisonline.com/arcgis/rest/services/Wildfire_secure/FeatureServer/?token=9VOz2HXe_mfOXJSVSFr4cgNL-5k6fIMfqTOYytKEefs&f=json‍‍

The response looks like:

{"error":{"code":498,"message":"Invalid Token","details":[]}}‍

 

 

 

As an aside I tried this morning to hard code the credentials into a html page as per one of the examples - I was able to get around this error. Since this will be a production system I cannot take this approach. Then I had a different issue altogether, but that's another matter. 

 

I cannot help but think that the referer is causing me grief, but I'm just not sure how to get around the issue.

 

Any bright ideas are appreciated!

0 Kudos
1 Solution

Accepted Solutions
SimonWebster
Occasional Contributor

Fixed by treating the json object as json properly. 

  <?php	
        //Establish our requesting url
        global $wp;
        $RequestingPage = home_url( $wp->request );
	    //Check to see if the request came from the proper application

		//The url to the token endpoint of the server containing the secure service
	    $url = "https://someplace.com/arcgis/tokens/";
	    
	    //The fields included in the request
		$fields = array(
			'request' => 'getToken',
			'username' => 'username1',
			'password' => 'password1',
			'expiration' => 120,
			'clientid' => 'ref.' . $RequestingPage . '/',
			'f' => 'json'
		);
		$fields_string = "";
		foreach($fields as $key=>$value) {
			$fields_string .= $key.'='.$value.'&'; 
		}
		//Instantiate Curl
		$ch = curl_init($url);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ch,CURLOPT_POST,count($fields));
		curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	    curl_setopt($ch, CURLOPT_REFERER, $RequestingPage);
	    
		$result = curl_exec($ch);
		
		//If Curl was not instantiated successfully
		if (!curl_exec($ch)) {
			echo 'An error has occurred: ' . curl_error($ch);
		}
		//Otherwise return the result
		else {
			//Treat the response as JSON, and split by attribute.
			$someObject = json_decode($result);
			echo '<script type="text/javascript">';
			echo "\r\n";
			echo 'var val = "'.$someObject->token .'"';
			echo "\r\n";
			echo '</script>';
			echo "\r\n";
		}
		//Close Curl
	    curl_close($ch);
  }
}

add_action( 'wp_head', 'ple_hook_javascript' );

View solution in original post

3 Replies
SimonWebster
Occasional Contributor

Fixed by treating the json object as json properly. 

  <?php	
        //Establish our requesting url
        global $wp;
        $RequestingPage = home_url( $wp->request );
	    //Check to see if the request came from the proper application

		//The url to the token endpoint of the server containing the secure service
	    $url = "https://someplace.com/arcgis/tokens/";
	    
	    //The fields included in the request
		$fields = array(
			'request' => 'getToken',
			'username' => 'username1',
			'password' => 'password1',
			'expiration' => 120,
			'clientid' => 'ref.' . $RequestingPage . '/',
			'f' => 'json'
		);
		$fields_string = "";
		foreach($fields as $key=>$value) {
			$fields_string .= $key.'='.$value.'&'; 
		}
		//Instantiate Curl
		$ch = curl_init($url);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ch,CURLOPT_POST,count($fields));
		curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	    curl_setopt($ch, CURLOPT_REFERER, $RequestingPage);
	    
		$result = curl_exec($ch);
		
		//If Curl was not instantiated successfully
		if (!curl_exec($ch)) {
			echo 'An error has occurred: ' . curl_error($ch);
		}
		//Otherwise return the result
		else {
			//Treat the response as JSON, and split by attribute.
			$someObject = json_decode($result);
			echo '<script type="text/javascript">';
			echo "\r\n";
			echo 'var val = "'.$someObject->token .'"';
			echo "\r\n";
			echo '</script>';
			echo "\r\n";
		}
		//Close Curl
	    curl_close($ch);
  }
}

add_action( 'wp_head', 'ple_hook_javascript' );
petiibhuzah
New Contributor

This works out for me. Thanks Simon

0 Kudos
SimonWebster
Occasional Contributor

Glad it helped!

I guess what I didn't mention in my initial series of posts was that the script returns the variable "var" which contains the token. 

When collecting a layer with ESRI-Leaflet you can supply this token as part of the URL. Just for anyone else who happens upon this in the future. 

0 Kudos