Hello @rueben.tiow,
I did read your script and try it previously, but it did not mitigate the issue. First, I did have the issue with one of the tokens being larger than 2048 and tried your solution to this, but as this did not remove the problem, I instead reduced the size of the token which was possible, as there were permissions that could be removed. While watching the logs, I can see that sometimes the token is found in cache, and sometimes not and it is therefore refetched. I attach my code below.
/**
* 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 ManagementClient = require('auth0').ManagementClient;
const axios = require('axios').default;
const { jwtDecode } = require('jwt-decode');
const domain = 'REMOVED';
const managementAuth0 = 'auth0';
const customAPI = 'REMOVED';
// cache key for API explorer for Auth0 Management API
const cacheKeyManagementToken = 'management-auth0-token';
// cache key for custom api
const cacheKeyAuthorizationToken = 'REMOVED';
// -------------------------------
// Helper functions - start
// -------------------------------
const queryToken = async settings => {
const options = {
method: 'POST',
url: `https://${domain}/oauth/token`,
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: new URLSearchParams({
grant_type: 'client_credentials',
audience: settings.audience,
client_id: settings.client_id,
client_secret: settings.client_secret,
}),
};
try {
const response = await axios.request(options);
return response.data;
} catch (error) {
console.error(error);
}
};
const getAccessToken = async manager => {
let settings;
// to call Auth0 Management
if (manager === managementAuth0)
settings = {
audience: 'REMOVED',
client_id: 'REMOVED',
client_secret: 'REMOVED',
};
// to call custom API
else {
settings = {
audience: 'REMOVED',
client_id: 'REMOVED',
client_secret: 'REMOVED',
};
}
const result = await queryToken(settings);
return result.access_token;
};
const saveTokenToCache = (cacheKey, token) => {
const decodedNew = jwtDecode(token);
// Deduce the cache expiry time: Five minutes before the token expires (to make sure the token is valid throughout the action).
const expiresAtSeconds = decodedNew?.exp ?? 0;
let cacheExpiresMs = (expiresAtSeconds * 1000) - 5*60*1000;
if (cacheExpiresMs < 0){
cacheExpiresMs = 1000;
}
//console.log('Setting to cache with expires_at: ', cacheExpiresMs);
//console.log('Blob size of the token is: ',(new Blob([token]))?.size);
const result = api.cache.set(cacheKey, token, {expires_at: cacheExpiresMs});
if (result.type === 'error') {
console.log(
'failed to set the token in the cache with error code',
result.code,
);
} else {
console.log(`successfully cached token.`);
}
};
const getTokenFromCache = (cacheKey) => {
return api.cache.get(cacheKey)?.value;
};
/**
* Get an acccess token, primarily from cache, but if it is not found in cache or isn't valid anymore,
* fetch a new one.
* @param {*} manager
* @param {*} cacheKey
* @returns
*/
const getToken = async (manager, cacheKey) => {
let token = getTokenFromCache(cacheKey);
//console.log('Trying first to get from cache, got:', token);
let current_time = Date.now().valueOf() / 1000;
let decoded = null;
if (token) decoded = jwtDecode(token);
//console.log('The exp of the token is', decoded?.exp);
if (token != undefined && decoded?.exp > current_time) {
console.log('Found token in cache.');
return token;
} else {
token = await getAccessToken(manager);
if (token) {
saveTokenToCache(cacheKey, token);
return token;
}
// Never throw errors in actions, deny access instead in error situations.
api.access.deny(`Couldn't get or create access token for: ${manager}!`);
}
};
// query data from custom API
const getDataFromCustomApi = async (/*REMOVED PARAMETERS */) => {
let token = await getToken(customAPI, cacheKeyAuthorizationToken);
let url = 'REMOVED';
const options = {
method: 'GET',
url: url,
headers: {
'content-type': 'application/json',
'cache-control': 'no-cache',
Authorization: `Bearer ${token}`,
},
data: {
myfield: 'myvalue'
},
};
// request custom data
try {
const response = await axios.request(options);
return response.data;
} catch (error) {
console.error(`request error: ${error}`);
}
};
const handleStuff = async (myvar = []) => {
const token = await getToken(managementAuth0, cacheKeyManagementToken);
const management = new ManagementClient({ domain: domain, token: token });
const roles = (await management.roles.getAll()) || [];
if (2 > 1 /* Removed condition */) {
try {
await management.users.deleteRoles(params, { roles: [/* removed */] });
} catch (error) {
console.log(error);
}
}
};
// -------------------------------
// Helper functions - end
// -------------------------------
const customData = await getDataFromCustomApi(
event.user.user_id,
event.client.name,
'removed' || null,
);
await handleStuff([/*removed*/]);
api.accessToken.setCustomClaim(
`${namespace}/removedId`,
customData?.removedId,
);
api.idToken.setCustomClaim(
`${namespace}/removedId`,
customData?.removedId,
);
// add rest of needed token data
api.accessToken.setCustomClaim(`${namespace}/removed`, 'removed');
};