Skip to content

Instantly share code, notes, and snippets.

@antonzy
Created December 29, 2024 13:50
Show Gist options
  • Select an option

  • Save antonzy/c81e1facbac6c39817aee4645b0dec10 to your computer and use it in GitHub Desktop.

Select an option

Save antonzy/c81e1facbac6c39817aee4645b0dec10 to your computer and use it in GitHub Desktop.
Stigg for Auth0 Login Action
const ManagementClient = require('auth0').ManagementClient;
const Stigg = require('@stigg/node-server-sdk').Stigg;
const crypto = require('crypto');
/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {
const apiKey = event.secrets.STIGG_API_KEY;
const individualFreePlanId = event.secrets.STIGG_INDIVIDUAL_PLAN_ID;
const businessFreePlanId = event.secrets.STIGG_BUSINESS_PLAN_ID;
const activeUsersFeatureId = event.secrets.STIGG_ACTIVE_USERS_FEATURE_ID;
const applicationId = event.secrets.STIGG_APPLICATION_ID;
const auth0Domain = event.secrets.DOMAIN;
const auth0ClientId = event.secrets.CLIENT_ID;
const auth0ClientSecret = event.secrets.CLIENT_SECRET;
const isOrganization = !!event.organization;
const customerAlreadyProvisioned = isOrganization ? event.organization?.metadata?.customer_provisioned : event.user.user_metadata?.customer_provisioned;
const customerId = event.organization?.id || event.user.user_id;
const customerName = event.organization?.display_name || event.user.name;
const customerEmail = isOrganization ? undefined : event.user.email;
const initialCustomerPlanId = isOrganization ? businessFreePlanId : individualFreePlanId;
const organizationId = event.organization?.id;
// store last login for organization in a separate key
const lastLoginMetadataKey = `${organizationId}:last_login`;
if (event.client.client_id !== applicationId) {
return;
}
const managementClient = new ManagementClient({
domain: auth0Domain,
clientId: auth0ClientId,
clientSecret: auth0ClientSecret,
});
const stigg = Stigg.initialize({
apiKey,
realtimeUpdatesEnabled: false,
enableRemoteConfig: false,
});
const provisionCustomer = async () => {
try {
if (!customerAlreadyProvisioned && initialCustomerPlanId) {
await stigg.provisionCustomer({
customerId,
name: customerName,
email: customerEmail,
subscriptionParams: {
planId: initialCustomerPlanId,
},
});
}
} catch (e) {
if (!e.message.includes('Duplicate')) {
console.error('Failed to provision customer', e);
}
return;
}
}
const updateMetadata = async () => {
try {
if (isOrganization) {
await managementClient.organizations.update(
{
id: customerId,
},
{
metadata: {
customer_provisioned: 'true',
},
},
);
} else {
await managementClient.users.update(
{
id: customerId
},
{
user_metadata: {
customer_provisioned: 'true',
}
}
)
}
} catch (e) {
console.error(`Failed to update ${isOrganization ? 'organization' : 'user'}`, e);
}
}
const checkActiveUsersEntitlement = async () => {
const mauEntitlement = await stigg.getMeteredEntitlement({
customerId: organizationId,
featureId: activeUsersFeatureId,
options: {
requestedUsage: 1,
},
});
// block access to new MAU if the organization has exceeded the limit
if (!mauEntitlement.hasAccess && mauEntitlement.accessDeniedReason === 'RequestedUsageExceedingLimit') {
const lastLogin = new Date(event.user.app_metadata[lastLoginMetadataKey] || 0);
const usagePeriodStart = new Date(mauEntitlement.usagePeriodStart?.getTime() || 0);
const isNewMau = lastLogin < usagePeriodStart;
if (isNewMau) {
api.access.deny('Organization has exceeded the MAU limit, please contact your administrator.');
return false;
}
}
return true;
}
const reportActiveUsersEvent = async () => {
const now = Date.now();
api.user.setAppMetadata(lastLoginMetadataKey, now);
const userId = event.user.user_id;
const eventName = 'user-login';
// generate a unique idempotency key for the event
const idempotencyKey = crypto
.createHash('md5')
.update(`${userId}:${organizationId}:${eventName}:${now}`)
.digest('hex');
try {
await stigg.reportEvent({
customerId: organizationId,
eventName,
timestamp: now,
idempotencyKey,
dimensions: {
user_id: userId,
},
});
} catch (e) {
console.error('Failed to report user login event', e);
}
}
await provisionCustomer();
await updateMetadata();
if (!event.organization) {
return;
}
const hasAccess = await checkActiveUsersEntitlement();
if (hasAccess) {
await reportActiveUsersEvent();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment