Skip to content
Open
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
26 changes: 26 additions & 0 deletions docs/resource-specific-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -1129,3 +1129,29 @@ exports.onExecuteSendPhoneMessage = async (event) => {
);
};
```

## Supplemental Signals

Supplemental signals configuration allows you to enable third-party integrations for enhanced security and risk assessment.

- `akamai_enabled` (boolean): Enable processing of incoming Akamai headers for supplemental security signals

### YAML Example

```yaml
# Contents of ./tenant.yaml
supplementalSignals:
akamai_enabled: true
```

### Directory Example

Folder: `./supplemental-signals.json`

```json
{
"akamai_enabled": true
}
```

For more details, see the [Management API documentation](https://auth0.com/docs/api/management/v2#!/Supplemental_Signals).
3 changes: 3 additions & 0 deletions examples/directory/supplemental-signals.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"akamai_enabled": true
}
2 changes: 2 additions & 0 deletions examples/yaml/tenant.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -525,3 +525,5 @@ riskAssessment:
# new_device:
# remember_for: 30

supplementalSignals:
akamai_enabled: true
2 changes: 2 additions & 0 deletions src/context/directory/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import networkACLs from './networkACLs';
import userAttributeProfiles from './userAttributeProfiles';
import connectionProfiles from './connectionProfiles';
import tokenExchangeProfiles from './tokenExchangeProfiles';
import supplementalSignals from './supplementalSignals';

import DirectoryContext from '..';
import { AssetTypes, Asset } from '../../../types';
Expand Down Expand Up @@ -90,6 +91,7 @@ const directoryHandlers: {
userAttributeProfiles,
connectionProfiles,
tokenExchangeProfiles,
supplementalSignals,
};

export default directoryHandlers;
41 changes: 41 additions & 0 deletions src/context/directory/handlers/supplementalSignals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import path from 'path';
import { existsMustBeDir, isFile, dumpJSON, loadJSON } from '../../../utils';
import { DirectoryHandler } from '.';
import DirectoryContext from '..';
import { ParsedAsset } from '../../../types';

type ParsedSupplementalSignals = ParsedAsset<'supplementalSignals', { akamai_enabled?: boolean }>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Use SDK interface


function parse(context: DirectoryContext): ParsedSupplementalSignals {
const baseFolder = path.join(context.filePath);
if (!existsMustBeDir(baseFolder)) return { supplementalSignals: null }; // Skip

const supplementalSignalsFile = path.join(baseFolder, 'supplemental-signals.json');

if (!isFile(supplementalSignalsFile)) {
return { supplementalSignals: null };
}

const supplementalSignals = loadJSON(supplementalSignalsFile, {
mappings: context.mappings,
disableKeywordReplacement: context.disableKeywordReplacement,
});

return {
supplementalSignals,
};
}

async function dump(context: DirectoryContext): Promise<void> {
const { supplementalSignals } = context.assets;

if (!supplementalSignals) return; // Skip, nothing to dump

const supplementalSignalsFile = path.join(context.filePath, 'supplemental-signals.json');
dumpJSON(supplementalSignalsFile, supplementalSignals);
}

export default {
parse,
dump,
} as DirectoryHandler<ParsedSupplementalSignals>;
2 changes: 2 additions & 0 deletions src/context/yaml/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import networkACLs from './networkACLs';
import userAttributeProfiles from './userAttributeProfiles';
import connectionProfiles from './connectionProfiles';
import tokenExchangeProfiles from './tokenExchangeProfiles';
import supplementalSignals from './supplementalSignals';

import YAMLContext from '..';
import { AssetTypes } from '../../../types';
Expand Down Expand Up @@ -88,6 +89,7 @@ const yamlHandlers: { [key in AssetTypes]: YAMLHandler<{ [key: string]: unknown
userAttributeProfiles,
connectionProfiles,
tokenExchangeProfiles,
supplementalSignals,
};

export default yamlHandlers;
22 changes: 22 additions & 0 deletions src/context/yaml/handlers/supplementalSignals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { YAMLHandler } from '.';
import YAMLContext from '..';
import { ParsedAsset } from '../../../types';

type ParsedSupplementalSignals = ParsedAsset<'supplementalSignals', { akamai_enabled?: boolean }>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Use SDK interface


async function parseAndDump(context: YAMLContext): Promise<ParsedSupplementalSignals> {
const { supplementalSignals } = context.assets;

if (!supplementalSignals) return { supplementalSignals: null };

return {
supplementalSignals,
};
}

const supplementalSignalsHandler: YAMLHandler<ParsedSupplementalSignals> = {
parse: parseAndDump,
dump: parseAndDump,
};

export default supplementalSignalsHandler;
2 changes: 2 additions & 0 deletions src/tools/auth0/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import * as networkACLs from './networkACLs';
import * as userAttributeProfiles from './userAttributeProfiles';
import * as connectionProfiles from './connectionProfiles';
import * as tokenExchangeProfiles from './tokenExchangeProfiles';
import * as supplementalSignals from './supplementalSignals';

import { AssetTypes } from '../../../types';
import APIHandler from './default';
Expand Down Expand Up @@ -84,6 +85,7 @@ const auth0ApiHandlers: { [key in AssetTypes]: any } = {
userAttributeProfiles,
connectionProfiles,
tokenExchangeProfiles,
supplementalSignals,
};

export default auth0ApiHandlers as {
Expand Down
65 changes: 65 additions & 0 deletions src/tools/auth0/handlers/supplementalSignals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Management, ManagementError } from 'auth0';
import DefaultHandler, { order } from './default';
import { Asset, Assets } from '../../../types';
import log from '../../../logger';

export const schema = {
type: 'object',
properties: {
akamai_enabled: {
type: 'boolean',
description: 'Enable Akamai supplemental signals integration',
},
},
additionalProperties: false,
Copy link
Contributor

Choose a reason for hiding this comment

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

better to remove it: additionalProperties: false,

};

export type SupplementalSignals = Management.GetSupplementalSignalsResponseContent;

export default class SupplementalSignalsHandler extends DefaultHandler {
existing: SupplementalSignals;
Copy link
Contributor

@kushalshit27 kushalshit27 Feb 27, 2026

Choose a reason for hiding this comment

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

Suggested change
existing: SupplementalSignals;
existing: SupplementalSignals | null;


constructor(options: DefaultHandler) {
super({
...options,
type: 'supplementalSignals',
});
}

async getType(): Promise<Asset> {
Copy link
Contributor

@kushalshit27 kushalshit27 Feb 27, 2026

Choose a reason for hiding this comment

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

Suggested change
async getType(): Promise<Asset> {
async getType(): Promise<Asset | null> {

try {
const supplementalSignals = await this.client.supplementalSignals.get();
this.existing = supplementalSignals;
return supplementalSignals;
} catch (err) {
if (err instanceof ManagementError && err.statusCode === 403) {
log.debug(
'Supplemental Signals feature is not available for this tenant. Please verify `scope` or contact Auth0 support to enable this feature.'
);
return {};
Copy link
Contributor

@kushalshit27 kushalshit27 Feb 27, 2026

Choose a reason for hiding this comment

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

Suggested change
return {};
return null;

}
throw err;
}
}

async validate(assets: Assets): Promise<void> {
const { supplementalSignals } = assets;

if (!supplementalSignals) return;
}
Comment on lines +45 to +49
Copy link
Contributor

@kushalshit27 kushalshit27 Feb 27, 2026

Choose a reason for hiding this comment

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

Do we need this validation? this is not needed


@order('100')
async processChanges(assets: Assets): Promise<void> {
const { supplementalSignals } = assets;

if (!supplementalSignals) return;

if (supplementalSignals && Object.keys(supplementalSignals).length > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (supplementalSignals && Object.keys(supplementalSignals).length > 0) {
if (Object.keys(supplementalSignals).length > 0) {

await this.client.supplementalSignals.patch(
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need 403 here?

supplementalSignals as Management.UpdateSupplementalSignalsRequestContent
);
this.updated += 1;
this.didUpdate(supplementalSignals);
}
}
}
1 change: 1 addition & 0 deletions src/tools/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ const constants = {
USER_ATTRIBUTE_PROFILES_DIRECTORY: 'user-attribute-profiles',
CONNECTION_PROFILES_DIRECTORY: 'connection-profiles',
TOKEN_EXCHANGE_PROFILES_DIRECTORY: 'token-exchange-profiles',
SUPPLEMENTAL_SIGNALS_DIRECTORY: 'supplemental-signals',
};

export default constants;
4 changes: 3 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export type Assets = Partial<{
rulesConfigs: Asset[] | null;
tenant: Tenant | null;
triggers: Asset[] | null;
supplementalSignals: Management.GetSupplementalSignalsResponseContent | null;
Copy link
Contributor

@kushalshit27 kushalshit27 Feb 27, 2026

Choose a reason for hiding this comment

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

keep Management.GetSupplementalSignalsResponseContent on handler , please check other handler for reference

// non-resource types
exclude?: {
[key: string]: string[];
Expand Down Expand Up @@ -204,7 +205,8 @@ export type AssetTypes =
| 'networkACLs'
| 'userAttributeProfiles'
| 'connectionProfiles'
| 'tokenExchangeProfiles';
| 'tokenExchangeProfiles'
| 'supplementalSignals';

export type KeywordMappings = { [key: string]: (string | number)[] | string | number };

Expand Down
88 changes: 88 additions & 0 deletions test/context/directory/supplementalSignals.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import path from 'path';
import { expect } from 'chai';

import Context from '../../../src/context/directory';
import { testDataDir, createDir, mockMgmtClient, cleanThenMkdir } from '../../utils';
import handler from '../../../src/context/directory/handlers/supplementalSignals';
import { loadJSON } from '../../../src/utils';

describe('#directory context supplementalSignals', () => {
it('should process supplementalSignals', async () => {
const supplementalSignalsTest = {
'supplemental-signals.json': `{
"akamai_enabled": true
}`,
};

const supplementalSignalsTarget = {
akamai_enabled: true,
};

createDir(path.join(testDataDir, 'directory'), {
supplementalSignals1: supplementalSignalsTest,
});

const config = {
AUTH0_INPUT_FILE: path.join(testDataDir, 'directory', 'supplementalSignals1'),
};
const context = new Context(config, mockMgmtClient());
await context.loadAssetsFromLocal();

expect(context.assets.supplementalSignals).to.deep.equal(supplementalSignalsTarget);
});

it('should process supplementalSignals with keyword replacement', async () => {
const supplementalSignalsTest = {
'supplemental-signals.json': `{
"akamai_enabled": ##AKAMAI_ENABLED##
}`,
};

const supplementalSignalsTarget = {
akamai_enabled: false,
};

createDir(path.join(testDataDir, 'directory'), {
supplementalSignals2: supplementalSignalsTest,
});

const config = {
AUTH0_INPUT_FILE: path.join(testDataDir, 'directory', 'supplementalSignals2'),
AUTH0_KEYWORD_REPLACE_MAPPINGS: { AKAMAI_ENABLED: false },
};
const context = new Context(config, mockMgmtClient());
await context.loadAssetsFromLocal();

expect(context.assets.supplementalSignals).to.deep.equal(supplementalSignalsTarget);
});

it('should dump supplementalSignals', async () => {
const dir = path.join(testDataDir, 'directory', 'supplementalSignalsDump');
cleanThenMkdir(dir);
const context = new Context({ AUTH0_INPUT_FILE: dir }, mockMgmtClient());

context.assets.supplementalSignals = {
akamai_enabled: true,
};

await handler.dump(context);
const dumped = loadJSON(path.join(dir, 'supplemental-signals.json'));

expect(dumped).to.deep.equal(context.assets.supplementalSignals);
});

it('should dump supplementalSignals with false value', async () => {
const dir = path.join(testDataDir, 'directory', 'supplementalSignalsDumpFalse');
cleanThenMkdir(dir);
const context = new Context({ AUTH0_INPUT_FILE: dir }, mockMgmtClient());

context.assets.supplementalSignals = {
akamai_enabled: false,
};

await handler.dump(context);
const dumped = loadJSON(path.join(dir, 'supplemental-signals.json'));

expect(dumped).to.deep.equal(context.assets.supplementalSignals);
});
});
9 changes: 9 additions & 0 deletions test/context/yaml/context.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,9 @@ describe('#YAML context validation', () => {
flows: [],
forms: [],
selfServiceProfiles: [],
supplementalSignals: {
akamai_enabled: false,
},
tokenExchangeProfiles: [],
userAttributeProfiles: [],
phoneTemplates: [],
Expand Down Expand Up @@ -477,6 +480,9 @@ describe('#YAML context validation', () => {
flows: [],
forms: [],
selfServiceProfiles: [],
supplementalSignals: {
akamai_enabled: false,
},
tokenExchangeProfiles: [],
userAttributeProfiles: [],
phoneTemplates: [],
Expand Down Expand Up @@ -618,6 +624,9 @@ describe('#YAML context validation', () => {
flows: [],
forms: [],
selfServiceProfiles: [],
supplementalSignals: {
akamai_enabled: false,
},
tokenExchangeProfiles: [],
userAttributeProfiles: [],
phoneTemplates: [],
Expand Down
Loading