@salesforce/webapp-template-feature-react-authentication-experimental 1.11.2 → 1.13.0

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.
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: install-feature
3
+ description: Install a feature into the current app. Use when the user asks to add a feature, capability, or functionality like authentication, search, charts, or navigation to their React web app.
4
+ ---
5
+
6
+ # Install feature
7
+
8
+ When the user asks to add a feature to their app, follow this workflow.
9
+
10
+ ## 1. Match the request to a feature
11
+
12
+ Available features (npm packages):
13
+
14
+
15
+ | Feature | Package | Description |
16
+ | ---------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------- |
17
+ | **Authentication** | `@salesforce/webapp-template-feature-react-authentication-experimental` | Login, register, password reset, protected routes |
18
+ | **Global search** | `@salesforce/webapp-template-feature-react-global-search-experimental` | Search Salesforce objects with filters and pagination |
19
+ | **Navigation menu** | `@salesforce/webapp-template-feature-react-nav-menu-experimental` | App layout with responsive navigation menu |
20
+ | **Analytics charts** | `@salesforce/webapp-template-feature-react-chart-experimental` | Recharts line/bar charts with theming (AnalyticsChart, ChartContainer) |
21
+ | **Shared UI (shadcn)** | `@salesforce/webapp-template-feature-react-shadcn-experimental` | Button, Card, Input, Select, Table, Tabs, etc. |
22
+
23
+
24
+ If no feature matches, tell the user and offer to build it from scratch following the project's existing patterns.
25
+
26
+ ## 2. Install the npm package
27
+
28
+ ```bash
29
+ npm install <package-name>
30
+ ```
31
+
32
+ ## 3. Copy skills and rules from the package
33
+
34
+ After install, check the package in `node_modules/<package-name>/` for:
35
+
36
+ - `**skills/**` – If it exists, copy each skill folder into the current project's skills directory (e.g. `.cline/skills/` or `.cursor/skills/`).
37
+ - `**rules/**` – If it exists, copy each rule file into the current project's rules directory (e.g. `.clinerules/` or `.cursor/rules/`).
38
+
39
+ Not every package has skills or rules; skip this step if neither directory exists.
40
+
41
+ ## 4. Read the feature's exports and components
42
+
43
+ Read the package's entry point (usually `index.ts` or the `main` field in its `package.json`) to understand what components and types it exports.
44
+
45
+ Also read the `feature.ts` file if it exists — it lists dependencies on other features (e.g. shadcn) and any npm dependencies the feature needs (e.g. `recharts`). Install any missing dependencies.
46
+
47
+ ## 5. Implement the feature in the current app
48
+
49
+ - **If the package exports reusable components** (e.g. `AnalyticsChart`, `ChartContainer`): import and use them directly. Follow the package's README or skill instructions for props, data shapes, and usage patterns.
50
+ - **If the package is a reference implementation** (e.g. authentication pages, search pages): read its source code under `src/` as a reference, then create equivalent pages/components in the current app following the app's existing patterns and conventions.
51
+
52
+ Place new routes inside the existing app's router (e.g. `routes.tsx`). Do not replace the app shell; add the feature as new routes or sections within it.
53
+
54
+ ## 6. Verify
55
+
56
+ - Confirm the app builds without errors.
57
+ - Confirm any new routes are accessible and render correctly.
58
+
package/dist/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.13.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.12.0...v1.13.0) (2026-02-06)
7
+
8
+
9
+ ### Features
10
+
11
+ * adding a sample feature package for reference ([#77](https://github.com/salesforce-experience-platform-emu/webapps/issues/77)) ([94b4d8e](https://github.com/salesforce-experience-platform-emu/webapps/commit/94b4d8e5e89c10888868b8b014c1032ff4b6177d))
12
+
13
+
14
+
15
+
16
+
17
+ # [1.12.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.11.2...v1.12.0) (2026-02-06)
18
+
19
+ **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
20
+
21
+
22
+
23
+
24
+
6
25
  ## [1.11.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.11.1...v1.11.2) (2026-02-06)
7
26
 
8
27
  **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
@@ -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,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>66.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * REST endpoint for Web Applications for user self-registration.
3
- *
3
+ *
4
4
  * Endpoint: POST /services/apexrest/auth/register/
5
- *
5
+ *
6
6
  * Creates a new external user account, validates credentials against org password policy,
7
7
  * and returns a login URL for automatic session creation.
8
- *
8
+ *
9
9
  * Security: Uses 'without sharing' to allow guest users to check for duplicate usernames.
10
10
  */
11
- @RestResource(urlMapping='/auth/register/')
11
+ @RestResource(urlMapping='/auth/register')
12
12
  global without sharing class WebAppRegistration {
13
13
 
14
14
  /**
@@ -21,7 +21,7 @@ global without sharing class WebAppRegistration {
21
21
  Savepoint sp = Database.setSavepoint();
22
22
  try {
23
23
  request.validate();
24
-
24
+
25
25
  User u = new User(
26
26
  Username = request.email,
27
27
  Email = request.email,
@@ -57,13 +57,14 @@ global without sharing class WebAppRegistration {
57
57
  }
58
58
 
59
59
  /**
60
- * Success response with login URL.
60
+ * Success response with redirect URL.
61
61
  */
62
62
  global class SuccessRegistrationResponse extends RegistrationResponse {
63
- global String loginUrl;
64
-
65
- global SuccessRegistrationResponse(String loginUrl) {
66
- this.loginUrl = loginUrl;
63
+ global Boolean success = true;
64
+ global String redirectUrl;
65
+
66
+ global SuccessRegistrationResponse(String redirectUrl) {
67
+ this.redirectUrl = redirectUrl;
67
68
  }
68
69
  }
69
70
 
@@ -72,7 +73,7 @@ global without sharing class WebAppRegistration {
72
73
  */
73
74
  global class ErrorRegistrationResponse extends RegistrationResponse {
74
75
  global List<String> errors;
75
-
76
+
76
77
  global ErrorRegistrationResponse(List<String> errors) {
77
78
  this.errors = new List<String>(errors);
78
79
  }
@@ -7,6 +7,7 @@ import { AuthForm } from "../components/forms/auth-form";
7
7
  import { useAppForm } from "../hooks/form";
8
8
  import { ROUTES } from "../utils/authenticationConfig";
9
9
  import { emailSchema, getStartUrl, handleApiResponse, getErrorMessage } from "../utils/helpers";
10
+ import type { AuthResponse } from "../utils/helpers";
10
11
 
11
12
  const loginSchema = z.object({
12
13
  email: emailSchema,
@@ -26,18 +27,22 @@ export default function Login() {
26
27
  try {
27
28
  // [Dev Note] Salesforce Integration:
28
29
  // We use 'baseClient.post' to make an authenticated (or guest) call to Salesforce.
29
- // "/services/apexrest/auth/login" refers to a custom Apex Class exposed as a REST resource.
30
+ // "/sfciapi/services/apexrest/auth/login" refers to a custom Apex Class exposed as a REST resource.
30
31
  // You must ensure this Apex class exists in your org and handles the login logic
31
32
  // (e.g., creating a session or returning a token).
32
- const response = await baseClient.post("/services/apexrest/auth/login", {
33
+ const response = await baseClient.post("/sfdcapi/services/apexrest/auth/login", {
33
34
  email: value.email.trim().toLowerCase(),
34
35
  password: value.password,
36
+ startUrl: getStartUrl(searchParams),
35
37
  });
36
- await handleApiResponse(response, "Login failed");
37
-
38
- // [Dev Note] After successful API call, redirect to the intended page.
39
- // The session cookie is typically set by the server/Apex response headers.
40
- navigate(getStartUrl(searchParams), { replace: true });
38
+ const result = await handleApiResponse<AuthResponse>(response, "Login failed");
39
+ if (result?.redirectUrl) {
40
+ // Hard navigate to the URL which establishes the server session cookie
41
+ window.location.replace(result.redirectUrl);
42
+ } else {
43
+ // In case redirectUrl is null, navigate to home
44
+ navigate("/", { replace: true });
45
+ }
41
46
  } catch (err) {
42
47
  setSubmitError(getErrorMessage(err, "Login failed"));
43
48
  }
@@ -13,10 +13,7 @@ import {
13
13
  handleApiResponse,
14
14
  getErrorMessage,
15
15
  } from "../utils/helpers";
16
-
17
- interface RegistrationResponse {
18
- loginUrl: string | null;
19
- }
16
+ import type { AuthResponse } from "../utils/helpers";
20
17
 
21
18
  const registerSchema = z
22
19
  .object({
@@ -53,18 +50,15 @@ export default function Register() {
53
50
  // [Dev Note] Calls the custom Apex REST endpoint for user registration.
54
51
  // Ensure your Apex logic handles duplicate checks and user creation (e.g., Site.createExternalUser).
55
52
  const { confirmPassword, ...request } = formFieldValues;
56
- const response = await baseClient.post("/sfdcapi/services/apexrest/auth/register/", {
53
+ const response = await baseClient.post("/sfdcapi/services/apexrest/auth/register", {
57
54
  request,
58
55
  });
59
- const result = await handleApiResponse<RegistrationResponse>(
60
- response,
61
- "Registration failed",
62
- );
63
- if (result?.loginUrl) {
56
+ const result = await handleApiResponse<AuthResponse>(response, "Registration failed");
57
+ if (result?.redirectUrl) {
64
58
  // Hard navigate to the URL which logs the new user in
65
- window.location.replace(result.loginUrl);
59
+ window.location.replace(result.redirectUrl);
66
60
  } else {
67
- // In case loginUrl is null, redirect to the login page
61
+ // In case redirectUrl is null, redirect to the login page
68
62
  navigate(ROUTES.LOGIN.PATH, { replace: true });
69
63
  }
70
64
  } catch (err) {
@@ -112,6 +112,17 @@ export async function handleApiResponse<T = unknown>(
112
112
  return data as T;
113
113
  }
114
114
 
115
+ /**
116
+ * Shared response type for authentication endpoints (login/register).
117
+ * Success responses contain `success: true` and `redirectUrl`.
118
+ * Error responses contain `errors` array.
119
+ */
120
+ export interface AuthResponse {
121
+ success?: boolean;
122
+ redirectUrl?: string | null;
123
+ errors?: string[];
124
+ }
125
+
115
126
  /**
116
127
  * UI API Record response structure.
117
128
  */
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-base-sfdx-project-experimental",
3
- "version": "1.11.2",
3
+ "version": "1.13.0",
4
4
  "description": "Base SFDX project template",
5
5
  "private": true,
6
6
  "files": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-feature-react-authentication-experimental",
3
- "version": "1.11.2",
3
+ "version": "1.13.0",
4
4
  "description": "Authentication feature for web applications",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "author": "",
@@ -19,7 +19,7 @@
19
19
  "watch": "npx tsx ../../cli/src/index.ts watch-patches packages/template/feature/feature-react-authentication packages/template/base-app/base-react-app packages/template/feature/feature-react-authentication/dist"
20
20
  },
21
21
  "devDependencies": {
22
- "@salesforce/webapp-experimental": "^1.11.2",
22
+ "@salesforce/webapp-experimental": "^1.13.0",
23
23
  "@tanstack/react-form": "^1.27.7",
24
24
  "@types/react": "^19.2.7",
25
25
  "@types/react-dom": "^19.2.3",
@@ -27,5 +27,5 @@
27
27
  "react-router": "^7.10.1",
28
28
  "vite": "^7.3.1"
29
29
  },
30
- "gitHead": "d2bb8a908eca2cff75f421769bf8dbb0a36f698f"
30
+ "gitHead": "f5823a67b25f01d2c4510377cb4d2c34e7ace922"
31
31
  }