@salesforce/ui-bundle-template-feature-angular-authentication 12.9.0 → 12.10.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
+ ## [12.10.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v12.9.0...v12.10.0) (2026-10-06)
7
+
8
+ **Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
9
+
10
+
11
+
12
+
13
+
6
14
  ## [12.9.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v12.8.0...v12.9.0) (2026-09-29)
7
15
 
8
16
  **Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
@@ -0,0 +1,145 @@
1
+ /**
2
+ * REST endpoint for Social Login configuration on React Experience Cloud sites.
3
+ *
4
+ * Endpoint: GET /services/apexrest/auth/social-login-config?startURL=/
5
+ *
6
+ * This replicates the behavior of the OOTB community_login/socialLogin LWC:
7
+ * - applauncher.SocialLoginController.getAuthProviders()
8
+ * - applauncher.SocialLoginController.getSamlProviders()
9
+ * - applauncher.SocialLoginController.getCommunityDomainSsoUrl()
10
+ * - applauncher.SocialLoginController.getSamlSsoUrl()
11
+ * - applauncher.SocialLoginController.handleIdp()
12
+ *
13
+ * The OOTB LWC does not render on React (Site Container) sites due to a platform
14
+ * restriction (isSiteContainer check in node-loginsettings.ftl).
15
+ * This endpoint enables a custom React component to achieve the same functionality.
16
+ *
17
+ * @see https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_Auth_AuthConfiguration.htm
18
+ */
19
+ @RestResource(urlMapping='/auth/social-login-config')
20
+ global with sharing class UIBundleSocialLoginConfig {
21
+
22
+ @HttpGet
23
+ global static SocialLoginConfigResponse getConfig() {
24
+ try {
25
+ // Resolve the post-authentication redirect path, sanitized against
26
+ // open-redirect attacks (CWE-601). See resolveStartUrl.
27
+ String startUrl = resolveStartUrl();
28
+
29
+ String communityUrl = Site.getBaseUrl();
30
+ Auth.AuthConfiguration authConfig = new Auth.AuthConfiguration(communityUrl, startUrl);
31
+
32
+ List<AuthProviderInfo> authProviders = new List<AuthProviderInfo>();
33
+ List<SamlProviderInfo> samlProviders = new List<SamlProviderInfo>();
34
+ String autoRedirectUrl = null;
35
+
36
+ // Fetch OAuth Auth Providers (Google, Facebook, etc.)
37
+ List<AuthProvider> rawAuthProviders = authConfig.getAuthProviders();
38
+ for (AuthProvider ap : rawAuthProviders) {
39
+ String ssoUrl = Auth.AuthConfiguration.getAuthProviderSsoDomainUrl(
40
+ communityUrl, startUrl, ap.DeveloperName
41
+ );
42
+ authProviders.add(new AuthProviderInfo(
43
+ ap.DeveloperName,
44
+ ap.FriendlyName,
45
+ ap.IconUrl,
46
+ ssoUrl
47
+ ));
48
+ }
49
+
50
+ // Fetch SAML SSO Providers
51
+ List<SamlSsoConfig> rawSamlProviders = authConfig.getSamlProviders();
52
+ for (SamlSsoConfig saml : rawSamlProviders) {
53
+ String ssoUrl = Auth.AuthConfiguration.getSamlSsoUrl(
54
+ communityUrl, startUrl, saml.Id
55
+ );
56
+ samlProviders.add(new SamlProviderInfo(
57
+ saml.Id,
58
+ saml.MasterLabel,
59
+ ssoUrl
60
+ ));
61
+ }
62
+
63
+ // handleIdp behavior: auto-redirect if exactly one IDP and no username/password login
64
+ Integer totalProviders = rawAuthProviders.size() + rawSamlProviders.size();
65
+ if (totalProviders == 1 && !authConfig.getUsernamePasswordEnabled()) {
66
+ if (!authProviders.isEmpty()) {
67
+ autoRedirectUrl = authProviders[0].ssoUrl;
68
+ } else if (!samlProviders.isEmpty()) {
69
+ autoRedirectUrl = samlProviders[0].ssoUrl;
70
+ }
71
+ }
72
+
73
+ return new SocialLoginConfigResponse(authProviders, samlProviders, autoRedirectUrl);
74
+ } catch (Exception ex) {
75
+ System.debug(LoggingLevel.ERROR, 'UIBundleSocialLoginConfig error: ' + ex.getMessage());
76
+ return new SocialLoginConfigResponse(
77
+ new List<AuthProviderInfo>(),
78
+ new List<SamlProviderInfo>(),
79
+ null
80
+ );
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Resolves the post-authentication redirect path from the request and
86
+ * sanitizes it to prevent open-redirect attacks (CWE-601).
87
+ *
88
+ * Delegates to the shared UIBundleAuthUtils.getSanitizedStartUrl — the same
89
+ * validation used by UIBundleLogin and UIBundleRegistration — which
90
+ * URL-decodes the value (catching encoded bypasses such as %2f%2f) and then
91
+ * rejects absolute, protocol-relative ("//"), backslash, "@", ":" and
92
+ * control-character inputs, falling back to the community path prefix. Only
93
+ * safe, community-relative paths survive, prefixed with Site.getPathPrefix()
94
+ * so the redirect lands on this community rather than the org default.
95
+ */
96
+ @TestVisible
97
+ private static String resolveStartUrl() {
98
+ return UIBundleAuthUtils.getSanitizedStartUrl(
99
+ RestContext.request.params.get('startURL'),
100
+ Site.getPathPrefix()
101
+ );
102
+ }
103
+
104
+ global class SocialLoginConfigResponse {
105
+ global List<AuthProviderInfo> authProviders;
106
+ global List<SamlProviderInfo> samlProviders;
107
+ global String autoRedirectUrl;
108
+
109
+ global SocialLoginConfigResponse(
110
+ List<AuthProviderInfo> authProviders,
111
+ List<SamlProviderInfo> samlProviders,
112
+ String autoRedirectUrl
113
+ ) {
114
+ this.authProviders = authProviders;
115
+ this.samlProviders = samlProviders;
116
+ this.autoRedirectUrl = autoRedirectUrl;
117
+ }
118
+ }
119
+
120
+ global class AuthProviderInfo {
121
+ global String developerName;
122
+ global String friendlyName;
123
+ global String iconUrl;
124
+ global String ssoUrl;
125
+
126
+ global AuthProviderInfo(String developerName, String friendlyName, String iconUrl, String ssoUrl) {
127
+ this.developerName = developerName;
128
+ this.friendlyName = friendlyName;
129
+ this.iconUrl = iconUrl;
130
+ this.ssoUrl = ssoUrl;
131
+ }
132
+ }
133
+
134
+ global class SamlProviderInfo {
135
+ global String id;
136
+ global String masterLabel;
137
+ global String ssoUrl;
138
+
139
+ global SamlProviderInfo(String id, String masterLabel, String ssoUrl) {
140
+ this.id = id;
141
+ this.masterLabel = masterLabel;
142
+ this.ssoUrl = ssoUrl;
143
+ }
144
+ }
145
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>67.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Test class for UIBundleSocialLoginConfig REST endpoint.
3
+ *
4
+ * Auth.AuthConfiguration requires a valid community URL so these tests
5
+ * exercise the error-handling path and verify the response wrapper structure.
6
+ * Integration-level testing of actual Auth Provider/SAML discovery happens
7
+ * in the target org via the React component E2E flow.
8
+ */
9
+ @isTest
10
+ private class UIBundleSocialLoginConfig_Test {
11
+
12
+ /**
13
+ * Test that the endpoint returns an empty config when no community context
14
+ * is available (covers the catch block graceful fallback).
15
+ */
16
+ @isTest
17
+ static void testGetConfigNoSiteContext() {
18
+ RestRequest req = new RestRequest();
19
+ req.requestURI = '/services/apexrest/auth/social-login-config';
20
+ req.httpMethod = 'GET';
21
+ req.params.put('startURL', '/');
22
+ RestContext.request = req;
23
+ RestContext.response = new RestResponse();
24
+
25
+ Test.startTest();
26
+ UIBundleSocialLoginConfig.SocialLoginConfigResponse result =
27
+ UIBundleSocialLoginConfig.getConfig();
28
+ Test.stopTest();
29
+
30
+ // Should return empty lists (graceful fallback), not null
31
+ System.assertNotEquals(null, result, 'Response should not be null');
32
+ System.assertNotEquals(null, result.authProviders, 'authProviders list should not be null');
33
+ System.assertNotEquals(null, result.samlProviders, 'samlProviders list should not be null');
34
+ System.assertEquals(0, result.authProviders.size(), 'authProviders should be empty without community context');
35
+ System.assertEquals(0, result.samlProviders.size(), 'samlProviders should be empty without community context');
36
+ System.assertEquals(null, result.autoRedirectUrl, 'autoRedirectUrl should be null without providers');
37
+ }
38
+
39
+ /**
40
+ * Test that the endpoint handles a missing startURL parameter gracefully
41
+ * (defaults to "/").
42
+ */
43
+ @isTest
44
+ static void testGetConfigNoStartUrl() {
45
+ RestRequest req = new RestRequest();
46
+ req.requestURI = '/services/apexrest/auth/social-login-config';
47
+ req.httpMethod = 'GET';
48
+ // Intentionally not setting startURL param
49
+ RestContext.request = req;
50
+ RestContext.response = new RestResponse();
51
+
52
+ Test.startTest();
53
+ UIBundleSocialLoginConfig.SocialLoginConfigResponse result =
54
+ UIBundleSocialLoginConfig.getConfig();
55
+ Test.stopTest();
56
+
57
+ System.assertNotEquals(null, result, 'Response should not be null even without startURL');
58
+ System.assertNotEquals(null, result.authProviders, 'authProviders should not be null');
59
+ System.assertNotEquals(null, result.samlProviders, 'samlProviders should not be null');
60
+ }
61
+
62
+ /**
63
+ * Test the inner class constructors for code coverage.
64
+ */
65
+ @isTest
66
+ static void testInnerClassConstructors() {
67
+ Test.startTest();
68
+
69
+ UIBundleSocialLoginConfig.AuthProviderInfo authInfo =
70
+ new UIBundleSocialLoginConfig.AuthProviderInfo(
71
+ 'TestProvider', 'Test Provider', 'https://example.com/icon.png', 'https://example.com/sso'
72
+ );
73
+ System.assertEquals('TestProvider', authInfo.developerName);
74
+ System.assertEquals('Test Provider', authInfo.friendlyName);
75
+ System.assertEquals('https://example.com/icon.png', authInfo.iconUrl);
76
+ System.assertEquals('https://example.com/sso', authInfo.ssoUrl);
77
+
78
+ UIBundleSocialLoginConfig.SamlProviderInfo samlInfo =
79
+ new UIBundleSocialLoginConfig.SamlProviderInfo(
80
+ '001000000000001', 'Test SAML', 'https://example.com/saml/sso'
81
+ );
82
+ System.assertEquals('001000000000001', samlInfo.id);
83
+ System.assertEquals('Test SAML', samlInfo.masterLabel);
84
+ System.assertEquals('https://example.com/saml/sso', samlInfo.ssoUrl);
85
+
86
+ List<UIBundleSocialLoginConfig.AuthProviderInfo> authList =
87
+ new List<UIBundleSocialLoginConfig.AuthProviderInfo>{ authInfo };
88
+ List<UIBundleSocialLoginConfig.SamlProviderInfo> samlList =
89
+ new List<UIBundleSocialLoginConfig.SamlProviderInfo>{ samlInfo };
90
+
91
+ UIBundleSocialLoginConfig.SocialLoginConfigResponse resp =
92
+ new UIBundleSocialLoginConfig.SocialLoginConfigResponse(
93
+ authList, samlList, 'https://example.com/redirect'
94
+ );
95
+ System.assertEquals(1, resp.authProviders.size());
96
+ System.assertEquals(1, resp.samlProviders.size());
97
+ System.assertEquals('https://example.com/redirect', resp.autoRedirectUrl);
98
+
99
+ Test.stopTest();
100
+ }
101
+
102
+ // ---- Open-redirect sanitization (CWE-601) -------------------------------
103
+ // resolveStartUrl() is the boundary where the endpoint sanitizes the
104
+ // caller-supplied startURL before it is embedded in the SSO redirect URL.
105
+ // These tests assert malicious values never survive that boundary and that
106
+ // legitimate relative paths do.
107
+
108
+ @isTest
109
+ static void resolveStartUrlRejectsAbsoluteUrl() {
110
+ assertNotReflected('https://evil-attacker.com');
111
+ }
112
+
113
+ @isTest
114
+ static void resolveStartUrlRejectsProtocolRelativeUrl() {
115
+ assertNotReflected('//evil-attacker.com');
116
+ }
117
+
118
+ @isTest
119
+ static void resolveStartUrlRejectsBackslashVariant() {
120
+ assertNotReflected('/\\evil-attacker.com');
121
+ }
122
+
123
+ @isTest
124
+ static void resolveStartUrlRejectsEncodedBypass() {
125
+ assertNotReflected('%2f%2fevil-attacker.com');
126
+ }
127
+
128
+ @isTest
129
+ static void resolveStartUrlAllowsRelativePath() {
130
+ setRequest('/dashboard');
131
+ String resolved = UIBundleSocialLoginConfig.resolveStartUrl();
132
+ System.assertNotEquals(null, resolved, 'A valid relative path should resolve to a non-null value');
133
+ System.assert(
134
+ resolved.endsWith('/dashboard'),
135
+ 'A valid relative path must be preserved (optionally prefixed): ' + resolved
136
+ );
137
+ System.assert(!resolved.startsWith('http'), 'Result must not be absolute: ' + resolved);
138
+ }
139
+
140
+ @isTest
141
+ static void getConfigWithMaliciousStartUrlReturnsGracefulEmpty() {
142
+ // End-to-end: a malicious startURL must not crash the endpoint; without
143
+ // a real site context it returns the graceful empty response (unchanged).
144
+ setRequest('https://evil-attacker.com');
145
+
146
+ Test.startTest();
147
+ UIBundleSocialLoginConfig.SocialLoginConfigResponse result =
148
+ UIBundleSocialLoginConfig.getConfig();
149
+ Test.stopTest();
150
+
151
+ System.assertNotEquals(null, result, 'Response should not be null');
152
+ System.assertEquals(0, result.authProviders.size(), 'No providers without site context');
153
+ System.assertEquals(0, result.samlProviders.size(), 'No SAML providers without site context');
154
+ System.assertEquals(null, result.autoRedirectUrl, 'No auto-redirect without providers');
155
+ }
156
+
157
+ // ---- helpers ------------------------------------------------------------
158
+
159
+ /** Asserts a malicious startURL value is not reflected by resolveStartUrl(). */
160
+ private static void assertNotReflected(String maliciousStartUrl) {
161
+ setRequest(maliciousStartUrl);
162
+ String resolved = UIBundleSocialLoginConfig.resolveStartUrl();
163
+ String safe = resolved == null ? '' : resolved;
164
+ System.assert(!safe.contains('evil-attacker'), 'Malicious host must not be reflected: ' + safe);
165
+ System.assert(!safe.startsWith('http'), 'Result must not be an absolute URL: ' + safe);
166
+ System.assert(!safe.startsWith('//'), 'Result must not be protocol-relative: ' + safe);
167
+ System.assert(!safe.contains('\\'), 'Result must not contain a backslash: ' + safe);
168
+ }
169
+
170
+ /** Builds a GET RestContext with the given startURL param (null => param omitted). */
171
+ private static void setRequest(String startUrlValue) {
172
+ RestRequest req = new RestRequest();
173
+ req.requestURI = '/services/apexrest/auth/social-login-config';
174
+ req.httpMethod = 'GET';
175
+ if (startUrlValue != null) {
176
+ req.params.put('startURL', startUrlValue);
177
+ }
178
+ RestContext.request = req;
179
+ RestContext.response = new RestResponse();
180
+ }
181
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>67.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -28,8 +28,8 @@
28
28
  "@angular/router": "^21.2.0",
29
29
  "@ng-icons/core": "^32.2.0",
30
30
  "@ng-icons/lucide": "^32.2.0",
31
- "@salesforce/angular-plugin-ui-bundle": "^12.9.0",
32
- "@salesforce/platform-sdk": "^12.9.0",
31
+ "@salesforce/angular-plugin-ui-bundle": "^12.10.0",
32
+ "@salesforce/platform-sdk": "^12.10.0",
33
33
  "@spartan-ng/brain": "^1.3.1",
34
34
  "class-variance-authority": "^0.7.1",
35
35
  "clsx": "^2.1.1",
@@ -61,7 +61,7 @@
61
61
  "typescript": "~5.9.2",
62
62
  "typescript-eslint": "^8.0.0",
63
63
  "vitest": "^4.1.11",
64
- "@salesforce/graphiti": "^12.9.0"
64
+ "@salesforce/graphiti": "^12.10.0"
65
65
  },
66
66
  "overrides": {
67
67
  "undici": "^7.28.0"
@@ -0,0 +1,39 @@
1
+ @if (hasProviders()) {
2
+ <div class="mt-6">
3
+ @if (showHeader()) {
4
+ <div class="relative">
5
+ <div class="absolute inset-0 flex items-center">
6
+ <span class="w-full border-t"></span>
7
+ </div>
8
+ <div class="relative flex justify-center text-xs uppercase">
9
+ <span class="bg-background px-2 text-muted-foreground">{{ headerText() }}</span>
10
+ </div>
11
+ </div>
12
+ }
13
+
14
+ <div class="mt-4 grid gap-2">
15
+ @for (provider of authProviders(); track provider.developerName) {
16
+ <button
17
+ type="button"
18
+ (click)="redirectTo(provider.ssoUrl)"
19
+ [attr.aria-label]="'Sign in with ' + provider.friendlyName"
20
+ class="inline-flex w-full items-center justify-center gap-2 rounded-md border border-input bg-background px-4 py-2 text-sm font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
21
+ >
22
+ @if (provider.iconUrl) {
23
+ <img [src]="provider.iconUrl" alt="" class="h-4 w-4" aria-hidden="true" />
24
+ }
25
+ <span>{{ provider.friendlyName }}</span>
26
+ </button>
27
+ } @for (provider of samlProviders(); track provider.id) {
28
+ <button
29
+ type="button"
30
+ (click)="redirectTo(provider.ssoUrl)"
31
+ [attr.aria-label]="'Sign in with ' + provider.masterLabel"
32
+ class="inline-flex w-full items-center justify-center gap-2 rounded-md border border-input bg-background px-4 py-2 text-sm font-medium shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
33
+ >
34
+ <span>{{ provider.masterLabel }}</span>
35
+ </button>
36
+ }
37
+ </div>
38
+ </div>
39
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import {
7
+ ChangeDetectionStrategy,
8
+ Component,
9
+ type OnInit,
10
+ computed,
11
+ inject,
12
+ input,
13
+ signal,
14
+ } from "@angular/core";
15
+ import { ActivatedRoute } from "@angular/router";
16
+ import { DataClient } from "../../../api/data-client.service";
17
+ import { getStartUrl } from "../utils/auth-helpers";
18
+
19
+ interface AuthProviderInfo {
20
+ developerName: string;
21
+ friendlyName: string;
22
+ iconUrl: string | null;
23
+ ssoUrl: string;
24
+ }
25
+
26
+ interface SamlProviderInfo {
27
+ id: string;
28
+ masterLabel: string;
29
+ ssoUrl: string;
30
+ }
31
+
32
+ interface SocialLoginConfigResponse {
33
+ authProviders: AuthProviderInfo[];
34
+ samlProviders: SamlProviderInfo[];
35
+ autoRedirectUrl: string | null;
36
+ }
37
+
38
+ /**
39
+ * Social Login (IDP) component — Angular port of the React `SocialLogin`.
40
+ *
41
+ * Replicates the OOTB community_login/socialLogin LWC behavior which does not
42
+ * render on Site Container sites due to a platform restriction (isSiteContainer
43
+ * check in node-loginsettings.ftl).
44
+ *
45
+ * Behavior:
46
+ * - On init, fetches configured Auth Providers + SAML Providers
47
+ * - If only one IDP is configured and username/password is disabled, auto-redirects
48
+ * - Renders a button per provider with icon (Auth Providers) or without (SAML)
49
+ * - On click, redirects to the SSO URL to initiate the OAuth/SAML flow
50
+ *
51
+ * Backend: UIBundleSocialLoginConfig.cls (Apex REST endpoint), copied verbatim
52
+ * from the React feature. Uses the Auth.AuthConfiguration API.
53
+ */
54
+ @Component({
55
+ selector: "app-social-login",
56
+ changeDetection: ChangeDetectionStrategy.OnPush,
57
+ templateUrl: "./social-login.html",
58
+ })
59
+ export class SocialLoginComponent implements OnInit {
60
+ private readonly route = inject(ActivatedRoute);
61
+ private readonly dataClient = inject(DataClient);
62
+
63
+ /** Whether to show the header text above the buttons. Default: true */
64
+ readonly showHeader = input(true);
65
+
66
+ /** Header text. Default: "Or log in using:" */
67
+ readonly headerText = input("Or log in using:");
68
+
69
+ protected readonly authProviders = signal<AuthProviderInfo[]>([]);
70
+ protected readonly samlProviders = signal<SamlProviderInfo[]>([]);
71
+ protected readonly hasProviders = computed(
72
+ () => this.authProviders().length > 0 || this.samlProviders().length > 0,
73
+ );
74
+
75
+ async ngOnInit(): Promise<void> {
76
+ const startUrl = getStartUrl(this.route.snapshot.queryParamMap);
77
+ const config = await this.fetchConfig(startUrl);
78
+ if (!config) {
79
+ return;
80
+ }
81
+ if (config.autoRedirectUrl) {
82
+ window.location.assign(config.autoRedirectUrl);
83
+ return;
84
+ }
85
+ this.authProviders.set(config.authProviders ?? []);
86
+ this.samlProviders.set(config.samlProviders ?? []);
87
+ }
88
+
89
+ protected redirectTo(ssoUrl: string): void {
90
+ window.location.assign(ssoUrl);
91
+ }
92
+
93
+ private async fetchConfig(startUrl: string): Promise<SocialLoginConfigResponse | null> {
94
+ try {
95
+ const response = await this.dataClient.fetch(
96
+ `/services/apexrest/auth/social-login-config?startURL=${encodeURIComponent(startUrl)}`,
97
+ { method: "GET", headers: { Accept: "application/json" } },
98
+ );
99
+ if (!response.ok) {
100
+ return null;
101
+ }
102
+ return (await response.json()) as SocialLoginConfigResponse;
103
+ } catch {
104
+ return null;
105
+ }
106
+ }
107
+ }
@@ -43,5 +43,7 @@
43
43
  </app-field>
44
44
  </div>
45
45
  </app-auth-form>
46
+
47
+ <app-social-login />
46
48
  </div>
47
49
  </main>
@@ -8,6 +8,7 @@ import { FormBuilder } from "@angular/forms";
8
8
  import { ActivatedRoute, Router, RouterLink } from "@angular/router";
9
9
  import { DataClient } from "../../api/data-client.service";
10
10
  import { AuthFormComponent } from "../../features/authentication/auth-form/auth-form";
11
+ import { SocialLoginComponent } from "../../features/authentication/social-login/social-login";
11
12
  import { ReactiveFormPageBase } from "../../features/authentication/reactive-form-page.base";
12
13
  import { FieldComponent } from "../../components/ui/field/field";
13
14
  import { InputComponent } from "../../components/ui/input/input";
@@ -32,7 +33,7 @@ import {
32
33
  @Component({
33
34
  selector: "app-login-page",
34
35
  changeDetection: ChangeDetectionStrategy.OnPush,
35
- imports: [RouterLink, AuthFormComponent, FieldComponent, InputComponent],
36
+ imports: [RouterLink, AuthFormComponent, FieldComponent, InputComponent, SocialLoginComponent],
36
37
  templateUrl: "./login-page.html",
37
38
  })
38
39
  export class LoginPageComponent extends ReactiveFormPageBase {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
3
- "version": "12.9.0",
3
+ "version": "12.10.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
9
- "version": "12.9.0",
9
+ "version": "12.10.0",
10
10
  "license": "SEE LICENSE IN LICENSE.txt",
11
11
  "dependencies": {
12
12
  "fast-xml-parser": "^5.9.3",
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
3
- "version": "12.9.0",
3
+ "version": "12.10.0",
4
4
  "description": "Base SFDX project template",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "publishConfig": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-feature-angular-authentication",
3
- "version": "12.9.0",
3
+ "version": "12.10.0",
4
4
  "private": false,
5
5
  "engines": {
6
6
  "node": ">=22"