Unauthenticated access to Places

0

Hi,

I'm just getting started with Amazon Location Services. I've created my first map, and can access it fine as an unauthenticated user. However, I'm trying to now add geocoding functionality, but it's not clear to me how to make access to the 'places' API be unauthenticated. The documentation explicitly covers granting unauthenticated access to maps, but it is silent on this topic for places.

I have tried a simple POST to https://places.geo.eu-west-1.amazonaws.com/places/v0/indexes/<placesname>/search/text, but I get back an HTTP 403 response.

Can anyone help?

Thanks,

Sam

Edited by: samcrawford on Jan 14, 2021 1:39 PM

asked 3 years ago393 views
6 Answers
0
Accepted Answer

Hi Sam.

The mechanism for accessing Places as an unauthenticated user is the same as for Maps (https://docs.aws.amazon.com/location/latest/developerguide/how-to-access.html#authenticating-using-cognito). The differences are that you need to include geo:SearchPlaceIndex* in the list of Actions in the associated IAM policy and need to target the appropriate Resource ARN (which you can get from the Place Index detail screen in the console).

seth

Edited by: seth-at-aws on Jan 14, 2021 2:16 PM

AWS
answered 3 years ago
0

Hi Seth,

Thanks for the quick reply, much appreciated. I've followed your advice, and created a new identity pool that grants unauthenticated access using this schema:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SearchReadOnly",
"Effect": "Allow",
"Action": [
"geo:Search*"
],
"Resource": "arn:aws:geo:eu-west-1:ACCOUNTID:place-index/placedemo"
}
]
}

However, I'm still getting a 403 "Missing authentication token" response.

I'm trying to test it with:

curl -v --data @demo.json https://maps.geo.eu-west-1.amazonaws.com/places/v0/indexes/placedemo/search/text

Where demo.json contains:

{
"BiasPosition": [ -123.4567,45.6789 ],
"Text": "Starbucks"
}

Any further suggestions would be appreciated!

Thanks,

Sam

answered 3 years ago
0

Hi again.

The underlying issue is that you don't appear to be using the identity pool. You'll need to exchange the identity pool ID for a set of AWS credentials (which can then be used with the AWS CLI or elsewhere). Here's a Node.js script that will do that for you, producing shell output that can be copied and pasted to configure those credentials:

// cognito-credentials.js
const AWS = require("aws-sdk");
// region containing Cognito pool
AWS.config.region = "<region>";
async function main() {
const credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: "<identity pool id>"
});
await credentials.refreshPromise();
console.log("export AWS_ACCESS_KEY_ID=%s", credentials.accessKeyId);
console.log("export AWS_SECRET_ACCESS_KEY=%s", credentials.secretAccessKey);
console.log("export AWS_SESSION_TOKEN=%s", credentials.sessionToken);
}
main();

You won't be able to use curl (since it doesn't know how to sign requests), but https://github.com/okigan/awscurl and Postman can be configured to use the obtained credentials and sign requests accordingly.

seth

AWS
answered 3 years ago
0

Ah, my mistake, I see that the Mapbox GL sample code injected the AWS credentials into the HTTP request. I guess I'll need to find a similar way to do that with a geocoding library too.

Thanks for your help!

answered 3 years ago
0

For anyone reading this in the future, I eventually got this working, but man it was painful! I know Amazon Location is new, but the documentation here is quite poor.

Firstly, as Seth said, you need to create an unauthenticated identity pool that can access the 'places' resource. I created a single identity pool covering maps and places with this:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "MapsReadOnly",
"Effect": "Allow",
"Action": [
"geo:GetMapStyleDescriptor",
"geo:GetMapGlyphs",
"geo:GetMapSprites",
"geo:GetMapTile"
],
"Resource": "arn:aws:geo:eu-west-1:XXXXXXXXXX:map/mapdemo"
},
{
"Sid": "PlacesReadOnly",
"Effect": "Allow",
"Action": [
"geo:SearchPlaceIndex*"
],
"Resource": "arn:aws:geo:eu-west-1:XXXXXXXXX:place-index/placedemo"
}
]
}

To use the geocoder, you need to sign the HTTP requests, in a similar way to how it's done in the transformRequest function in https://docs.aws.amazon.com/location/latest/developerguide/tutorial-mapbox-gl-js.html

However, because the 'places' requests are POSTs, the signing mechanism is different (it needs to sign the URL, the method and the request body). So the updated transformRequest function looks like this:

  function transformRequest(url, resourceType, body) {  
    if (resourceType === "Style" && !url.includes("://")) {  
      // resolve to an AWS URL  
      url = `https://maps.geo.${AWS.config.region}.amazonaws.com/maps/v0/maps/${url}/style-descriptor`;  
    }  

    if (url.includes("amazonaws.com")) {  
      // There's two types of Amazon requests at play here. Tile requests are GET, but place requests  
      // (geocoding) are POSTs. When doing a POST request, you need to pass up the POST verb, _and_ the  
      // POST body to the AWS request signer.  
      return {  
        url: Signer.signUrl({  
          url: url,  
          method: url.includes("places.geo") ? "POST" : "GET",  
          body: url.includes("places.geo") ? body : undefined  
        },  
        {  
          access_key: credentials.accessKeyId,  
          secret_key: credentials.secretAccessKey,  
          session_token: credentials.sessionToken,  
        }),  
      };  
    }  

Once you've done all this, you still have to get a geocoding client library working on the frontend. I ended up modifying the Mapbox GL Geocoder. The Mapbox GL Geocoder already supports an 'externalGeocoder' option, which can be used to plugin to AWS's Places API.

It would be nice if Amazon included an example of how to use the Places API on the client side, like they do with the maps.

answered 3 years ago
0

Hi again.

One thing that may simplify life is to use the AWS SDK for JavaScript (https://aws.amazon.com/sdk-for-javascript/), although this may not work with the Mapbox GL Geocoder plugin. When you instantiate a new AWS.Location (https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Location.html), you can pass the Cognito credential provider in as credentials. The Maps example doesn't use it because the Mapbox GL JS is responsible for making the HTTP requests and the transformation function only handles signing.

With an unauthenticated identity pool associated with the role above, this should work (pardon the formatting):

const AWS = require("aws-sdk");

// region containing Cognito pool
AWS.config.region = "<region>";
const credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: "<identity pool id>"
});

const location = new AWS.Location({
credentials
});

const rsp = await location.searchPlaceIndexForText({
IndexName: "placedemo",
Text: "Starbucks",
BiasPosition: [-123.4567, 45.6789]
}).promise();

// rsp.Results contains search results

Edited by: seth-at-aws on Jan 15, 2021 5:32 PM

AWS
answered 3 years ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions