Skip to content

Commit

Permalink
fix: Updated templates to check cache for token
Browse files Browse the repository at this point in the history
- Updated four templates, to make use of API cache, to check for auth
  token BEFORE attempting to create a new token. this will reduce the
  number of times the template attempts to create a new auth0 token,
  especially when under high loads
  • Loading branch information
jean1880 committed Feb 5, 2024
1 parent 9640504 commit c9dfd45
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { ManagementClient } = require('auth0');
const { ManagementClient, AuthenticationClient } = require('auth0');

const HTTP_TIMEOUT = 1000; // 1s

Expand Down Expand Up @@ -47,11 +47,10 @@ exports.onExecutePostChallenge = async (event, api) => {

const domain = event.secrets.TENANT_DOMAIN;

const management = new ManagementClient({
domain,
clientId,
clientSecret,
httpTimeout: HTTP_TIMEOUT,
const management = await getManagementApiClient(api.cache, {
domain: event.secrets.API2_DOMAIN,
clientId: event.secrets.API2_CLIENT_ID,
clientSecret: event.secrets.API2_CLIENT_SECRET,
});

await management.users.update(
Expand All @@ -63,6 +62,51 @@ exports.onExecutePostChallenge = async (event, api) => {
}
);
};
/**
* Get an AccessToken
*
* @param {CacheAPI} cache
* @param {{domain: string, clientId: string, clientSecret: string, audience?: string}} options - AuthenticationClient options to fetch the token
* @return {Promise<string>}
*/
async function getAccessToken(cache, options) {
const key = `access_token_${options.clientId}_${options.audience}`;
// Check the cache if we have a valid entry
const record = cache.get(key);
if (record && record.expires_at > Date.now()) {
return record.value;
}

// Get the AccessToken using a client_credential grant.
const authClient = new AuthenticationClient(options);

const {
data: { access_token, expires_in },
} = await authClient.oauth.clientCredentialsGrant({
audience: options.audience ?? `https://${options.domain}/api/v2/`,
});

// Try to cache it
const cacheSetResult = cache.set(key, access_token, { ttl: expires_in });
if (cacheSetResult.type === 'error') {
console.error(`Failed to set ${key}: ${cacheSetResult.code}`);
}

return access_token;
}

/**
* @param {CacheAPI} cache
* @param {{domain: string, clientId: string, clientSecret: string, audience?: string}} options - AuthenticationClient options to fetch the token
*/
async function getManagementApiClient(cache, options) {
const token = await getAccessToken(cache, options);

return new ManagementClient({
domain: options.domain,
token,
});
}

/**
* Handler that will be invoked when this action is resuming after an external redirect. If your
Expand Down
57 changes: 51 additions & 6 deletions templates/add-persistence-attribute-POST_CHANGE_PASSWORD/code.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { ManagementClient } = require('auth0');
const { ManagementClient, AuthenticationClient } = require('auth0');

const HTTP_TIMEOUT = 1000; // 1s

Expand Down Expand Up @@ -52,11 +52,10 @@ exports.onExecutePostChangePassword = async (event, api) => {

const domain = event.secrets.TENANT_DOMAIN;

const management = new ManagementClient({
domain,
clientId,
clientSecret,
httpTimeout: HTTP_TIMEOUT,
const management = await getManagementApiClient(api.cache, {
domain: event.secrets.API2_DOMAIN,
clientId: event.secrets.API2_CLIENT_ID,
clientSecret: event.secrets.API2_CLIENT_SECRET,
});

await management.users.update(
Expand All @@ -68,3 +67,49 @@ exports.onExecutePostChangePassword = async (event, api) => {
}
);
};

/**
* Get an AccessToken
*
* @param {CacheAPI} cache
* @param {{domain: string, clientId: string, clientSecret: string, audience?: string}} options - AuthenticationClient options to fetch the token
* @return {Promise<string>}
*/
async function getAccessToken(cache, options) {
const key = `access_token_${options.clientId}_${options.audience}`;
// Check the cache if we have a valid entry
const record = cache.get(key);
if (record && record.expires_at > Date.now()) {
return record.value;
}

// Get the AccessToken using a client_credential grant.
const authClient = new AuthenticationClient(options);

const {
data: { access_token, expires_in },
} = await authClient.oauth.clientCredentialsGrant({
audience: options.audience ?? `https://${options.domain}/api/v2/`,
});

// Try to cache it
const cacheSetResult = cache.set(key, access_token, { ttl: expires_in });
if (cacheSetResult.type === 'error') {
console.error(`Failed to set ${key}: ${cacheSetResult.code}`);
}

return access_token;
}

/**
* @param {CacheAPI} cache
* @param {{domain: string, clientId: string, clientSecret: string, audience?: string}} options - AuthenticationClient options to fetch the token
*/
async function getManagementApiClient(cache, options) {
const token = await getAccessToken(cache, options);

return new ManagementClient({
domain: options.domain,
token,
});
}
58 changes: 52 additions & 6 deletions templates/add-persistence-attribute-POST_USER_REGISTRATION/code.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { ManagementClient } = require('auth0');
const { ManagementClient, AuthenticationClient } = require('auth0');

const HTTP_TIMEOUT = 1000; // 1s

Expand Down Expand Up @@ -52,11 +52,10 @@ exports.onExecutePostUserRegistration = async (event, api) => {

const domain = event.secrets.TENANT_DOMAIN;

const management = new ManagementClient({
domain,
clientId,
clientSecret,
httpTimeout: HTTP_TIMEOUT,
const management = await getManagementApiClient(api.cache, {
domain: event.secrets.API2_DOMAIN,
clientId: event.secrets.API2_CLIENT_ID,
clientSecret: event.secrets.API2_CLIENT_SECRET,
});

await management.users.update(
Expand All @@ -68,3 +67,50 @@ exports.onExecutePostUserRegistration = async (event, api) => {
}
);
};

/**
* Get an AccessToken
*
* @param {CacheAPI} cache
* @param {{domain: string, clientId: string, clientSecret: string, audience?: string}} options - AuthenticationClient options to fetch the token
* @return {Promise<string>}
*/
async function getAccessToken(cache, options) {
const key = `access_token_${options.clientId}_${options.audience}`;
// Check the cache if we have a valid entry
const record = cache.get(key);

if (record && record.expires_at > Date.now()) {
return record.value;
}

// Get the AccessToken using a client_credential grant.
const authClient = new AuthenticationClient(options);

const {
data: { access_token, expires_in },
} = await authClient.oauth.clientCredentialsGrant({
audience: options.audience ?? `https://${options.domain}/api/v2/`,
});

// Try to cache it
const cacheSetResult = cache.set(key, access_token, { ttl: expires_in });
if (cacheSetResult.type === 'error') {
console.error(`Failed to set ${key}: ${cacheSetResult.code}`);
}

return access_token;
}

/**
* @param {CacheAPI} cache
* @param {{domain: string, clientId: string, clientSecret: string, audience?: string}} options - AuthenticationClient options to fetch the token
*/
async function getManagementApiClient(cache, options) {
const token = await getAccessToken(cache, options);

return new ManagementClient({
domain: options.domain,
token,
});
}
80 changes: 60 additions & 20 deletions templates/add-persistence-attribute-SEND_PHONE_MESSAGE/code.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
const { ManagementClient } = require('auth0');
const { ManagementClient, AuthenticationClient } = require('auth0');

const HTTP_TIMEOUT = 1000; // 1s

// --- AUTH0 ACTIONS TEMPLATE https://github.com/auth0/opensource-marketplace/blob/main/templates/add-persistence-attribute-SEND_PHONE_MESSAGE ---
// --- AUTH0 ACTIONS TEMPLATE https://github.com/auth0/opensource-marketplace/blob/main/templates/add-persistence-attribute-PASSWORD_RESET_POST_CHALLENGE ---
/**
* Handler that will be called during the execution of a SendPhoneMessage flow.
* Handler that will be called during the execution of a Password Reset / Post Challenge Flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {SendPhoneMessageAPI} api - Methods and utilities to help change the behavior of sending a phone message.
* @param {Event} event - Details about the post challenge request.
* @param {PasswordResetPostChallengeAPI} api - Interface whose methods can be used to change the behavior of the post challenge flow.
*/
exports.onExecuteSendPhoneMessage = async (event, api) => {
if (!event.secrets.METADATA_KEY) {
console.log('missing event.secrets.METADATA_KEY');
return;
return api.access.deny('missing metadata key');
}

const metadataKey = event.secrets.METADATA_KEY;

if (!event.secrets.METADATA_DEFAULT_VALUE) {
console.log('missing event.secrets.METADATA_DEFAULT_VALUE');
return;
return api.access.deny('missing metadata default value');
}

const metadataValue = event.user.user_metadata[metadataKey];
Expand All @@ -32,31 +30,27 @@ exports.onExecuteSendPhoneMessage = async (event, api) => {
const metadataDefaultValue = event.secrets.METADATA_DEFAULT_VALUE;

if (!event.secrets.CLIENT_ID) {
console.log('missing event.secrets.CLIENT_ID');
return;
return api.access.deny('missing client id');
}

const clientId = event.secrets.CLIENT_ID;

if (!event.secrets.CLIENT_SECRET) {
console.log('missing event.secrets.CLIENT_SECRET');
return;
return api.access.deny('missing client secret');
}

const clientSecret = event.secrets.CLIENT_SECRET;

if (!event.secrets.TENANT_DOMAIN) {
console.log('missing event.secrets.TENANT_DOMAIN');
return;
return api.access.deny('missing tenant domain');
}

const domain = event.secrets.TENANT_DOMAIN;

const management = new ManagementClient({
domain,
clientId,
clientSecret,
httpTimeout: HTTP_TIMEOUT,
const management = await getManagementApiClient(api.cache, {
domain: event.secrets.TENANT_DOMAIN,
clientId: event.secrets.CLIENT_ID,
clientSecret: event.secrets.CLIENT_SECRET,
});

await management.users.update(
Expand All @@ -68,3 +62,49 @@ exports.onExecuteSendPhoneMessage = async (event, api) => {
}
);
};
/**
* Get an AccessToken
*
* @param {CacheAPI} cache
* @param {{domain: string, clientId: string, clientSecret: string, audience?: string}} options - AuthenticationClient options to fetch the token
* @return {Promise<string>}
*/
async function getAccessToken(cache, options) {
const key = `access_token_${options.clientId}_${options.audience}`;
// Check the cache if we have a valid entry
const record = cache.get(key);

if (record && record.expires_at > Date.now()) {
return record.value;
}

// Get the AccessToken using a client_credential grant.
const authClient = new AuthenticationClient(options);

const {
data: { access_token, expires_in },
} = await authClient.oauth.clientCredentialsGrant({
audience: options.audience ?? `https://${options.domain}/api/v2/`,
});

// Try to cache it
const cacheSetResult = cache.set(key, access_token, { ttl: expires_in });
if (cacheSetResult.type === 'error') {
console.error(`Failed to set ${key}: ${cacheSetResult.code}`);
}

return access_token;
}

/**
* @param {CacheAPI} cache
* @param {{domain: string, clientId: string, clientSecret: string, audience?: string}} options - AuthenticationClient options to fetch the token
*/
async function getManagementApiClient(cache, options) {
const token = await getAccessToken(cache, options);

return new ManagementClient({
domain: options.domain,
token,
});
}

0 comments on commit c9dfd45

Please sign in to comment.