Skip to content

Commit b0db5a2

Browse files
authored
feat: support for custom token exchange (#1232)
* Add token exchange profiles support and update dependencies * update e2e test recording * Update auth0 package resolution URL in package-lock.json * Update examples and update ACTIONS_TRIGGERS
1 parent 0460934 commit b0db5a2

25 files changed

+10851
-19266
lines changed

examples/directory/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ repository =>
4040
resource-servers
4141
resource_server1.json
4242
resource_server2.json
43+
token-exchange-profiles
44+
profile1.json
45+
profile2.json
4346
guardian
4447
factors
4548
sms.json

examples/directory/clients/My SPA.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,10 @@
3636
"token_endpoint_auth_method": "none",
3737
"web_origins": [
3838
"https://##ENV##.myapp.com"
39-
]
39+
],
40+
"token_exchange": {
41+
"allow_any_profile_of_type": [
42+
"custom_authentication"
43+
]
44+
}
4045
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "Custom Authentication Profile",
3+
"type": "custom_authentication",
4+
"subject_token_type": "urn:ietf:params:oauth:token-type:custom",
5+
"action": "custom-token-exchange-action"
6+
}

examples/yaml/tenant.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ clients:
4242
name: "My Resource Server Client"
4343
app_type: "resource_server"
4444
resource_server_identifier: "https://##ENV##.myapp.com/api/v1"
45+
-
46+
name: "My Token Exchange App"
47+
app_type: "regular_web"
48+
grant_types:
49+
- "authorization_code"
50+
- "refresh_token"
51+
- "client_credentials"
52+
- "urn:auth0:params:oauth:grant-type:token-exchange:federated-connection-access-token"
53+
token_exchange:
54+
allow_any_profile_of_type:
55+
- "custom_authentication"
4556

4657
databases:
4758
- name: "users"
@@ -99,6 +110,12 @@ resourceServers:
99110
# client_id: "if linked to a resource server client (readonly)"
100111
# Add other resource server settings (https://auth0.com/docs/api/management/v2#!/Resource_Servers/post_resource_servers)
101112

