Skip to content

Commit cbdbf68

Browse files
committed
Support Auth Providers
Signed-off-by: Rupesh J <rupesh.j@salesforce.com>
1 parent 33e2f02 commit cbdbf68

6 files changed

Lines changed: 195 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "../schema.json",
3+
"settings": {
4+
"authProviders": {}
5+
}
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "../schema.json",
3+
"settings": {
4+
"authProviders": {}
5+
}
6+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { BrowserforcePlugin } from '../../plugin.js';
2+
3+
const CONSUMER_SECRET_SELECTOR = '#ConsumerSecret';
4+
const CONSUMER_KEY_SELECTOR = '#ConsumerKey';
5+
const SAVE_BUTTON_SELECTOR = 'input[id$=":saveBtn"], #topButtonRow > input[name="save"], button[title="Save"]';
6+
7+
const getUrl = (orgId: string) => `/${orgId}/e`;
8+
9+
type AuthProviderConfig = {
10+
consumerSecret?: string;
11+
consumerKey?: string;
12+
};
13+
14+
type Config = {
15+
[developerName: string]: AuthProviderConfig;
16+
};
17+
18+
type AuthProviderRecord = {
19+
Id: string;
20+
DeveloperName: string;
21+
};
22+
23+
export class AuthProviders extends BrowserforcePlugin {
24+
public async retrieve(): Promise<Config> {
25+
// Skip retrieve as requested - return empty config
26+
return {};
27+
}
28+
29+
public async apply(config: Config): Promise<void> {
30+
if (!config || Object.keys(config).length === 0) {
31+
return;
32+
}
33+
34+
const developerNames = Object.keys(config);
35+
const developerNamesList = developerNames.map((name) => `'${name}'`).join(',');
36+
37+
// Query AuthProviders using standard REST API (not Tooling API)
38+
const authProviders = await this.org
39+
.getConnection()
40+
.query<AuthProviderRecord>(
41+
`SELECT Id, DeveloperName FROM AuthProvider WHERE DeveloperName IN (${developerNamesList})`,
42+
);
43+
44+
if (authProviders.records.length === 0) {
45+
throw new Error(`No AuthProviders found with DeveloperNames: ${developerNames.join(', ')}`);
46+
}
47+
48+
// Create a map for quick lookup
49+
const authProviderMap = new Map<string, string>();
50+
for (const authProvider of authProviders.records) {
51+
authProviderMap.set(authProvider.DeveloperName, authProvider.Id);
52+
}
53+
54+
// Verify we found all required AuthProviders
55+
const missingProviders = developerNames.filter((name) => !authProviderMap.has(name));
56+
if (missingProviders.length > 0) {
57+
throw new Error(
58+
`AuthProvider with DeveloperName(s) not found: ${missingProviders.join(', ')}. ` +
59+
`Please verify the DeveloperNames are correct and the AuthProviders exist in your org.`,
60+
);
61+
}
62+
63+
// Process each auth provider configuration
64+
for (const [developerName, authProviderConfig] of Object.entries(config)) {
65+
const authProviderId = authProviderMap.get(developerName);
66+
if (!authProviderId) {
67+
throw new Error(`AuthProvider with DeveloperName '${developerName}' not found`);
68+
}
69+
70+
// Navigate to the edit page
71+
const editPageUrl = getUrl(authProviderId);
72+
73+
this.browserforce.logger?.log('editPageUrl', editPageUrl);
74+
console.log(`[AuthProviders] Navigating to edit page for ${developerName}: ${editPageUrl}`);
75+
76+
const page = await this.browserforce.openPage(editPageUrl);
77+
78+
// Wait for the page/frame to load - handle both Lightning (iframe) and Classic UI
79+
// Use ConsumerSecret as the selector to wait for, or fallback to ConsumerKey
80+
const formSelector = `${CONSUMER_SECRET_SELECTOR}, ${CONSUMER_KEY_SELECTOR}, form`;
81+
const frameOrPage = await this.browserforce.waitForSelectorInFrameOrPage(page, formSelector);
82+
83+
try {
84+
// Update ConsumerSecret if provided
85+
if (authProviderConfig.consumerSecret !== undefined) {
86+
await frameOrPage.waitForSelector(CONSUMER_SECRET_SELECTOR, { timeout: 10000 });
87+
await frameOrPage.$eval(
88+
CONSUMER_SECRET_SELECTOR,
89+
(e: HTMLInputElement, v: string) => {
90+
e.value = v;
91+
},
92+
authProviderConfig.consumerSecret,
93+
);
94+
}
95+
96+
// Update ConsumerKey if provided
97+
if (authProviderConfig.consumerKey !== undefined) {
98+
await frameOrPage.waitForSelector(CONSUMER_KEY_SELECTOR, { timeout: 10000 });
99+
await frameOrPage.$eval(
100+
CONSUMER_KEY_SELECTOR,
101+
(e: HTMLInputElement, v: string) => {
102+
e.value = v;
103+
},
104+
authProviderConfig.consumerKey,
105+
);
106+
}
107+
108+
// Save the changes
109+
await frameOrPage.waitForSelector(SAVE_BUTTON_SELECTOR, { timeout: 10000 });
110+
111+
// Click save button - don't wait for navigation as it may redirect to a non-existent page
112+
// Instead, wait for the click to complete and then check for errors
113+
const saveButton = await frameOrPage.$(SAVE_BUTTON_SELECTOR);
114+
if (!saveButton) {
115+
throw new Error(`Save button not found for AuthProvider '${developerName}'`);
116+
}
117+
118+
// Click the save button
119+
await saveButton.click();
120+
121+
// Wait for save to complete - give it time to process
122+
// The page might reload or show a success/error message
123+
await new Promise((resolve) => setTimeout(resolve, 3000));
124+
125+
// Check for errors on the page/frame
126+
// If the frame still exists, check it; otherwise check the main page
127+
try {
128+
// Try to check for errors in the frame first
129+
const errorElements = await frameOrPage.$$('div.errorMsg, div.error, .errorMessage, #errorTitle');
130+
if (errorElements.length > 0) {
131+
const errorText = await frameOrPage.evaluate(() => {
132+
const errorDiv = document.querySelector('div.errorMsg, div.error, .errorMessage, #errorTitle');
133+
return errorDiv ? errorDiv.textContent : null;
134+
});
135+
if (errorText && !errorText.includes('page no longer exists')) {
136+
throw new Error(`Save failed: ${errorText}`);
137+
}
138+
}
139+
} catch (e) {
140+
// If checking frame fails, check the main page
141+
await this.browserforce.throwPageErrors(page);
142+
}
143+
} catch (error) {
144+
await page.close();
145+
throw new Error(`Failed to update AuthProvider '${developerName}': ${error.message}`);
146+
}
147+
148+
await page.close();
149+
}
150+
}
151+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema",
3+
"$id": "./auth-providers/schema.json",
4+
"title": "Auth Providers",
5+
"description": "Configuration for updating Auth Provider Consumer Key and Consumer Secret",
6+
"type": "object",
7+
"patternProperties": {
8+
"^[a-zA-Z0-9_]+$": {
9+
"type": "object",
10+
"properties": {
11+
"consumerSecret": {
12+
"title": "Consumer Secret",
13+
"description": "The Consumer Secret value for the Auth Provider",
14+
"type": "string",
15+
"x-password": true
16+
},
17+
"consumerKey": {
18+
"title": "Consumer Key",
19+
"description": "The Consumer Key value for the Auth Provider",
20+
"type": "string"
21+
}
22+
},
23+
"additionalProperties": false
24+
}
25+
},
26+
"additionalProperties": false
27+
}

src/plugins/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ActivitySettings as activitySettings } from './activity-settings/index.js';
2+
import { AuthProviders as authProviders } from './auth-providers/index.js';
23
import { CompanyInformation as companyInformation } from './company-information/index.js';
34
import { CustomerPortal as customerPortal } from './customer-portal/index.js';
45
import { DensitySettings as densitySettings } from './density-settings/index.js';
@@ -24,6 +25,7 @@ import { UserAccessPolicies as userAccessPolicies } from './user-access-policies
2425

2526
export {
2627
activitySettings,
28+
authProviders,
2729
companyInformation,
2830
customerPortal,
2931
densitySettings,

src/plugins/schema.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"properties": {
88
"settings": {
99
"properties": {
10+
"authProviders": {
11+
"$ref": "./auth-providers/schema.json"
12+
},
1013
"userAccessPolicies": {
1114
"$ref": "./user-access-policies/schema.json"
1215
},

0 commit comments

Comments
 (0)