Skip to content

Commit a98cf07

Browse files
committed
Enable auth provider authentication service
Signed-off-by: Rupesh J <rupesh.j@salesforce.com>
1 parent d912860 commit a98cf07

7 files changed

Lines changed: 179 additions & 22 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { type SalesforceUrlPath } from '../../../browserforce.js';
2+
import { BrowserforcePlugin } from '../../../plugin.js';
3+
4+
// Authentication Service page selectors
5+
const AUTH_SERVICE_EDIT_PATH = '/domainname/EditLogin.apexp';
6+
const AUTH_SERVICE_FORM_SELECTOR = 'form[id="BrandSetup:brandSetupForm"]';
7+
const AUTH_SERVICE_CHECKBOX_SELECTOR = 'input.authOption[type="checkbox"]';
8+
const AUTH_SERVICE_SAVE_BUTTON_SELECTOR = 'input[id$=":Save"]';
9+
10+
export type Config = {
11+
authProviderId: string;
12+
developerName: string;
13+
enabled: boolean;
14+
};
15+
16+
export class AuthenticationService extends BrowserforcePlugin {
17+
public async retrieve(definition: Config): Promise<boolean> {
18+
// Navigate to Authentication Service edit page
19+
await using page = await this.browserforce.openPage(AUTH_SERVICE_EDIT_PATH as SalesforceUrlPath);
20+
const authServiceFrameOrPage = await this.browserforce.waitForSelectorInFrameOrPage(
21+
page,
22+
AUTH_SERVICE_FORM_SELECTOR,
23+
);
24+
25+
// Find the checkbox with value attribute matching the AuthProvider ID
26+
const checkboxLocators = await authServiceFrameOrPage.locator(AUTH_SERVICE_CHECKBOX_SELECTOR).all();
27+
for (const checkboxLocator of checkboxLocators) {
28+
const value = await checkboxLocator.getAttribute('value');
29+
if (definition.authProviderId.includes(value)) {
30+
return await checkboxLocator.isChecked();
31+
}
32+
}
33+
34+
throw new Error(
35+
`Authentication Service checkbox not found for AuthProvider '${definition.developerName}' (ID: ${definition.authProviderId}). ` +
36+
`The AuthProvider may not be available in the Authentication Service configuration.`,
37+
);
38+
}
39+
40+
public async apply(config: Config): Promise<void> {
41+
this.browserforce.logger?.log('enableAuthenticationService', config.enabled.toString());
42+
console.log(`[AuthenticationService] Updating Authentication Service for ${config.developerName} (ID: ${config.authProviderId} Enabled: ${config.enabled})`);
43+
44+
// Navigate to Authentication Service edit page
45+
await using page = await this.browserforce.openPage(AUTH_SERVICE_EDIT_PATH as SalesforceUrlPath);
46+
const authServiceFrameOrPage = await this.browserforce.waitForSelectorInFrameOrPage(
47+
page,
48+
AUTH_SERVICE_FORM_SELECTOR,
49+
);
50+
51+
// Find the checkbox with value attribute matching the AuthProvider ID
52+
const checkboxLocators = await authServiceFrameOrPage.locator(AUTH_SERVICE_CHECKBOX_SELECTOR).all();
53+
let checkboxFound = false;
54+
55+
for (const checkboxLocator of checkboxLocators) {
56+
const value = await checkboxLocator.getAttribute('value');
57+
if (config.authProviderId.includes(value)) {
58+
checkboxFound = true;
59+
const isChecked = await checkboxLocator.isChecked();
60+
61+
if (config.enabled !== isChecked) {
62+
// Check / Uncheck the checkbox
63+
if (config.enabled) {
64+
await checkboxLocator.check();
65+
} else {
66+
await checkboxLocator.uncheck();
67+
}
68+
console.log(`[AuthenticationService] Updated Authentication Service checkbox for ${config.developerName}`);
69+
} else {
70+
console.log(`[AuthenticationService] Authentication Service checkbox already updated for ${config.developerName}`);
71+
}
72+
73+
// Save the changes
74+
await Promise.all([
75+
page.waitForResponse((resp) => new URL(resp.url()).pathname === '/domainname/DomainName.apexp'),
76+
authServiceFrameOrPage.locator(AUTH_SERVICE_SAVE_BUTTON_SELECTOR).first().click(),
77+
]);
78+
79+
console.log(`[AuthenticationService] Saved Authentication Service configuration for ${config.developerName}`);
80+
break;
81+
}
82+
}
83+
84+
if (!checkboxFound) {
85+
throw new Error(
86+
`Authentication Service checkbox not found for AuthProvider '${config.developerName}' (ID: ${config.authProviderId}). ` +
87+
`The AuthProvider may not be available in the Authentication Service configuration.`,
88+
);
89+
}
90+
}
91+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema",
3+
"$id": "https://github.com/amtrack/sfdx-browserforce-plugin/src/plugins/auth-providers/authentication-service/schema.json",
4+
"title": "Enable Authentication Service",
5+
"description": "Enable this Auth Provider in the Authentication Service configuration",
6+
"type": "boolean"
7+
}
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
{
22
"$schema": "../schema.json",
33
"settings": {
4-
"authProviders": {}
4+
"authProviders": {
5+
"TestAuthProvider": {
6+
"consumerSecret": "test-secret-12345",
7+
"consumerKey": "test-key-67890",
8+
"enableAuthenticationService": false
9+
}
10+
}
511
}
612
}
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
{
22
"$schema": "../schema.json",
33
"settings": {
4-
"authProviders": {}
4+
"authProviders": {
5+
"TestAuthProvider": {
6+
"consumerSecret": "test-secret-12345",
7+
"consumerKey": "test-key-67890",
8+
"enableAuthenticationService": true
9+
}
10+
}
511
}
612
}

