Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion src/tools/auth0/handlers/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import {
ClientExpressConfiguration,
ClientOrganizationRequireBehaviorEnum,
} from 'auth0';
import _ from 'lodash';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please import functions instead of _ for lodash.

import { Assets, Auth0APIClient } from '../../../types';
import { paginate } from '../client';
import DefaultAPIHandler from './default';
import { getConnectionProfile } from './connectionProfiles';
import { getUserAttributeProfiles } from './userAttributeProfiles';
import log from '../../../logger';

const multiResourceRefreshTokenPoliciesSchema = {
type: ['array', 'null'],
Expand Down Expand Up @@ -316,6 +318,11 @@ export default class ClientHandler extends DefaultAPIHandler {

assets.clients = await this.sanitizeMapExpressConfiguration(this.client, clients);

assets.clients = this.normalizeClientFields({
clients,
fields: [{ newField: 'cross_origin_authentication', deprecatedField: 'cross_origin_auth' }],
});

const excludedClients = (assets.exclude && assets.exclude.clients) || [];

const { del, update, create, conflicts } = await this.calcChanges(assets);
Expand Down Expand Up @@ -373,10 +380,56 @@ export default class ClientHandler extends DefaultAPIHandler {
is_global: false,
});

this.existing = clients;
const sanitizedClients = this.normalizeClientFields({
clients,
fields: [{ newField: 'cross_origin_authentication', deprecatedField: 'cross_origin_auth' }],
});
Comment on lines +383 to +386
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this logic under existing to

// Sanitize client fields
    const sanitizeClientFields = (list: Client[]): Client[] => ....


this.existing = sanitizedClients;
return this.existing;
}

/**
* @description Maps deprecated client fields to their new counterparts and removes the deprecated field.
* If a deprecated field exists, its value is always used for the new field, ensuring data migration
* and preventing loss of configuration data during schema transitions.
Comment on lines +393 to +395
Copy link
Contributor

@kushalshit27 kushalshit27 Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic is not correctly handled.

  • If only cross_origin_auth exists, use that value. (with a warn log)

  • if both exists

	cross_origin_auth: false
	cross_origin_authentication: false  

Use the value of cross_origin_authentication (with a warn log for cross_origin_auth)

  • if both exists
	cross_origin_auth: true
	cross_origin_authentication: false  

Use the value of cross_origin_authentication (with a warn log for cross_origin_auth)

Always give preference to the new key cross_origin_authentication.

* @returns Client[]
*/
normalizeClientFields = ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a function named sanitizeClientFields . please be specific with the function name

clients,
fields,
}: {
clients: Client[];
fields: {
newField: string;
deprecatedField: string;
}[];
}): Client[] =>
clients.map(
(client) =>
fields.reduce(
(acc, { deprecatedField, newField }) => {
const hasDeprecated = _.has(acc, deprecatedField);
const hasNew = _.has(acc, newField);

if (hasDeprecated) {
// If deprecated exists and new is missing, log a warning and copy the value
if (!hasNew) {
log.warn(
`Client '${client.name}': The '${deprecatedField}' field is deprecated. Migrating value to '${newField}'.`
);
acc[newField] = acc[deprecatedField];
}
// Remove the deprecated field
return _.omit(acc, deprecatedField);
}

return acc;
},
{ ...client } as Record<string, unknown>
) as Client
);
Comment on lines +407 to +431
Copy link
Contributor

@kushalshit27 kushalshit27 Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reduce complexity, read more on link


// convert names back to IDs for express configuration
async sanitizeMapExpressConfiguration(
auth0Client: Auth0APIClient,
Expand Down
68 changes: 68 additions & 0 deletions test/context/yaml/clients.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,74 @@ describe('#YAML context clients', () => {
expect(context.assets.clients).to.deep.equal(target);
});

it('should process clients and migrate deprecated fields', async () => {
const dir = path.join(testDataDir, 'yaml', 'clientsDeprecated');
cleanThenMkdir(dir);
const yaml = `
clients:
-
name: "deprecatedOnlyClient"
app_type: "spa"
cross_origin_auth: true
-
name: "bothFieldsClient"
app_type: "spa"
cross_origin_auth: false
cross_origin_authentication: true
-
name: "newOnlyClient"
app_type: "spa"
cross_origin_authentication: false
`;

const target = [
{
name: 'deprecatedOnlyClient',
app_type: 'spa',
cross_origin_authentication: true,
},
{
name: 'bothFieldsClient',
app_type: 'spa',
cross_origin_authentication: true,
},
{
name: 'newOnlyClient',
app_type: 'spa',
cross_origin_authentication: false,
},
];

const yamlFile = path.join(dir, 'clientsDeprecated.yaml');
fs.writeFileSync(yamlFile, yaml);

const config = {
AUTH0_INPUT_FILE: yamlFile,
};
const context = new Context(config, mockMgmtClient());
await context.loadAssetsFromLocal();

const actualClients = context.assets.clients.map((client) => {
const updated = { ...client };
const deprecatedField = 'cross_origin_auth';
const newField = 'cross_origin_authentication';

if (Object.prototype.hasOwnProperty.call(updated, deprecatedField)) {
if (!Object.prototype.hasOwnProperty.call(updated, newField)) {
updated[newField] = updated[deprecatedField];
}
delete updated[deprecatedField];
}
return updated;
});

const sortByName = (a, b) => a.name.localeCompare(b.name);
const actual = actualClients.sort(sortByName);
const expected = target.sort(sortByName);

expect(actual).to.deep.equal(expected);
});

it('should dump clients', async () => {
const dir = path.join(testDataDir, 'yaml', 'clientsDump');
cleanThenMkdir(dir);
Expand Down