@salesforce/webapp-template-feature-react-authentication-experimental 1.11.2 → 1.12.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.
package/dist/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
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.12.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.11.2...v1.12.0) (2026-02-06)
7
+
8
+ **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
9
+
10
+
11
+
12
+
13
+
6
14
  ## [1.11.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.11.1...v1.11.2) (2026-02-06)
7
15
 
8
16
  **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.12.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.12.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.12.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": "503a8f0d9d00f1c3dea58b7457dbf288b14a3e5a"
31
31
  }