Amplify-js: How to use Analytics in conjunction with Cognito User Pool

Created on 13 Mar 2019  路  9Comments  路  Source: aws-amplify/amplify-js

I am trying to implement Analytics into an application that is already using a Cognito User Pool for identity and authentication but none of the tutorials that I can find cover how to go about this. Just to check that I have understood how Analytics is supposed to work without a user pool; app gets unauth identity from identity pool, IAM role gives that identity rights to write data to Pinpoint, app sends analytics data to Pinpoint which then shows up in the Pinpoint console, is that correct?

If that is correct then how does throwing a User Pool authenticated user into the mix change things? Do I have to register the user pool as an authentication provider and then update the IAM policy for auth'd users as well? If that is the case how do I preserve the Analytics session after login? How do I even get analytics before someone logs in?

Theory out of the way, I can't get any of this working, I tried setting up a Pinpoint project by following the tutorial and modifying the unauth IAM policy as instructed. When I add the required details to my Amplify configure JSON as detailed here: https://aws-amplify.github.io/docs/js/analytics#manual-setup and on the Pinpoint web analytics setup page I just get a flood of errors in the Chrome console.

Specifically these two repeating:

OPTIONS https://mobileanalytics.eu-west-1.amazonaws.com/2014-06-05/events net::ERR_NAME_NOT_RESOLVED

localhost/:1 Access to XMLHttpRequest at 'https://pinpoint.eu-west-1.amazonaws.com/v1/apps/8f2b9e28751d45fc98b0477ef75f4657/endpoints/91a8f930-4593-11e9-82e3-21d191fdb32c' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

The unauth identity is showing up in the identity pool but nothing is appearing in the Pinpoint console. I found this issue https://github.com/aws-amplify/amplify-js/issues/448 but it doesn't really give any insight into how to resolve the problem and I would rather understand how the systems work than rely on the automated setup to fix it for me.

Analytics pending-close-response-required question

Most helpful comment

I'm facing kinda the same issue. There are no clear instructions any where. I'm currently using Analytics.record and pass the userId as an attribute there (which is possibly wrong, since there is a dedicated section for users in the Pinpoint console). But there should be a way to have Endpoints and their events automatically associated with their users, shouldn't it?

For reference, currently I'm just doing:

// index.js in React
import Analytics from '@aws-amplify/analytics';
import Amplify from 'aws-amplify';
import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';
import config from './aws-exports';

Amplify.configure(config);
Analytics.autoTrack('pageView', {
  enable: true,
  type: 'SPA',
});

ReactDOM.render(<App />, document.getElementById('root'));

And <App /> is wrapped in the withAuthenticator HOC.

Log in's and page changes are tracked correctly, except that there is no userId associated with them (only if I use it manually with Analytics.record there is an id).

How can you have users data associated with events when using AWS Amplify? I also tried calling Analytics.updateEndpoint with a userId:

// in App.js
async function trackUserId() {
  try {
    const user = await Auth.currentAuthenticatedUser();
    const userId = user.attributes.sub;
    await Analytics.updateEndpoint({ userId });
  } catch (error) {
    console.log(error);
  }
}