113+
tokenExchangeProfiles:
114+
- name: "Custom Authentication Profile"
115+
type: "custom_authentication"
116+
subject_token_type: "urn:ietf:params:oauth:token-type:custom"
117+
action: "custom-token-exchange-action"
118+
102119
phoneProviders:
103120
- name: twilio
104121
configuration:

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"homepage": "https://github.com/auth0/auth0-deploy-cli#readme",
3434
"dependencies": {
3535
"ajv": "^6.12.6",
36-
"auth0": "^5.1.0",
36+
"auth0": "^5.2.0",
3737
"dot-prop": "^5.3.0",
3838
"fs-extra": "^10.1.0",
3939
"js-yaml": "^4.1.1",

src/context/directory/handlers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import flows from './flows';
3333
import flowVaultConnections from './flowVaultConnections';
3434
import networkACLs from './networkACLs';
3535
import userAttributeProfiles from './userAttributeProfiles';
36+
import tokenExchangeProfiles from './tokenExchangeProfiles';
3637

3738
import DirectoryContext from '..';
3839
import { AssetTypes, Asset } from '../../../types';
@@ -82,6 +83,7 @@ const directoryHandlers: {
8283
selfServiceProfiles,
8384
networkACLs,
8485
userAttributeProfiles,
86+
tokenExchangeProfiles,
8587
};
8688

8789
export default directoryHandlers;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import path from 'path';
2+
import fs from 'fs-extra';
3+
import { constants } from '../../../tools';
4+
import log from '../../../logger';
5+
import { getFiles, existsMustBeDir, dumpJSON, loadJSON, sanitize } from '../../../utils';
6+
import { DirectoryHandler } from '.';
7+
import DirectoryContext from '..';
8+
import { Asset, ParsedAsset } from '../../../types';
9+
10+
type ParsedTokenExchangeProfiles = ParsedAsset<'tokenExchangeProfiles', Asset[]>;
11+
12+
function parse(context: DirectoryContext): ParsedTokenExchangeProfiles {
13+
const folder = path.join(context.filePath, constants.TOKEN_EXCHANGE_PROFILES_DIRECTORY);
14+
if (!existsMustBeDir(folder)) return { tokenExchangeProfiles: null }; // Skip
15+
16+
const files = getFiles(folder, ['.json']);
17+
18+
const profiles = files.map((f) =>
19+
loadJSON(f, {
20+
mappings: context.mappings,
21+
disableKeywordReplacement: context.disableKeywordReplacement,
22+
})
23+
);
24+
25+
return {
26+
tokenExchangeProfiles: profiles,
27+
};
28+
}
29+
30+
async function dump(context: DirectoryContext) {
31+
const { tokenExchangeProfiles } = context.assets;
32+
33+
if (!tokenExchangeProfiles || !Array.isArray(tokenExchangeProfiles)) return; // Skip
34+
35+
const folder = path.join(context.filePath, constants.TOKEN_EXCHANGE_PROFILES_DIRECTORY);
36+
fs.ensureDirSync(folder);
37+
38+
tokenExchangeProfiles.forEach((profile) => {
39+
const { id, created_at, updated_at, ...profileWithoutMetadata } = profile;
40+
const fileName = path.join(folder, sanitize(`${profile.name}.json`));
41+
log.info(`Writing ${fileName}`);
42+
dumpJSON(fileName, profileWithoutMetadata);
43+
});
44+
}
45+
46+
const handler: DirectoryHandler<ParsedTokenExchangeProfiles> = {
47+
parse,
48+
dump,
49+
};
50+
51+
export default handler;

src/context/yaml/handlers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import flows from './flows';
3333
import flowVaultConnections from './flowVaultConnections';
3434
import networkACLs from './networkACLs';
3535
import userAttributeProfiles from './userAttributeProfiles';
36+
import tokenExchangeProfiles from './tokenExchangeProfiles';
3637

3738
import YAMLContext from '..';
3839
import { AssetTypes } from '../../../types';
@@ -80,6 +81,7 @@ const yamlHandlers: { [key in AssetTypes]: YAMLHandler<{ [key: string]: unknown
8081
selfServiceProfiles,
8182
networkACLs,
8283
userAttributeProfiles,
84+
tokenExchangeProfiles,
8385
};
8486

8587
export default yamlHandlers;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { YAMLHandler } from '.';
2+
import YAMLContext from '..';
3+
import { Asset, ParsedAsset } from '../../../types';
4+
5+
type ParsedTokenExchangeProfiles = ParsedAsset<'tokenExchangeProfiles', Asset[]>;
6+
7+
async function parse(context: YAMLContext): Promise<ParsedTokenExchangeProfiles> {
8+
const { tokenExchangeProfiles } = context.assets;
9+
10+
if (!tokenExchangeProfiles) return { tokenExchangeProfiles: null };
11+
12+
return {
13+
tokenExchangeProfiles,
14+
};
15+
}
16+
17+
async function dump(context: YAMLContext): Promise<ParsedTokenExchangeProfiles> {
18+
const { tokenExchangeProfiles } = context.assets;
19+
20+
if (!tokenExchangeProfiles) return { tokenExchangeProfiles: null };
21+
22+
return {
23+
tokenExchangeProfiles: tokenExchangeProfiles.map((profile) => {
24+
// Strip server-generated fields
25+
const { id, created_at, updated_at, ...cleanProfile } = profile;
26+
return cleanProfile;
27+
}),
28+
};
29+
}
30+
31+
const handler: YAMLHandler<ParsedTokenExchangeProfiles> = {
32+
parse,
33+
dump,
34+
};
35+
36+
export default handler;

0 commit comments

Comments
 (0)