src/plugins/auth-providers/index.e2e-spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,26 @@ describe(AuthProviders.name, function () {
3737
TestAuthProvider: {},
3838
};
3939

40+
const configWithAuthenticationService: Config = {
41+
TestAuthProvider: {
42+
enableAuthenticationService: true,
43+
},
44+
};
45+
46+
const configWithDisabledAuthenticationService: Config = {
47+
TestAuthProvider: {
48+
enableAuthenticationService: false,
49+
},
50+
};
51+
52+
const configWithAllProperties: Config = {
53+
TestAuthProvider: {
54+
consumerSecret: 'test-secret-12345',
55+
consumerKey: 'test-key-67890',
56+
enableAuthenticationService: true,
57+
},
58+
};
59+
4060
it('should deploy an AuthProvider for testing', () => {
4161
const sourceDeployCmd = child.spawnSync('sf', [
4262
'project',
@@ -66,6 +86,16 @@ describe(AuthProviders.name, function () {
6686
await plugin.apply(configEmpty);
6787
});
6888

89+
it('should enable authentication service', async () => {
90+
await plugin.apply(configWithAuthenticationService);
91+
// Note: retrieve() returns empty config, so we can only verify apply completes without errors
92+
});
93+
94+
it('should update consumerSecret, consumerKey, and enable authentication service together', async () => {
95+
await plugin.apply(configWithAllProperties);
96+
// Note: retrieve() returns empty config, so we can only verify apply completes without errors
97+
});
98+
6999
it('should throw an error when AuthProvider does not exist', async () => {
70100
const configInvalid: Config = {
71101
NonExistentAuthProvider: {
@@ -84,6 +114,11 @@ describe(AuthProviders.name, function () {
84114
}, /No AuthProviders found with DeveloperNames/);
85115
});
86116

117+
it('should disable authentication service', async () => {
118+
await plugin.apply(configWithDisabledAuthenticationService);
119+
// Note: retrieve() returns empty config, so we can only verify apply completes without errors
120+
});
121+
87122
it('should remove the testing AuthProvider', async () => {
88123
await global.browserforce.connection.metadata.delete('AuthProvider', ['TestAuthProvider']);
89124
});

src/plugins/auth-providers/index.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type SalesforceUrlPath, waitForPageErrors } from '../../browserforce.js';
22
import { BrowserforcePlugin } from '../../plugin.js';
3+
import { AuthenticationService } from './authentication-service/index.js';
34

45
const CONSUMER_SECRET_SELECTOR = '#ConsumerSecret';
56
const CONSUMER_KEY_SELECTOR = '#ConsumerKey';
@@ -10,6 +11,7 @@ const getUrl = (orgId: string): SalesforceUrlPath => `/${orgId}/e` as Salesforce
1011
type AuthProviderConfig = {
1112
consumerSecret?: string;
1213
consumerKey?: string;
14+
enableAuthenticationService?: boolean;
1315
};
1416

1517
export type Config = {
@@ -66,24 +68,23 @@ export class AuthProviders extends BrowserforcePlugin {
6668
throw new Error(`AuthProvider with DeveloperName '${developerName}' not found`);
6769
}
6870

69-
// Navigate to the edit page
70-
const editPageUrl = getUrl(authProviderId);
71-
72-
this.browserforce.logger?.log('editPageUrl', editPageUrl);
73-
console.log(`[AuthProviders] Navigating to edit page for ${developerName}: ${editPageUrl}`);
74-
75-
await using page = await this.browserforce.openPage(editPageUrl);
71+
try {
72+
// Check if there are updates to consumerSecret or consumerKey
73+
const hasConsumerUpdates = authProviderConfig.consumerSecret !== undefined || authProviderConfig.consumerKey !== undefined;
7674

77-
// Wait for the page/frame to load - handle both Lightning (iframe) and Classic UI
78-
// Use ConsumerSecret as the selector to wait for, or fallback to ConsumerKey
79-
const formSelector = `${CONSUMER_SECRET_SELECTOR}, ${CONSUMER_KEY_SELECTOR}`;
80-
const frameOrPage = await this.browserforce.waitForSelectorInFrameOrPage(page, formSelector);
75+
if (hasConsumerUpdates) {
76+
// Navigate to the edit page
77+
const editPageUrl = getUrl(authProviderId);
8178

82-
try {
83-
// Check if there's anything to update
84-
const hasUpdates = authProviderConfig.consumerSecret !== undefined || authProviderConfig.consumerKey !== undefined;
79+
this.browserforce.logger?.log('editPageUrl', editPageUrl);
80+
console.log(`[AuthProviders] Navigating to edit page for ${developerName}: ${editPageUrl}`);
81+
82+
await using page = await this.browserforce.openPage(editPageUrl);
8583

86-
if (hasUpdates) {
84+
// Wait for the page/frame to load - handle both Lightning (iframe) and Classic UI
85+
// Use ConsumerSecret as the selector to wait for, or fallback to ConsumerKey
86+
const formSelector = `${CONSUMER_SECRET_SELECTOR}, ${CONSUMER_KEY_SELECTOR}`;
87+
const frameOrPage = await this.browserforce.waitForSelectorInFrameOrPage(page, formSelector);
8788
// Update ConsumerSecret if provided
8889
if (authProviderConfig.consumerSecret !== undefined) {
8990
await frameOrPage.locator(CONSUMER_SECRET_SELECTOR).waitFor({ timeout: 10000 });
@@ -131,9 +132,19 @@ export class AuthProviders extends BrowserforcePlugin {
131132
await waitForPageErrors(page);
132133
}
133134
}
135+
136+
// Handle enableAuthenticationService if requested
137+
if (authProviderConfig.enableAuthenticationService !== undefined) {
138+
const pluginAuthenticationService = new AuthenticationService(this.browserforce);
139+
await pluginAuthenticationService.apply({
140+
authProviderId,
141+
developerName,
142+
enabled: authProviderConfig.enableAuthenticationService,
143+
});
144+
}
134145
} catch (error) {
135146
throw new Error(`Failed to update AuthProvider '${developerName}': ${error.message}`);
136-
}
147+
}
137148
}
138149
}
139150
}

src/plugins/auth-providers/schema.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
"title": "Consumer Key",
1919
"description": "The Consumer Key value for the Auth Provider",
2020
"type": "string"
21+
},
22+
"enableAuthenticationService": {
23+
"$ref": "./authentication-service/schema.json"
2124
}
22-
},
23-
"additionalProperties": false
25+
}
2426
}
25-
},
26-
"additionalProperties": false
27+
}
2728
}

0 commit comments

Comments
 (0)