function App() {
  useEffect(() => {
    trackUserId();
  }, []);
  // ...

which also didn't change anything.

EDIT: I managed to solve it like this:

const mapObj = f => obj =>
  Object.keys(obj).reduce((acc, key) => ({ ...acc, [key]: f(obj[key]) }), {});
const toArrayOfStrings = value => [`${value}`];
const mapToArrayOfStrings = mapObj(toArrayOfStrings);

async function trackUserId() {
  try {
    const { attributes } = await Auth.currentAuthenticatedUser();
    const userAttributes = mapToArrayOfStrings(attributes);
    Analytics.updateEndpoint({
      address: attributes.email,
      channelType: 'EMAIL',
      userId: attributes.sub,
      userAttributes,
    });
  } catch (error) {
    console.log(error);
  }
}

function App() {
  useEffect(() => {
    trackUserId();
  }, []);

All 9 comments

Hi @Exitialis1,

Can I ask a few clarifying questions:

  • How are you using the Analytics category when receiving these errors
  • Can you provide any sample code
  • Are you using Amplify's Auth category?
  • Could you include the version of aws-amplify you are using?

@Exitialis1 - When you check you run your application, do you see anything in the network tab? You should see requests being sent to pinpoint. If so, what are the responses?

@jordanranz

  • Nothing special, just configuring it and then calling the below from the constructor of one of the components a user sees first.
    Analytics.record("Home page loaded");
  • This is how I am configuring Amplify in the App component:
Amplify.configure({
  Auth: {
      identityPoolId: '',
      region: '',
      userPoolId: '',
      userPoolWebClientId: '',
  },
  Analytics: {
    AWSPinpoint: {
      region: '',
      appId: ""
    }
  }
});
  • Yes, see above, a Cognito user pool is my primary authentication for the application
  • According to "npm list | grep aws-amplify" I am using version 1.1.22

@haverchuck there are some 200 status codes but the vast majority are 403

Just to check that I have understood how Analytics is supposed to work without a user pool; app gets unauth identity from identity pool, IAM role gives that identity rights to write data to Pinpoint, app sends analytics data to Pinpoint which then shows up in the Pinpoint console, is that correct?

Yes. When using identity pool, you need to pass IAM policy to auth_role and unauth_role, and the IAM should contain Pinpoint related policy that would allow you to send data to the service.

If that is correct then how does throwing a User Pool authenticated user into the mix change things?

As long as your user pool is a federated provider of your identity pool, the Cognito user will get the "auth_role" AWS credentials when logged in. When using amplify, the process happens automatically.

Do I have to register the user pool as an authentication provider and then update the IAM policy for auth'd users as well?

Yes, if you mean register it in the Cognito Identity Pool, and then update the IAM policy for the auth_role

If that is the case how do I preserve the Analytics session after login?

We need more info about the Analytics session. The auth session will be preserved automatically in your local cache.

How do I even get analytics before someone logs in?

If no one logs in, then Amplify will use the guest credentials which has the un_auth role to send the events. Of course you should have corresponding policy for that role.

I have finally had a chance to look at this again and I have kind of got it working. I did some experimenting with the unath IAM role and discovered that it doesn't work when I copy paste the provided example code from the Pinpoint settings page but did work if I just allowed any resource. Then I tried narrowing it down based on each part of the arn and both region and account number can be specified without issue, but when I specify the App ID it stops workings, with a wildcard * it works, with the triple checked, correct Pinpoint App ID it stops working. Do I need to specify the App ID if I only have one Pinpoint project?

@powerful23 thank you for answering my questions, it helped a lot with trying to understand why it was not working.

@Exitialis1 seems like it's releted to #2908
I am going to close this issue now but if you want to reopen it, please let me know.

I'm facing kinda the same issue. There are no clear instructions any where. I'm currently using Analytics.record and pass the userId as an attribute there (which is possibly wrong, since there is a dedicated section for users in the Pinpoint console). But there should be a way to have Endpoints and their events automatically associated with their users, shouldn't it?

For reference, currently I'm just doing:

// index.js in React
import Analytics from '@aws-amplify/analytics';
import Amplify from 'aws-amplify';
import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';
import config from './aws-exports';

Amplify.configure(config);
Analytics.autoTrack('pageView', {
  enable: true,
  type: 'SPA',
});

ReactDOM.render(<App />, document.getElementById('root'));

And <App /> is wrapped in the withAuthenticator HOC.

Log in's and page changes are tracked correctly, except that there is no userId associated with them (only if I use it manually with Analytics.record there is an id).

How can you have users data associated with events when using AWS Amplify? I also tried calling Analytics.updateEndpoint with a userId:

// in App.js
async function trackUserId() {
  try {
    const user = await Auth.currentAuthenticatedUser();
    const userId = user.attributes.sub;
    await Analytics.updateEndpoint({ userId });
  } catch (error) {
    console.log(error);
  }
}

function App() {
  useEffect(() => {
    trackUserId();
  }, []);
  // ...

which also didn't change anything.

EDIT: I managed to solve it like this:

const mapObj = f => obj =>
  Object.keys(obj).reduce((acc, key) => ({ ...acc, [key]: f(obj[key]) }), {});
const toArrayOfStrings = value => [`${value}`];
const mapToArrayOfStrings = mapObj(toArrayOfStrings);

async function trackUserId() {
  try {
    const { attributes } = await Auth.currentAuthenticatedUser();
    const userAttributes = mapToArrayOfStrings(attributes);
    Analytics.updateEndpoint({
      address: attributes.email,
      channelType: 'EMAIL',
      userId: attributes.sub,
      userAttributes,
    });
  } catch (error) {
    console.log(error);
  }
}

function App() {
  useEffect(() => {
    trackUserId();
  }, []);

Thank you @janhesters your solution worked for me too. I had to add optOut: 'NONE', in the endpoint configuration in order for the endpoint to be 'eligible'.
Do you happen to know the proper way to update the endpoint when the user signs out?

@andriworld I would say you have to do nothing when the user logs out, but remove the endpoint when the user removes or deactivates his/her account, right?

Was this page helpful?
0 / 5 - 0 ratings