Identifying Users
Proper analytics and usage metering requires a mechanism to identify customers and ensure web and API usage is correctly attributed to the correct customers. This may be complicated because an API consumer (user) can consist of different states:
- An anonymous visitor viewing your doc pages.
- Signed into your portal and created an API key.
- Created a subscription that is tied to a company.
- Making authenticated calls against your API.
Overview of user tracking
The challenge of stitching together a customer journey
Most platform-focused companies have a variety of different products. For example, some products are API-based applications that are accessed by customers programmatically, whereas other products are web-based applications that customers log into with their browsers and use interactively. So it’s critical to stitch together a holistic customer journey so you can understand funnel metrics like initial sign up to first API call across both your API and web apps.
Yet, each of these products may have different ways to identify and authenticate users, creating multiple challenges. For example, an API may require adding a bearer token whereas a website may require a user to log in via an OAuth flow and store a session cookie.
In addition, not all users have identifying information. For example, you may have anonymous visitors who browsed your website before they signed up. These visitors don’t even have a user id generated yet. What if the same anonymous visitor browses your website from multiple devices? A naive user tracking implementation would double-count that person twice, reducing the integrity of your metrics.
How Moesif solves this
The recommended way to set up Moesif is with two integration points:
- A server integration to monitor API calls.
- A client integration to track users and their web activity.
By using both integrations together, a complete picture can be drawn showing user interactions with each API (the server integration) and interactions with the frontend UI (the client integration).
Note that the server integration can be used without the client integration since both are mutually exclusive implementations.
A complete implementation is shown in the below diagram.
Behind the scenes, the Moesif platform leverages a variety of tricks to accurately stitch together each customer’s journey. This mechanism includes user aliasing, anonymous ids, and merging users. Most of this is handled automatically by Moesif, but this article details what you need and how it works.
User Id
Moesif uses a user id (or company id) to associate API and web traffic to the same customer. If you are using both the server and client integrations, you should ensure your user id is consistent across both. You will also want to make sure that the user id is unique and unchanging.
If you use a user id that may change in the future, it will likely break your metrics. For instance, using an identifier such as email may be risky if you allow users to change their email in your application. A randomly generated UID that stays with the user regardless of profile changes is recommended.
To attach a user id to an event or action, server and client integrations should call the identifyUser
method. It is important that when calling this method, every call, whether it be from the client or server integrations, be made with the same id for the target user, so metrics are properly tracked.
The id used to track the user should be a permanent and robust identifier that never changes, like a database id. Using fields that can change, like email addresses or API keys, is not recommended. Using different ids for the same user will lead to issues with tracking the end-to-end journey (such as over-counting unique users) since Moesif uses one unique identifier per individual user.
Server integration
You will want to use a Moesif server integration to track API calls by user. We support many different platforms and the ability to create a custom integration for unsupported platforms.
Identifying users
The ability to identify users can only happen with authenticated API calls. For example, anonymous calls to the API will not be tracked by user ID.
You’ll need to ensure your server integration correctly identifies users so that API traffic is associated with each user.
User identification is accomplished by implementing the identifyUser
hook in the Moesif middleware. The identifyUser
hook should be supplied with your custom logic that returns the user id.
The identifyUser()
(or identifyCompany()
) function you supply has access to the request context. For example, if your API authentication middleware sets a variable req.user.id
, you can use that. You can also read from an HTTP header like X-User-Id
, extract a field from a JWT, or use any other field available in the request context.
Here is an example of a Node.js integration:
const moesifMiddleware = moesif({
applicationId: 'Your Moesif Application Id',
// Return the user id set by the auth middleware.
identifyUser: function (req, res) {
return req.user ? req.user.id : undefined;
},
});
app.use(moesifMiddleware);
You can see in the above snippet that identifyUser
is passed a function which has the request and response for the API call. In this example, we are grabbing the id from the requests user.id
field and if a user is not defined, no user id to the call/event in Moesif.
Depending on the framework, language, or gateway you are using, you should review the identify user section for your respective server integration.
Many API gateway plugins and SDKs have a default identifyUser
function out of the box based on the conventions for that framework.
For example, the Kong plugin reads the value set in x-consumer-custom-id
, the AWS API gateway reads the value principalId
and cognitoIdentityId
, and moesif-nodejs
uses the value set in req.user.id
You can do more advanced filtering and metrics by attaching a user id to an event in Moesif. Here’s what it looks like in the Live Event Log screen in Moesif.
Anonymous Ids
Unlike the client integration, the server integration can’t track anonymous user ids. This is because API traffic is usually authenticated traffic by definition. For example, if an ID is not attached to an event, the userID field in Moesif will simply be undefined.
Client integration
Moesif’s support for client integrations allows user interactions to be tracked beyond just the API calls tracked in the server integration. Each event (button click, register, login, etc.) can be easily added to your metrics in Moesif so you can see the full user journey.
Identifying users
To identify a user, you must have a unique ID to associate with them. Once you know the user id of a customer, you can call identifyUser()
to associate the current web session with your known permanent identifier. For best results, this should be done as soon as possible such as when a user logs into your app. Otherwise, tracking data can be lost if the customer clears their browser’s cookies and local storage before being identified with Moesif.
To make sure a seamless picture of the journey is recorded, be sure that the user ID supplied to the client integration matches the one your server integration is also using.
Here is an example of how to initialize the Moesif browser-js package in a JavaScript frontend:
<script src="//unpkg.com/moesif-browser-js@v1/moesif.min.js"></script>
<script type="text/javascript">
moesif.init({
applicationId: "Your Moesif Application Id",
});
// Identify the user with Moesif, such as when the user logs in
moesif.identifyUser("12345");
</script>
You can see above that we include the dependency (moesif.min.js), call init
with the Moesif Application ID for our app, and then call moesif.identifyUser(USER_ID)
. This would likely be done once the user logs into the app and a user id is retrieved. Once this is done, each action the user performs on the front end will be tracked and reported to Moesif.
Anonymous users
When a new visitor browses your website, which has a client integration like moesif-browser-js
installed, Moesif generates an anonymous user id and stores it on the user’s device.
By doing this, users who are yet to create an account or login can still be accurately tracked, and their journey reported to Moesif.
This anonymous id is persisted both into localStorage
and replicated to a cookie for redundancy so that if the user clears local storage or visits a different subdomain (like from docs.acmeinc.com
to acmeinc.com
), there is still a cookie to fall back to and the user can be accurately attributed.
Anonymous users make it possible to track unique users correctly and stitch together the user journey before and after signing into your app. Moesif uses two different ids to accomplish this:
Name | Description |
---|---|
User Id | The internal User Id assigned by Moesif. This may be either a generated Anonymous Id or can be your Identified User Id or Anonymous Id. |
Identified User Id | The Identified User Id is set by you by the identifyUser function. When no Identified User Id is set, the user is anonymous and has not been identified. |
You should call moesif.reset()
when a user logs out of your application to ensure a new anonymousId is generated. Otherwise, new activity may get associated with an old user session.
User merging
Moesif will automatically merge users if they are identified as the same person. This can happen if an existing user (previously tracked in Moesif) starts browsing your app on a new device. A new anonymous profile is created as soon as the user starts browsing anonymously from their new device. Once the user logs in (thus identifyUser() is called), Moesif will merge the newly created anonymous profile with the previously created user profile. You will see only one user in Moesif.
The below table describes different scenarios of how Moesif handles user creation and merging:
Example Scenario | How Moesif handles |
---|---|
No User Id identified | All events will be associated with an anonymous user profile. Identified User Id is null. User Id is set to a generated Anonymous Id. |
User Id is identified after anonymous events | Events are associated with an anonymous user profile. Initially, Identified User Id is null. Once the user is identified, both User Id and Identified User Id are set to the actual user id. |
Same User Id on multiple devices, with no anonymous events | Events will be initially associated to two different anonymous profiles. Once the user is identified, both profiles will be merged into a single user profile. User Id and Identified User Id are set to the actual user id. |
Multiple User Ids on the same device | Two profiles will be created, one for each user id. No merging will occur. Ensure moesif.reset() is called when a user logs out. Otherwise, events may get associated to an old user session. |
Multiple User Ids on the same device, with anonymous events | Ensure moesif.reset() is called when a user logs out. Otherwise, events may get associated to an old user session. |
Linking users to companies
In addition to tracking users, you can also track usage at the by Company. This is helpful if your business primarily sells to other businesses (B2B). Company tracking works by assigning a user to a company (i.e., a group of users), similar to tracking users by user ID.
Adding a Company identifier to a user can be done in one of three ways:
- Implement the
identifyCompany()
hook in your server integration. See example for Node.js This can be the easiest option if you already have company context available in your API. - Call the
identifyCompany()
function in your client integration, such as after they sign into your application. See this example for browser-js This is great if you already have company context in your front end such as when they sign into your application - Define a
company_id
when saving a user to Moesif directly. See updating user profiles This is great if you’re already syncing users and companies via a separate process
As long as you link a user to a company, Moesif will also associate any future web or API traffic from a user to that company.
Logging out
In your apps logout routine, if you use moesif-browser-js
, call moesif.reset()
when a user logs out of your app.
Doing this ensures a new anonymous id is generated and ensures different sessions are not mixed up, and is super important for testing.
Here is a simple example demonstrating the usage of the moesif.reset()
function:
function logoutUser() {
// example routine for logging out users from app
logoutUserFromApp();
clearSession();
// Reset browser session for Moesif
moesif.reset();
}