@salesforce/webapp-template-app-react-template-b2e-experimental 1.36.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +82 -0
- package/dist/.a4drules/build-validation.md +81 -0
- package/dist/.a4drules/code-quality.md +150 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-explore-graphql-schema.md +227 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-mutationquery.md +212 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-generate-graphql-readquery.md +185 -0
- package/dist/.a4drules/graphql/tools/knowledge/lds-guide-graphql.md +205 -0
- package/dist/.a4drules/graphql/tools/schemas/shared.graphqls +1150 -0
- package/dist/.a4drules/graphql.md +408 -0
- package/dist/.a4drules/images.md +13 -0
- package/dist/.a4drules/react.md +361 -0
- package/dist/.a4drules/react_image_processing.md +45 -0
- package/dist/.a4drules/skills/install-feature/SKILL.md +67 -0
- package/dist/.a4drules/skills/install-feature/scripts/copy-feature-assets.sh +36 -0
- package/dist/.a4drules/typescript.md +224 -0
- package/dist/.forceignore +15 -0
- package/dist/.husky/pre-commit +4 -0
- package/dist/.prettierignore +11 -0
- package/dist/.prettierrc +17 -0
- package/dist/CHANGELOG.md +533 -0
- package/dist/README.md +18 -0
- package/dist/config/project-scratch-def.json +13 -0
- package/dist/force-app/main/default/classes/WebAppAuthUtils.cls +68 -0
- package/dist/force-app/main/default/classes/WebAppAuthUtils.cls-meta.xml +5 -0
- package/dist/force-app/main/default/classes/WebAppChangePassword.cls +77 -0
- package/dist/force-app/main/default/classes/WebAppChangePassword.cls-meta.xml +5 -0
- package/dist/force-app/main/default/classes/WebAppForgotPassword.cls +71 -0
- package/dist/force-app/main/default/classes/WebAppForgotPassword.cls-meta.xml +5 -0
- package/dist/force-app/main/default/classes/WebAppLogin.cls +97 -0
- package/dist/force-app/main/default/classes/WebAppLogin.cls-meta.xml +5 -0
- package/dist/force-app/main/default/classes/WebAppRegistration.cls +162 -0
- package/dist/force-app/main/default/classes/WebAppRegistration.cls-meta.xml +5 -0
- package/dist/force-app/main/default/digitalExperienceConfigs/appreacttemplateb2e1.digitalExperienceConfig +8 -0
- package/dist/force-app/main/default/digitalExperiences/site/appreacttemplateb2e1/appreacttemplateb2e1.digitalExperience-meta.xml +11 -0
- package/dist/force-app/main/default/digitalExperiences/site/appreacttemplateb2e1/sfdc_cms__site/appreacttemplateb2e1/_meta.json +5 -0
- package/dist/force-app/main/default/digitalExperiences/site/appreacttemplateb2e1/sfdc_cms__site/appreacttemplateb2e1/content.json +10 -0
- package/dist/force-app/main/default/networks/appreacttemplateb2e.network +60 -0
- package/dist/force-app/main/default/package.xml +20 -0
- package/dist/force-app/main/default/sites/appreacttemplateb2e.site +31 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/.graphqlrc.yml +2 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/.prettierignore +9 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/.prettierrc +11 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/appreacttemplateb2e.webapplication-meta.xml +7 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/build/vite.config.d.ts +2 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/build/vite.config.js +94 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/codegen.yml +12 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/e2e/app.spec.ts +24 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/eslint.config.js +141 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/index.html +13 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/package-lock.json +13980 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/package.json +66 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/playwright.config.ts +24 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/scripts/get-graphql-schema.mjs +68 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/scripts/rewrite-e2e-assets.mjs +23 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/api/graphql-operations-types.ts +129 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/api/utils/accounts.ts +33 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/api/utils/query/highRevenueAccountsQuery.graphql +29 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/app.tsx +16 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/appLayout.tsx +11 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/assets/icons/book.svg +3 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/assets/icons/copy.svg +4 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/assets/icons/rocket.svg +3 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/assets/icons/star.svg +3 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/assets/images/codey-1.png +0 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/assets/images/codey-2.png +0 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/assets/images/codey-3.png +0 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/assets/images/vibe-codey.svg +194 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/alerts/status-alert.tsx +45 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/auth/authentication-route.tsx +21 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/auth/private-route.tsx +36 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/footers/footer-link.tsx +36 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/forms/auth-form.tsx +81 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/forms/submit-button.tsx +49 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/layout/card-layout.tsx +23 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/layout/centered-page-layout.tsx +73 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/layout/loading-page.tsx +46 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/alert.tsx +65 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/button.tsx +54 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/card.tsx +77 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/field.tsx +111 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/index.ts +71 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/input.tsx +19 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/label.tsx +19 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/pagination.tsx +99 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/select.tsx +151 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/skeleton.tsx +7 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/spinner.tsx +26 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/table.tsx +114 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/components/ui/tabs.tsx +115 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/context/AuthContext.tsx +83 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/hooks/form.tsx +116 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/lib/utils.ts +6 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/navigationMenu.tsx +81 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/About.tsx +12 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/ChangePassword.tsx +98 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/ForgotPassword.tsx +67 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/Home.tsx +12 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/Login.tsx +89 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/NotFound.tsx +18 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/Profile.tsx +146 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/Register.tsx +130 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/ResetPassword.tsx +101 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/pages/new.tsx +10 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/router-utils.tsx +34 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/routes.tsx +88 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/styles/global.css +94 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/utils/authenticationConfig.ts +52 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/utils/helpers.ts +187 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/tsconfig.json +36 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/tsconfig.node.json +13 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/vite-env.d.ts +1 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/vite.config.ts +102 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/vitest-env.d.ts +2 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/vitest.config.ts +11 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/vitest.setup.ts +1 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/webapplication.json +7 -0
- package/dist/jest.config.js +6 -0
- package/dist/package.json +38 -0
- package/dist/scripts/apex/hello.apex +10 -0
- package/dist/scripts/soql/account.soql +6 -0
- package/dist/sfdx-project.json +12 -0
- package/package.json +37 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Applications REST endpoint for authenticated user password changes.
|
|
3
|
+
*
|
|
4
|
+
* Endpoint: POST /services/apexrest/auth/change-password
|
|
5
|
+
*
|
|
6
|
+
* Allows authenticated users to change their password by providing their current password
|
|
7
|
+
* and a new password. The new password is validated against org password policies.
|
|
8
|
+
*
|
|
9
|
+
* Security: Uses 'with sharing' to enforce user-level security. Only authenticated users
|
|
10
|
+
* can change their own password.
|
|
11
|
+
*/
|
|
12
|
+
@RestResource(urlMapping='/auth/change-password')
|
|
13
|
+
global with sharing class WebAppChangePassword {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Changes the password for the currently authenticated user.
|
|
17
|
+
*
|
|
18
|
+
* @param newPassword The new password to set.
|
|
19
|
+
* @param currentPassword The user's current password for verification.
|
|
20
|
+
* @return SuccessPasswordChangeResponse on success, ErrorPasswordChangeResponse on failure.
|
|
21
|
+
*/
|
|
22
|
+
@HttpPost
|
|
23
|
+
global static PasswordChangeResponse changePassword(String newPassword, String currentPassword) {
|
|
24
|
+
Savepoint sp = Database.setSavepoint();
|
|
25
|
+
try {
|
|
26
|
+
// newPassword and confirmPassword should be validated to be the same value on the client
|
|
27
|
+
Site.changePassword(newPassword, newPassword, currentPassword);
|
|
28
|
+
return new SuccessPasswordChangeResponse();
|
|
29
|
+
} catch (Exception ex) {
|
|
30
|
+
Database.rollback(sp);
|
|
31
|
+
|
|
32
|
+
// System.SecurityException is a user error but treat others as system errors
|
|
33
|
+
if (ex instanceof System.SecurityException) {
|
|
34
|
+
RestContext.response.statusCode = 400;
|
|
35
|
+
return new ErrorPasswordChangeResponse(ex.getMessage());
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Logs are only captured if a Trace Flag is active for the user
|
|
39
|
+
WebAppAuthUtils.debugLog(ex, LoggingLevel.ERROR);
|
|
40
|
+
|
|
41
|
+
RestContext.response.statusCode = 500;
|
|
42
|
+
return new ErrorPasswordChangeResponse('Password change failed');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Base response class for password change operations. */
|
|
47
|
+
global abstract class PasswordChangeResponse {
|
|
48
|
+
|
|
49
|
+
/** Success or failure of the password change operation */
|
|
50
|
+
protected final Boolean success;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Success response for password change. */
|
|
54
|
+
global class SuccessPasswordChangeResponse extends PasswordChangeResponse {
|
|
55
|
+
|
|
56
|
+
/** Constructs a success response. */
|
|
57
|
+
private SuccessPasswordChangeResponse() {
|
|
58
|
+
this.success = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Error response for password change. */
|
|
63
|
+
global class ErrorPasswordChangeResponse extends PasswordChangeResponse {
|
|
64
|
+
|
|
65
|
+
/** Error message describing the failure reason */
|
|
66
|
+
private final String error;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Constructs an error response with the specified error message.
|
|
70
|
+
* @param error The error message to return to the client.
|
|
71
|
+
*/
|
|
72
|
+
private ErrorPasswordChangeResponse(String error) {
|
|
73
|
+
this.success = false;
|
|
74
|
+
this.error = error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST endpoint for Web Applications forgot password functionality.
|
|
3
|
+
*
|
|
4
|
+
* Endpoint: POST /services/apexrest/auth/forgot-password
|
|
5
|
+
*
|
|
6
|
+
* Sends a password reset email to the user's registered email address.
|
|
7
|
+
*/
|
|
8
|
+
@RestResource(urlMapping='/auth/forgot-password')
|
|
9
|
+
global with sharing class WebAppForgotPassword {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Initiates the forgot password process by sending a reset email.
|
|
13
|
+
* @param email User's email address (used as username).
|
|
14
|
+
* @return ForgotPasswordResponse with success status or error message.
|
|
15
|
+
*/
|
|
16
|
+
@HttpPost
|
|
17
|
+
global static ForgotPasswordResponse forgotPassword(String username) {
|
|
18
|
+
try {
|
|
19
|
+
username = username.trim().toLowerCase();
|
|
20
|
+
|
|
21
|
+
if (String.isBlank(username)) {
|
|
22
|
+
RestContext.response.statusCode = 400;
|
|
23
|
+
return new ErrorForgotPasswordResponse('Username is required.');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// [Dev Note] This will send a password reset link to the user's email address
|
|
27
|
+
// if the username exists.
|
|
28
|
+
Site.forgotPassword(username);
|
|
29
|
+
|
|
30
|
+
return new SuccessForgotPasswordResponse();
|
|
31
|
+
} catch (Exception ex) {
|
|
32
|
+
// Logs are only captured if a Trace Flag is active for the user
|
|
33
|
+
WebAppAuthUtils.debugLog(ex, LoggingLevel.ERROR);
|
|
34
|
+
|
|
35
|
+
RestContext.response.statusCode = 500;
|
|
36
|
+
return new ErrorForgotPasswordResponse('Could not send password reset link.');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Base response class for forgot password operations. */
|
|
41
|
+
global abstract class ForgotPasswordResponse {
|
|
42
|
+
|
|
43
|
+
/** Success or failure of the forgot password operation */
|
|
44
|
+
protected final Boolean success;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Success response for forgot password. */
|
|
48
|
+
global class SuccessForgotPasswordResponse extends ForgotPasswordResponse {
|
|
49
|
+
|
|
50
|
+
/** Constructs a success response. */
|
|
51
|
+
private SuccessForgotPasswordResponse() {
|
|
52
|
+
this.success = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Error response for forgot password. */
|
|
57
|
+
global class ErrorForgotPasswordResponse extends ForgotPasswordResponse {
|
|
58
|
+
|
|
59
|
+
/** Error message describing the failure reason */
|
|
60
|
+
private final String error;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Constructs an error response with the specified error message.
|
|
64
|
+
* @param error The error message to return to the client.
|
|
65
|
+
*/
|
|
66
|
+
private ErrorForgotPasswordResponse(String error) {
|
|
67
|
+
this.success = false;
|
|
68
|
+
this.error = error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST endpoint for Web Applications user login.
|
|
3
|
+
*
|
|
4
|
+
* Endpoint: POST /services/apexrest/auth/login
|
|
5
|
+
*
|
|
6
|
+
* Authenticates a user and returns a redirect URL for session creation.
|
|
7
|
+
*
|
|
8
|
+
* @see https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_classes_sites.htm#apex_System_Site_login
|
|
9
|
+
*/
|
|
10
|
+
@RestResource(urlMapping='/auth/login')
|
|
11
|
+
global with sharing class WebAppLogin {
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Authenticates a user and returns a redirect URL.
|
|
15
|
+
* @param email User's email address (used as username).
|
|
16
|
+
* @param password User's password.
|
|
17
|
+
* @param startUrl Optional URL to redirect to after login.
|
|
18
|
+
* @return LoginResponse with redirectUrl on success, or error message on failure.
|
|
19
|
+
*/
|
|
20
|
+
@HttpPost
|
|
21
|
+
global static LoginResponse doLogin(String email, String password, String startUrl) {
|
|
22
|
+
try {
|
|
23
|
+
validateInput(email, password);
|
|
24
|
+
|
|
25
|
+
String username = email.trim().toLowerCase();
|
|
26
|
+
String sanitizedStartUrl = WebAppAuthUtils.getSanitizedStartUrl(startUrl, Site.getPathPrefix());
|
|
27
|
+
PageReference loginResult = Site.login(username, password, sanitizedStartUrl);
|
|
28
|
+
|
|
29
|
+
if (loginResult != null) {
|
|
30
|
+
return new SuccessLoginResponse(loginResult.getUrl());
|
|
31
|
+
} else {
|
|
32
|
+
RestContext.response.statusCode = 401;
|
|
33
|
+
return new ErrorLoginResponse(
|
|
34
|
+
'Your login attempt has failed. Make sure the username and password are correct.'
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
} catch (WebAppAuthUtils.AuthException ex) {
|
|
38
|
+
RestContext.response.statusCode = ex.statusCode;
|
|
39
|
+
return new ErrorLoginResponse(ex.messages);
|
|
40
|
+
} catch (Exception ex) {
|
|
41
|
+
RestContext.response.statusCode = 500;
|
|
42
|
+
return new ErrorLoginResponse('An unexpected error occurred. Please contact your administrator.');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Validates login input fields.
|
|
48
|
+
* @param email The email to validate.
|
|
49
|
+
* @param password The password to validate.
|
|
50
|
+
* @throws WebAppAuthUtils.AuthException if validation fails.
|
|
51
|
+
*/
|
|
52
|
+
private static void validateInput(String email, String password) {
|
|
53
|
+
List<String> errors = new List<String>();
|
|
54
|
+
if (String.isBlank(email)) {
|
|
55
|
+
errors.add('Email is required.');
|
|
56
|
+
}
|
|
57
|
+
if (String.isBlank(password)) {
|
|
58
|
+
errors.add('Password is required.');
|
|
59
|
+
}
|
|
60
|
+
if (!errors.isEmpty()) {
|
|
61
|
+
throw new WebAppAuthUtils.AuthException(400, errors);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Base response class for login.
|
|
67
|
+
*/
|
|
68
|
+
global abstract class LoginResponse {
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Success response with redirect URL.
|
|
73
|
+
*/
|
|
74
|
+
global class SuccessLoginResponse extends LoginResponse {
|
|
75
|
+
global Boolean success = true;
|
|
76
|
+
global String redirectUrl;
|
|
77
|
+
|
|
78
|
+
global SuccessLoginResponse(String redirectUrl) {
|
|
79
|
+
this.redirectUrl = redirectUrl;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Error response with error messages.
|
|
85
|
+
*/
|
|
86
|
+
global class ErrorLoginResponse extends LoginResponse {
|
|
87
|
+
global List<String> errors;
|
|
88
|
+
|
|
89
|
+
global ErrorLoginResponse(List<String> errors) {
|
|
90
|
+
this.errors = new List<String>(errors);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
global ErrorLoginResponse(String error) {
|
|
94
|
+
this.errors = new List<String>{ error };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST endpoint for Web Applications for user self-registration.
|
|
3
|
+
*
|
|
4
|
+
* Endpoint: POST /services/apexrest/auth/register/
|
|
5
|
+
*
|
|
6
|
+
* Creates a new external user account, validates credentials against org password policy,
|
|
7
|
+
* and returns a login URL for automatic session creation.
|
|
8
|
+
*
|
|
9
|
+
* Security: Uses 'without sharing' to allow guest users to check for duplicate usernames.
|
|
10
|
+
*/
|
|
11
|
+
@RestResource(urlMapping='/auth/register')
|
|
12
|
+
global without sharing class WebAppRegistration {
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Registers a new external user and logs them in.
|
|
16
|
+
* @param request The registration request containing user details.
|
|
17
|
+
* @return SuccessResponse or ErrorResponse.
|
|
18
|
+
*/
|
|
19
|
+
@HttpPost
|
|
20
|
+
global static RegistrationResponse registerUser(RegistrationRequest request) {
|
|
21
|
+
Savepoint sp = Database.setSavepoint();
|
|
22
|
+
try {
|
|
23
|
+
request.validate();
|
|
24
|
+
|
|
25
|
+
User u = new User(
|
|
26
|
+
Username = request.email,
|
|
27
|
+
Email = request.email,
|
|
28
|
+
FirstName = request.firstName,
|
|
29
|
+
LastName = request.lastName,
|
|
30
|
+
// Generate unique nickname: first initial + up to 20 chars of last name + 4 random digits
|
|
31
|
+
CommunityNickname = request.firstName.left(1) + request.lastName.left(20) +
|
|
32
|
+
String.valueOf(Crypto.getRandomInteger()).left(4)
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
validatePassword(u, request.password);
|
|
36
|
+
createUser(u, request.password);
|
|
37
|
+
|
|
38
|
+
String startUrl = WebAppAuthUtils.getSanitizedStartUrl(request.startUrl, Site.getPathPrefix());
|
|
39
|
+
PageReference pageRef = Site.login(request.email, request.password, startUrl);
|
|
40
|
+
|
|
41
|
+
return new SuccessRegistrationResponse(pageRef?.getUrl());
|
|
42
|
+
} catch (WebAppAuthUtils.AuthException ex) {
|
|
43
|
+
Database.rollback(sp);
|
|
44
|
+
RestContext.response.statusCode = ex.statusCode;
|
|
45
|
+
return new ErrorRegistrationResponse(ex.messages);
|
|
46
|
+
} catch (Exception ex) {
|
|
47
|
+
Database.rollback(sp);
|
|
48
|
+
RestContext.response.statusCode = 500;
|
|
49
|
+
return new ErrorRegistrationResponse(ex.getMessage());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Base response class for registration.
|
|
55
|
+
*/
|
|
56
|
+
global abstract class RegistrationResponse {
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Success response with redirect URL.
|
|
61
|
+
*/
|
|
62
|
+
global class SuccessRegistrationResponse extends RegistrationResponse {
|
|
63
|
+
global Boolean success = true;
|
|
64
|
+
global String redirectUrl;
|
|
65
|
+
|
|
66
|
+
global SuccessRegistrationResponse(String redirectUrl) {
|
|
67
|
+
this.redirectUrl = redirectUrl;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Error response with error messages.
|
|
73
|
+
*/
|
|
74
|
+
global class ErrorRegistrationResponse extends RegistrationResponse {
|
|
75
|
+
global List<String> errors;
|
|
76
|
+
|
|
77
|
+
global ErrorRegistrationResponse(List<String> errors) {
|
|
78
|
+
this.errors = new List<String>(errors);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
global ErrorRegistrationResponse(String error) {
|
|
82
|
+
this.errors = new List<String>{ error };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Request payload for user registration.
|
|
88
|
+
*/
|
|
89
|
+
global class RegistrationRequest {
|
|
90
|
+
global String email;
|
|
91
|
+
global String firstName;
|
|
92
|
+
global String lastName;
|
|
93
|
+
global String password;
|
|
94
|
+
global String startUrl;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Trims fields and validates required fields and business rules.
|
|
98
|
+
* @throws WebAppAuthUtils.AuthException if validation fails.
|
|
99
|
+
*/
|
|
100
|
+
public void validate() {
|
|
101
|
+
email = email?.trim()?.toLowerCase();
|
|
102
|
+
firstName = firstName?.trim();
|
|
103
|
+
lastName = lastName?.trim();
|
|
104
|
+
startUrl = startUrl?.trim();
|
|
105
|
+
|
|
106
|
+
List<String> errors = new List<String>();
|
|
107
|
+
if (String.isBlank(firstName)) { errors.add('First name is required.'); }
|
|
108
|
+
if (String.isBlank(lastName)) { errors.add('Last name is required.'); }
|
|
109
|
+
if (String.isBlank(password)) { errors.add('Password is required.'); }
|
|
110
|
+
if (!Site.isValidUserName(email)) { errors.add('Email is invalid.'); }
|
|
111
|
+
else if (!isUserUnique(email)) {
|
|
112
|
+
errors.add('A user with this email already exists.');
|
|
113
|
+
}
|
|
114
|
+
if (!errors.isEmpty()) {
|
|
115
|
+
throw new WebAppAuthUtils.AuthException(400, errors);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Validates password against org password policy.
|
|
122
|
+
* @param user The user record.
|
|
123
|
+
* @param password The password to validate.
|
|
124
|
+
* @throws WebAppAuthUtils.AuthException if password does not meet requirements.
|
|
125
|
+
*/
|
|
126
|
+
private static void validatePassword(User user, String password) {
|
|
127
|
+
try {
|
|
128
|
+
Site.validatePassword(user, password, password);
|
|
129
|
+
} catch (System.SecurityException ex) {
|
|
130
|
+
throw new WebAppAuthUtils.AuthException(400, ex.getMessage());
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Creates the external user account.
|
|
136
|
+
* @param u The user record.
|
|
137
|
+
* @param password The password for the new user.
|
|
138
|
+
* @return The new user's ID.
|
|
139
|
+
* @throws WebAppAuthUtils.AuthException if user creation fails.
|
|
140
|
+
*/
|
|
141
|
+
private static String createUser(User u, String password) {
|
|
142
|
+
String userId;
|
|
143
|
+
try {
|
|
144
|
+
userId = Site.createExternalUser(u, null, password);
|
|
145
|
+
} catch (Site.ExternalUserCreateException ex) {
|
|
146
|
+
throw new WebAppAuthUtils.AuthException(500, ex.getDisplayMessages());
|
|
147
|
+
}
|
|
148
|
+
if (userId == null) {
|
|
149
|
+
throw new WebAppAuthUtils.AuthException(500, 'Could not register new user.');
|
|
150
|
+
}
|
|
151
|
+
return userId;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Checks if a username is unique (not already taken).
|
|
156
|
+
* @param username The username to check.
|
|
157
|
+
* @return {@code true} if unique, {@code false} if already exists.
|
|
158
|
+
*/
|
|
159
|
+
private static Boolean isUserUnique(String username) {
|
|
160
|
+
return [SELECT Id FROM User WHERE Username = :username LIMIT 1].isEmpty();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<DigitalExperienceConfig xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
3
|
+
<label>appreacttemplateb2e</label>
|
|
4
|
+
<site>
|
|
5
|
+
<urlPathPrefix>appreacttemplateb2e</urlPathPrefix>
|
|
6
|
+
</site>
|
|
7
|
+
<space>site/appreacttemplateb2e1</space>
|
|
8
|
+
</DigitalExperienceConfig>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<DigitalExperienceBundle xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
3
|
+
<digitalExperienceFolderShares/>
|
|
4
|
+
<label>appreacttemplateb2e1</label>
|
|
5
|
+
<modules>
|
|
6
|
+
<module>
|
|
7
|
+
<fullyQualifiedName>sfdc_cms__collection</fullyQualifiedName>
|
|
8
|
+
<status>ENABLED</status>
|
|
9
|
+
</module>
|
|
10
|
+
</modules>
|
|
11
|
+
</DigitalExperienceBundle>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<Network xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
3
|
+
<allowInternalUserLogin>false</allowInternalUserLogin>
|
|
4
|
+
<allowMembersToFlag>false</allowMembersToFlag>
|
|
5
|
+
<changePasswordTemplate>unfiled$public/CommunityChangePasswordEmailTemplate</changePasswordTemplate>
|
|
6
|
+
<communityRoles/>
|
|
7
|
+
<disableReputationRecordConversations>true</disableReputationRecordConversations>
|
|
8
|
+
<emailSenderAddress>trialorgfarmforu@salesforce.com.invalid.invalid</emailSenderAddress>
|
|
9
|
+
<emailSenderName>appreacttemplateb2e</emailSenderName>
|
|
10
|
+
<embeddedLoginEnabled>false</embeddedLoginEnabled>
|
|
11
|
+
<enableApexCDNCaching>true</enableApexCDNCaching>
|
|
12
|
+
<enableCustomVFErrorPageOverrides>false</enableCustomVFErrorPageOverrides>
|
|
13
|
+
<enableDirectMessages>true</enableDirectMessages>
|
|
14
|
+
<enableExpFriendlyUrlsAsDefault>false</enableExpFriendlyUrlsAsDefault>
|
|
15
|
+
<enableExperienceBundleBasedSnaOverrideEnabled>true</enableExperienceBundleBasedSnaOverrideEnabled>
|
|
16
|
+
<enableGuestChatter>false</enableGuestChatter>
|
|
17
|
+
<enableGuestFileAccess>false</enableGuestFileAccess>
|
|
18
|
+
<enableGuestMemberVisibility>false</enableGuestMemberVisibility>
|
|
19
|
+
<enableImageOptimizationCDN>true</enableImageOptimizationCDN>
|
|
20
|
+
<enableInvitation>false</enableInvitation>
|
|
21
|
+
<enableKnowledgeable>false</enableKnowledgeable>
|
|
22
|
+
<enableLWRExperienceConnectedApp>false</enableLWRExperienceConnectedApp>
|
|
23
|
+
<enableMemberVisibility>false</enableMemberVisibility>
|
|
24
|
+
<enableNicknameDisplay>true</enableNicknameDisplay>
|
|
25
|
+
<enablePrivateMessages>false</enablePrivateMessages>
|
|
26
|
+
<enableReputation>false</enableReputation>
|
|
27
|
+
<enableShowAllNetworkSettings>false</enableShowAllNetworkSettings>
|
|
28
|
+
<enableSiteAsContainer>true</enableSiteAsContainer>
|
|
29
|
+
<enableTalkingAboutStats>true</enableTalkingAboutStats>
|
|
30
|
+
<enableTopicAssignmentRules>true</enableTopicAssignmentRules>
|
|
31
|
+
<enableTopicSuggestions>false</enableTopicSuggestions>
|
|
32
|
+
<enableUpDownVote>false</enableUpDownVote>
|
|
33
|
+
<forgotPasswordTemplate>unfiled$public/CommunityForgotPasswordEmailTemplate</forgotPasswordTemplate>
|
|
34
|
+
<gatherCustomerSentimentData>false</gatherCustomerSentimentData>
|
|
35
|
+
<headlessForgotPasswordTemplate>unfiled$public/CommunityHeadlessForgotPasswordTemplate</headlessForgotPasswordTemplate>
|
|
36
|
+
<headlessRegistrationTemplate>unfiled$public/CommunityHeadlessRegistrationTemplate</headlessRegistrationTemplate>
|
|
37
|
+
<networkMemberGroups>
|
|
38
|
+
<profile>admin</profile>
|
|
39
|
+
</networkMemberGroups>
|
|
40
|
+
<networkPageOverrides>
|
|
41
|
+
<changePasswordPageOverrideSetting>Standard</changePasswordPageOverrideSetting>
|
|
42
|
+
<forgotPasswordPageOverrideSetting>Designer</forgotPasswordPageOverrideSetting>
|
|
43
|
+
<homePageOverrideSetting>Designer</homePageOverrideSetting>
|
|
44
|
+
<loginPageOverrideSetting>Designer</loginPageOverrideSetting>
|
|
45
|
+
<selfRegProfilePageOverrideSetting>Designer</selfRegProfilePageOverrideSetting>
|
|
46
|
+
</networkPageOverrides>
|
|
47
|
+
<newSenderAddress>trialorgfarmforu@salesforce.com</newSenderAddress>
|
|
48
|
+
<picassoSite>appreacttemplateb2e1</picassoSite>
|
|
49
|
+
<selfRegistration>false</selfRegistration>
|
|
50
|
+
<sendWelcomeEmail>true</sendWelcomeEmail>
|
|
51
|
+
<site>appreacttemplateb2e</site>
|
|
52
|
+
<siteArchiveStatus>NotArchived</siteArchiveStatus>
|
|
53
|
+
<status>Live</status>
|
|
54
|
+
<tabs>
|
|
55
|
+
<defaultTab>home</defaultTab>
|
|
56
|
+
<standardTab>Chatter</standardTab>
|
|
57
|
+
</tabs>
|
|
58
|
+
<urlPathPrefix>appreacttemplateb2evforcesite</urlPathPrefix>
|
|
59
|
+
<welcomeTemplate>unfiled$public/CommunityWelcomeEmailTemplate</welcomeTemplate>
|
|
60
|
+
</Network>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
3
|
+
<types>
|
|
4
|
+
<members>appreacttemplateb2e</members>
|
|
5
|
+
<name>CustomSite</name>
|
|
6
|
+
</types>
|
|
7
|
+
<types>
|
|
8
|
+
<members>site/appreacttemplateb2e1</members>
|
|
9
|
+
<name>DigitalExperienceBundle</name>
|
|
10
|
+
</types>
|
|
11
|
+
<types>
|
|
12
|
+
<members>appreacttemplateb2e1</members>
|
|
13
|
+
<name>DigitalExperienceConfig</name>
|
|
14
|
+
</types>
|
|
15
|
+
<types>
|
|
16
|
+
<members>appreacttemplateb2e</members>
|
|
17
|
+
<name>Network</name>
|
|
18
|
+
</types>
|
|
19
|
+
<version>66.0</version>
|
|
20
|
+
</Package>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<CustomSite xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
3
|
+
<active>true</active>
|
|
4
|
+
<allowGuestPaymentsApi>false</allowGuestPaymentsApi>
|
|
5
|
+
<allowHomePage>false</allowHomePage>
|
|
6
|
+
<allowStandardAnswersPages>false</allowStandardAnswersPages>
|
|
7
|
+
<allowStandardIdeasPages>false</allowStandardIdeasPages>
|
|
8
|
+
<allowStandardLookups>false</allowStandardLookups>
|
|
9
|
+
<allowStandardPortalPages>true</allowStandardPortalPages>
|
|
10
|
+
<allowStandardSearch>false</allowStandardSearch>
|
|
11
|
+
<authorizationRequiredPage>CommunitiesLogin</authorizationRequiredPage>
|
|
12
|
+
<bandwidthExceededPage>BandwidthExceeded</bandwidthExceededPage>
|
|
13
|
+
<browserXssProtection>true</browserXssProtection>
|
|
14
|
+
<cachePublicVisualforcePagesInProxyServers>true</cachePublicVisualforcePagesInProxyServers>
|
|
15
|
+
<clickjackProtectionLevel>SameOriginOnly</clickjackProtectionLevel>
|
|
16
|
+
<contentSniffingProtection>true</contentSniffingProtection>
|
|
17
|
+
<enableAuraRequests>true</enableAuraRequests>
|
|
18
|
+
<fileNotFoundPage>FileNotFound</fileNotFoundPage>
|
|
19
|
+
<genericErrorPage>Exception</genericErrorPage>
|
|
20
|
+
<inMaintenancePage>InMaintenance</inMaintenancePage>
|
|
21
|
+
<indexPage>CommunitiesLanding</indexPage>
|
|
22
|
+
<masterLabel>appreacttemplateb2e</masterLabel>
|
|
23
|
+
<redirectToCustomDomain>false</redirectToCustomDomain>
|
|
24
|
+
<referrerPolicyOriginWhenCrossOrigin>true</referrerPolicyOriginWhenCrossOrigin>
|
|
25
|
+
<selfRegPage>CommunitiesSelfReg</selfRegPage>
|
|
26
|
+
<!-- <serverIsDown>SNA_U2rxy_sf_default_cdn_byo1</serverIsDown> -->
|
|
27
|
+
<!-- <siteAdmin>epic.out.d1d3a2d722c8@orgfarm.salesforce.com</siteAdmin> -->
|
|
28
|
+
<!-- <siteGuestRecordDefaultOwner>epic.out.d1d3a2d722c8@orgfarm.salesforce.com</siteGuestRecordDefaultOwner> -->
|
|
29
|
+
<siteType>ChatterNetwork</siteType>
|
|
30
|
+
<urlPathPrefix>appreacttemplateb2evforcesite</urlPathPrefix>
|
|
31
|
+
</CustomSite>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<WebApplication xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
3
|
+
<masterLabel>appreacttemplateb2e</masterLabel>
|
|
4
|
+
<description>A Salesforce web application.</description>
|
|
5
|
+
<isActive>true</isActive>
|
|
6
|
+
<version>1</version>
|
|
7
|
+
</WebApplication>
|