@flusys/ng-auth 7.0.1 → 7.1.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/README.md +36 -3
- package/fesm2022/flusys-ng-auth.mjs +964 -41
- package/fesm2022/flusys-ng-auth.mjs.map +1 -1
- package/package.json +5 -5
- package/types/flusys-ng-auth.d.ts +149 -3
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { HttpClient } from '@angular/common/http';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, Injectable, signal, computed, effect, PLATFORM_ID, InjectionToken, Component, untracked, viewChild, DestroyRef, input, Injector, output, resource, makeStateKey, TransferState } from '@angular/core';
|
|
3
|
+
import { inject, Injectable, signal, computed, effect, PLATFORM_ID, InjectionToken, Component, untracked, viewChild, DestroyRef, DOCUMENT, input, Injector, output, resource, makeStateKey, TransferState } from '@angular/core';
|
|
4
4
|
import { APP_CONFIG, getServiceUrl, isCompanyFeatureEnabled, isServiceEnabled, isSignUpEnabled, getPermissionMode, getDefaultCompanySlug, getDefaultBranchSlug } from '@flusys/ng-core';
|
|
5
5
|
import { of, timeout, switchMap, map, tap, catchError, shareReplay, finalize, BehaviorSubject, throwError, filter, take, from, forkJoin, firstValueFrom } from 'rxjs';
|
|
6
6
|
import * as i2 from '@angular/router';
|
|
7
7
|
import { Router, RouterOutlet, ActivatedRoute } from '@angular/router';
|
|
8
|
-
import { PlatformService, TranslateService, PermissionValidatorService, ApiResourceService, CookieService, USER_PERMISSIONS, COMPANY_PERMISSIONS, BRANCH_PERMISSIONS, resolveTranslationModule, SHARED_MESSAGES, permissionGuard, USER_PROVIDER, COMPANY_API_PROVIDER, USER_PERMISSION_PROVIDER, AUTH_STATE_PROVIDER, RouterTabsComponent, TranslatePipe, AngularModule, HasPermissionDirective, BaseFormComponent, BaseListComponent, BaseListTemplateDirective, FileUrlService, FILE_PROVIDER, PROFILE_PERMISSION_PROVIDER, FileUploaderComponent } from '@flusys/ng-shared';
|
|
8
|
+
import { PlatformService, TranslateService, PermissionValidatorService, ApiResourceService, CookieService, USER_PERMISSIONS, COMPANY_PERMISSIONS, BRANCH_PERMISSIONS, SOCIAL_AUTH_CONFIG_PERMISSIONS, resolveTranslationModule, SHARED_MESSAGES, permissionGuard, USER_PROVIDER, COMPANY_API_PROVIDER, USER_PERMISSION_PROVIDER, AUTH_STATE_PROVIDER, RouterTabsComponent, TranslatePipe, AngularModule, HasPermissionDirective, BaseFormComponent, BaseListComponent, BaseListTemplateDirective, FileUrlService, FILE_PROVIDER, PROFILE_PERMISSION_PROVIDER, FileUploaderComponent } from '@flusys/ng-shared';
|
|
9
9
|
import { map as map$1, catchError as catchError$1, tap as tap$1 } from 'rxjs/operators';
|
|
10
10
|
import * as i2$1 from '@angular/common';
|
|
11
11
|
import { isPlatformBrowser } from '@angular/common';
|
|
@@ -16,6 +16,20 @@ import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
|
16
16
|
import { form, required, FormField, email, minLength } from '@angular/forms/signals';
|
|
17
17
|
import * as i1 from '@angular/forms';
|
|
18
18
|
|
|
19
|
+
var SocialProviderEnum;
|
|
20
|
+
(function (SocialProviderEnum) {
|
|
21
|
+
SocialProviderEnum["GOOGLE"] = "google";
|
|
22
|
+
SocialProviderEnum["FACEBOOK"] = "facebook";
|
|
23
|
+
SocialProviderEnum["LINKEDIN"] = "linkedin";
|
|
24
|
+
SocialProviderEnum["MICROSOFT"] = "microsoft";
|
|
25
|
+
})(SocialProviderEnum || (SocialProviderEnum = {}));
|
|
26
|
+
const SOCIAL_PROVIDERS = [
|
|
27
|
+
SocialProviderEnum.GOOGLE,
|
|
28
|
+
SocialProviderEnum.FACEBOOK,
|
|
29
|
+
SocialProviderEnum.LINKEDIN,
|
|
30
|
+
SocialProviderEnum.MICROSOFT,
|
|
31
|
+
];
|
|
32
|
+
|
|
19
33
|
// ─── Type guards ───────────────────────────────────────────────────────────────
|
|
20
34
|
function isEmailVerificationRequired(response) {
|
|
21
35
|
return !!response.data && 'requiresEmailVerification' in response.data && response.data.requiresEmailVerification === true;
|
|
@@ -326,6 +340,10 @@ class AuthInitService {
|
|
|
326
340
|
this._initialized.set(true);
|
|
327
341
|
return of(true);
|
|
328
342
|
}
|
|
343
|
+
if (this.authState.requiresCompanySelection()) {
|
|
344
|
+
this._initialized.set(true);
|
|
345
|
+
return of(false);
|
|
346
|
+
}
|
|
329
347
|
if (this._initialized()) {
|
|
330
348
|
return of(this.authState.isAuthenticated());
|
|
331
349
|
}
|
|
@@ -419,6 +437,90 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
419
437
|
args: [{ providedIn: 'root' }]
|
|
420
438
|
}], ctorParameters: () => [] });
|
|
421
439
|
|
|
440
|
+
/**
|
|
441
|
+
* Authentication API Endpoints
|
|
442
|
+
*
|
|
443
|
+
* Centralized endpoint definitions to prevent inconsistencies across interceptors
|
|
444
|
+
* and services. All auth-related endpoints should be referenced from here.
|
|
445
|
+
*/
|
|
446
|
+
const AUTH_ENDPOINTS = {
|
|
447
|
+
LOGIN: '/auth/login',
|
|
448
|
+
LOGOUT: '/auth/logout',
|
|
449
|
+
REFRESH: '/auth/refresh',
|
|
450
|
+
REGISTER: '/auth/register',
|
|
451
|
+
ME: '/auth/me',
|
|
452
|
+
SELECT: '/auth/select',
|
|
453
|
+
SWITCH_COMPANY: '/auth/switch-company',
|
|
454
|
+
CHANGE_PASSWORD: '/auth/change-password',
|
|
455
|
+
VERIFY_EMAIL: '/auth/verify-email',
|
|
456
|
+
FORGOT_PASSWORD: '/auth/forgot-password',
|
|
457
|
+
RESET_PASSWORD: '/auth/reset-password',
|
|
458
|
+
RESEND_VERIFICATION: '/auth/resend-verification',
|
|
459
|
+
SOCIAL_CALLBACK: '/auth/social/callback',
|
|
460
|
+
};
|
|
461
|
+
/** Check if a URL matches a specific endpoint */
|
|
462
|
+
function isEndpoint(url, endpoint) {
|
|
463
|
+
return url.includes(endpoint);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/** Round-trips the pre-login returnUrl through the OAuth redirect, which cannot carry it itself. */
|
|
467
|
+
const SOCIAL_RETURN_URL_KEY = 'flusys_social_return_url';
|
|
468
|
+
class SocialAuthApiService {
|
|
469
|
+
appConfig = inject(APP_CONFIG);
|
|
470
|
+
http = inject(HttpClient);
|
|
471
|
+
platform = inject(PlatformService);
|
|
472
|
+
baseUrl = getServiceUrl(this.appConfig, 'auth');
|
|
473
|
+
get redirectUri() {
|
|
474
|
+
if (this.platform.isServer)
|
|
475
|
+
return '';
|
|
476
|
+
return `${window.location.origin}${AUTH_ENDPOINTS.SOCIAL_CALLBACK}`;
|
|
477
|
+
}
|
|
478
|
+
/** Called before redirecting to the provider, so the callback page can resume where the user left off. */
|
|
479
|
+
saveReturnUrl(returnUrl) {
|
|
480
|
+
if (this.platform.isServer)
|
|
481
|
+
return;
|
|
482
|
+
if (returnUrl)
|
|
483
|
+
sessionStorage.setItem(SOCIAL_RETURN_URL_KEY, returnUrl);
|
|
484
|
+
else
|
|
485
|
+
sessionStorage.removeItem(SOCIAL_RETURN_URL_KEY);
|
|
486
|
+
}
|
|
487
|
+
/** Reads and clears the returnUrl saved by {@link saveReturnUrl}. */
|
|
488
|
+
consumeReturnUrl() {
|
|
489
|
+
if (this.platform.isServer)
|
|
490
|
+
return '';
|
|
491
|
+
const returnUrl = sessionStorage.getItem(SOCIAL_RETURN_URL_KEY) ?? '';
|
|
492
|
+
sessionStorage.removeItem(SOCIAL_RETURN_URL_KEY);
|
|
493
|
+
return returnUrl;
|
|
494
|
+
}
|
|
495
|
+
getProviders() {
|
|
496
|
+
return this.http.post(`${this.baseUrl}/social/providers`, { redirectUri: this.redirectUri });
|
|
497
|
+
}
|
|
498
|
+
authorize(provider) {
|
|
499
|
+
return this.http.post(`${this.baseUrl}/social/authorize`, { provider, redirectUri: this.redirectUri });
|
|
500
|
+
}
|
|
501
|
+
callback(request) {
|
|
502
|
+
return this.http.post(`${this.baseUrl}/social/callback`, request);
|
|
503
|
+
}
|
|
504
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialAuthApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
505
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialAuthApiService, providedIn: 'root' });
|
|
506
|
+
}
|
|
507
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialAuthApiService, decorators: [{
|
|
508
|
+
type: Injectable,
|
|
509
|
+
args: [{ providedIn: 'root' }]
|
|
510
|
+
}] });
|
|
511
|
+
|
|
512
|
+
class SocialAuthConfigApiService extends ApiResourceService {
|
|
513
|
+
constructor() {
|
|
514
|
+
super('social-config', 'auth');
|
|
515
|
+
}
|
|
516
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialAuthConfigApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
517
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialAuthConfigApiService, providedIn: 'root' });
|
|
518
|
+
}
|
|
519
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialAuthConfigApiService, decorators: [{
|
|
520
|
+
type: Injectable,
|
|
521
|
+
args: [{ providedIn: 'root' }]
|
|
522
|
+
}], ctorParameters: () => [] });
|
|
523
|
+
|
|
422
524
|
/**
|
|
423
525
|
* Token Refresh State Service
|
|
424
526
|
* Manages token refresh state for SSR-safe request queuing.
|
|
@@ -518,31 +620,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
518
620
|
args: [{ providedIn: 'root' }]
|
|
519
621
|
}] });
|
|
520
622
|
|
|
521
|
-
/**
|
|
522
|
-
* Authentication API Endpoints
|
|
523
|
-
*
|
|
524
|
-
* Centralized endpoint definitions to prevent inconsistencies across interceptors
|
|
525
|
-
* and services. All auth-related endpoints should be referenced from here.
|
|
526
|
-
*/
|
|
527
|
-
const AUTH_ENDPOINTS = {
|
|
528
|
-
LOGIN: '/auth/login',
|
|
529
|
-
LOGOUT: '/auth/logout',
|
|
530
|
-
REFRESH: '/auth/refresh',
|
|
531
|
-
REGISTER: '/auth/register',
|
|
532
|
-
ME: '/auth/me',
|
|
533
|
-
SELECT: '/auth/select',
|
|
534
|
-
SWITCH_COMPANY: '/auth/switch-company',
|
|
535
|
-
CHANGE_PASSWORD: '/auth/change-password',
|
|
536
|
-
VERIFY_EMAIL: '/auth/verify-email',
|
|
537
|
-
FORGOT_PASSWORD: '/auth/forgot-password',
|
|
538
|
-
RESET_PASSWORD: '/auth/reset-password',
|
|
539
|
-
RESEND_VERIFICATION: '/auth/resend-verification',
|
|
540
|
-
};
|
|
541
|
-
/** Check if a URL matches a specific endpoint */
|
|
542
|
-
function isEndpoint(url, endpoint) {
|
|
543
|
-
return url.includes(endpoint);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
623
|
function isValidReturnUrl(url) {
|
|
547
624
|
if (typeof url !== 'string' || !url)
|
|
548
625
|
return false;
|
|
@@ -618,6 +695,7 @@ const COOKIE_ENDPOINTS = [
|
|
|
618
695
|
AUTH_ENDPOINTS.SELECT,
|
|
619
696
|
AUTH_ENDPOINTS.SWITCH_COMPANY,
|
|
620
697
|
AUTH_ENDPOINTS.VERIFY_EMAIL,
|
|
698
|
+
AUTH_ENDPOINTS.SOCIAL_CALLBACK,
|
|
621
699
|
];
|
|
622
700
|
/**
|
|
623
701
|
* Auth Interceptor - Adds auth headers and cookie credentials to requests
|
|
@@ -666,11 +744,13 @@ function tokenRefreshInterceptor(req, next) {
|
|
|
666
744
|
const isLoginRequest = isEndpoint(req.url, AUTH_ENDPOINTS.LOGIN);
|
|
667
745
|
const isSelectRequest = isEndpoint(req.url, AUTH_ENDPOINTS.SELECT);
|
|
668
746
|
const isRegisterRequest = isEndpoint(req.url, AUTH_ENDPOINTS.REGISTER);
|
|
747
|
+
const isSocialCallbackRequest = isEndpoint(req.url, AUTH_ENDPOINTS.SOCIAL_CALLBACK);
|
|
669
748
|
// Don't intercept auth-related endpoints
|
|
670
749
|
if (isRefreshRequest ||
|
|
671
750
|
isLoginRequest ||
|
|
672
751
|
isSelectRequest ||
|
|
673
|
-
isRegisterRequest
|
|
752
|
+
isRegisterRequest ||
|
|
753
|
+
isSocialCallbackRequest) {
|
|
674
754
|
return next(req);
|
|
675
755
|
}
|
|
676
756
|
// PROACTIVE REFRESH: Check if token is expired or expiring soon
|
|
@@ -1005,6 +1085,47 @@ const AUTH_MESSAGES = {
|
|
|
1005
1085
|
'administrative.user.no.users': 'No users found',
|
|
1006
1086
|
'administrative.user.details': 'User Details',
|
|
1007
1087
|
'administrative.user.delete.title': 'Delete User',
|
|
1088
|
+
// Social sign-in API keys
|
|
1089
|
+
'auth.social.providers.success': 'Social providers retrieved successfully',
|
|
1090
|
+
'auth.social.authorize.success': 'Authorization URL generated successfully',
|
|
1091
|
+
'auth.social.login.success': 'Signed in successfully',
|
|
1092
|
+
'auth.social.provider.disabled': 'Social sign-in with {{provider}} is not enabled',
|
|
1093
|
+
'auth.social.provider.not.configured': 'Social sign-in with {{provider}} is missing its client credentials',
|
|
1094
|
+
'auth.social.state.invalid': 'Social sign-in session is invalid or has expired',
|
|
1095
|
+
'auth.social.redirect.uri.mismatch': 'Redirect URI does not match the one the sign-in started with',
|
|
1096
|
+
'auth.social.exchange.failed': 'Social sign-in with {{provider}} failed',
|
|
1097
|
+
'auth.social.profile.incomplete': '{{provider}} did not return an account identifier',
|
|
1098
|
+
'auth.social.account.not.linked': 'No account is linked to this {{provider}} profile',
|
|
1099
|
+
'auth.social.company.required': 'No account is linked to this {{provider}} profile. Please sign up manually and join or create a company first, then social sign-in will work.',
|
|
1100
|
+
'auth.social.email.required': '{{provider}} did not share an email address',
|
|
1101
|
+
'auth.social.email.not.verified': '{{provider}} has not verified this email address',
|
|
1102
|
+
// components/social-login-buttons.component.ts
|
|
1103
|
+
'auth.social.continue.with': 'or continue with',
|
|
1104
|
+
'auth.social.provider.google': 'Google',
|
|
1105
|
+
'auth.social.provider.facebook': 'Facebook',
|
|
1106
|
+
'auth.social.provider.linkedin': 'LinkedIn',
|
|
1107
|
+
'auth.social.provider.microsoft': 'Microsoft',
|
|
1108
|
+
// pages/social-callback/social-callback-page.component.ts
|
|
1109
|
+
'auth.social.signing.in': 'Signing you in...',
|
|
1110
|
+
'auth.social.failed': 'Social sign-in failed',
|
|
1111
|
+
// pages/social-config/social-config-page.component.ts (API keys)
|
|
1112
|
+
'social.config.provider.already.configured': 'Social provider "{{provider}}" is already configured',
|
|
1113
|
+
'social.config.credentials.required': 'Client ID and client secret are required for {{provider}}',
|
|
1114
|
+
'social.config.title': 'Social Config',
|
|
1115
|
+
'social.config.subtitle': 'Enable the providers users may sign in with, and hold their credentials',
|
|
1116
|
+
'social.config.redirect.uri': 'Redirect URI',
|
|
1117
|
+
'social.config.client.id': 'Client ID',
|
|
1118
|
+
'social.config.client.secret': 'Client Secret',
|
|
1119
|
+
'social.config.client.secret.unchanged': 'Unchanged - leave blank to keep the saved secret',
|
|
1120
|
+
'social.config.tenant.id': 'Directory (tenant) ID',
|
|
1121
|
+
'social.config.tenant.id.example': 'common',
|
|
1122
|
+
'social.config.scopes': 'Scopes',
|
|
1123
|
+
'social.config.scopes.placeholder': 'Leave blank to use the provider defaults',
|
|
1124
|
+
'social.config.save.success': 'Social sign-in settings saved',
|
|
1125
|
+
'social.config.provider.hint.google': 'Google Cloud console, OAuth 2.0 client ID',
|
|
1126
|
+
'social.config.provider.hint.facebook': 'Meta for Developers, Facebook Login app',
|
|
1127
|
+
'social.config.provider.hint.linkedin': 'LinkedIn developers, Sign In with LinkedIn',
|
|
1128
|
+
'social.config.provider.hint.microsoft': 'Microsoft Entra ID, app registration',
|
|
1008
1129
|
};
|
|
1009
1130
|
|
|
1010
1131
|
/**
|
|
@@ -1027,6 +1148,9 @@ function resolveAdministrationDefaultTab() {
|
|
|
1027
1148
|
permissionValidator.hasPermission(BRANCH_PERMISSIONS.READ)) {
|
|
1028
1149
|
return 'branch';
|
|
1029
1150
|
}
|
|
1151
|
+
if (permissionValidator.hasPermission(SOCIAL_AUTH_CONFIG_PERMISSIONS.READ)) {
|
|
1152
|
+
return 'social-config';
|
|
1153
|
+
}
|
|
1030
1154
|
return 'users';
|
|
1031
1155
|
}
|
|
1032
1156
|
/**
|
|
@@ -1065,6 +1189,10 @@ const AUTH_ROUTES = [
|
|
|
1065
1189
|
loadComponent: () => Promise.resolve().then(function () { return resetPasswordPage_component; }).then((m) => m.ResetPasswordPageComponent),
|
|
1066
1190
|
canActivate: [emailFeatureGuard, guestGuard],
|
|
1067
1191
|
},
|
|
1192
|
+
{
|
|
1193
|
+
path: 'social/callback',
|
|
1194
|
+
loadComponent: () => Promise.resolve().then(function () { return socialCallbackPage_component; }).then((m) => m.SocialCallbackPageComponent),
|
|
1195
|
+
},
|
|
1068
1196
|
{
|
|
1069
1197
|
path: 'verify-email',
|
|
1070
1198
|
loadComponent: () => Promise.resolve().then(function () { return verifyEmailPage_component; }).then((m) => m.VerifyEmailPageComponent),
|
|
@@ -1158,6 +1286,12 @@ const ADMINISTRATION_ROUTES = [
|
|
|
1158
1286
|
},
|
|
1159
1287
|
],
|
|
1160
1288
|
},
|
|
1289
|
+
// Social sign-in provider credentials tab
|
|
1290
|
+
{
|
|
1291
|
+
path: 'social-config',
|
|
1292
|
+
canActivate: [permissionGuard(SOCIAL_AUTH_CONFIG_PERMISSIONS.READ)],
|
|
1293
|
+
loadComponent: () => Promise.resolve().then(function () { return socialConfigPage_component; }).then((m) => m.SocialConfigPageComponent),
|
|
1294
|
+
},
|
|
1161
1295
|
// Default redirect to the first tab the user has permission to see
|
|
1162
1296
|
{
|
|
1163
1297
|
path: '',
|
|
@@ -1513,6 +1647,7 @@ function validateEmail(email, config) {
|
|
|
1513
1647
|
* - Users (always visible) - includes permission management via dialogs
|
|
1514
1648
|
* - Companies (only if company feature enabled)
|
|
1515
1649
|
* - Branches (only if company feature enabled)
|
|
1650
|
+
* - Social Config (social sign-in provider credentials)
|
|
1516
1651
|
*/
|
|
1517
1652
|
class AdministrationPageComponent {
|
|
1518
1653
|
appConfig = inject(APP_CONFIG);
|
|
@@ -1543,6 +1678,13 @@ class AdministrationPageComponent {
|
|
|
1543
1678
|
permission: BRANCH_PERMISSIONS.READ,
|
|
1544
1679
|
requiresCompanyFeature: true,
|
|
1545
1680
|
},
|
|
1681
|
+
{
|
|
1682
|
+
id: 'social-config',
|
|
1683
|
+
labelKey: 'social.config.title',
|
|
1684
|
+
icon: 'key',
|
|
1685
|
+
route: 'social-config',
|
|
1686
|
+
permission: SOCIAL_AUTH_CONFIG_PERMISSIONS.READ,
|
|
1687
|
+
},
|
|
1546
1688
|
];
|
|
1547
1689
|
/** Check if company feature is enabled */
|
|
1548
1690
|
companyFeatureEnabled = computed(() => isCompanyFeatureEnabled(this.appConfig), /* @ts-ignore */
|
|
@@ -1922,6 +2064,7 @@ class BranchFormComponent {
|
|
|
1922
2064
|
<div class="flex justify-end gap-4 md:col-span-2 pt-4">
|
|
1923
2065
|
<button
|
|
1924
2066
|
fButton
|
|
2067
|
+
type="button"
|
|
1925
2068
|
severity="secondary"
|
|
1926
2069
|
[outlined]="true"
|
|
1927
2070
|
(click)="navigateToList()"
|
|
@@ -2064,6 +2207,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
2064
2207
|
<div class="flex justify-end gap-4 md:col-span-2 pt-4">
|
|
2065
2208
|
<button
|
|
2066
2209
|
fButton
|
|
2210
|
+
type="button"
|
|
2067
2211
|
severity="secondary"
|
|
2068
2212
|
[outlined]="true"
|
|
2069
2213
|
(click)="navigateToList()"
|
|
@@ -3004,6 +3148,240 @@ var forgotPasswordPage_component = /*#__PURE__*/Object.freeze({
|
|
|
3004
3148
|
ForgotPasswordPageComponent: ForgotPasswordPageComponent
|
|
3005
3149
|
});
|
|
3006
3150
|
|
|
3151
|
+
class SocialLoginButtonsComponent {
|
|
3152
|
+
destroyRef = inject(DestroyRef);
|
|
3153
|
+
socialApi = inject(SocialAuthApiService);
|
|
3154
|
+
messageService = inject(MessageService);
|
|
3155
|
+
platform = inject(PlatformService);
|
|
3156
|
+
document = inject(DOCUMENT);
|
|
3157
|
+
translateAdapter = inject(TranslateService, {
|
|
3158
|
+
optional: true,
|
|
3159
|
+
});
|
|
3160
|
+
returnUrl = input('', /* @ts-ignore */
|
|
3161
|
+
...(ngDevMode ? [{ debugName: "returnUrl" }] : /* istanbul ignore next */ []));
|
|
3162
|
+
providerEnum = SocialProviderEnum;
|
|
3163
|
+
_providers = signal([], /* @ts-ignore */
|
|
3164
|
+
...(ngDevMode ? [{ debugName: "_providers" }] : /* istanbul ignore next */ []));
|
|
3165
|
+
providers = this._providers.asReadonly();
|
|
3166
|
+
_pendingProvider = signal(null, /* @ts-ignore */
|
|
3167
|
+
...(ngDevMode ? [{ debugName: "_pendingProvider" }] : /* istanbul ignore next */ []));
|
|
3168
|
+
pendingProvider = this._pendingProvider.asReadonly();
|
|
3169
|
+
showLabels = computed(() => this.providers().length <= 2, /* @ts-ignore */
|
|
3170
|
+
...(ngDevMode ? [{ debugName: "showLabels" }] : /* istanbul ignore next */ []));
|
|
3171
|
+
layoutClass = computed(() => {
|
|
3172
|
+
switch (this.providers().length) {
|
|
3173
|
+
case 1:
|
|
3174
|
+
return 'grid grid-cols-1';
|
|
3175
|
+
case 2:
|
|
3176
|
+
return 'grid grid-cols-2 gap-3';
|
|
3177
|
+
default:
|
|
3178
|
+
return 'flex flex-wrap justify-center gap-3';
|
|
3179
|
+
}
|
|
3180
|
+
}, /* @ts-ignore */
|
|
3181
|
+
...(ngDevMode ? [{ debugName: "layoutClass" }] : /* istanbul ignore next */ []));
|
|
3182
|
+
constructor() {
|
|
3183
|
+
if (this.platform.isServer)
|
|
3184
|
+
return;
|
|
3185
|
+
this.socialApi
|
|
3186
|
+
.getProviders()
|
|
3187
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3188
|
+
.subscribe({
|
|
3189
|
+
next: (response) => this._providers.set(response.data ?? []),
|
|
3190
|
+
error: () => this._providers.set([]),
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
3193
|
+
onSelect(provider) {
|
|
3194
|
+
this._pendingProvider.set(provider);
|
|
3195
|
+
this.socialApi.saveReturnUrl(this.returnUrl());
|
|
3196
|
+
this.socialApi
|
|
3197
|
+
.authorize(provider)
|
|
3198
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3199
|
+
.subscribe({
|
|
3200
|
+
next: (response) => {
|
|
3201
|
+
const url = response.data?.authorizationUrl;
|
|
3202
|
+
if (!url) {
|
|
3203
|
+
this._pendingProvider.set(null);
|
|
3204
|
+
return;
|
|
3205
|
+
}
|
|
3206
|
+
this.redirectTo(url);
|
|
3207
|
+
},
|
|
3208
|
+
error: (error) => {
|
|
3209
|
+
this._pendingProvider.set(null);
|
|
3210
|
+
const messageKey = error?.error?.messageKey;
|
|
3211
|
+
this.messageService.add({
|
|
3212
|
+
severity: 'error',
|
|
3213
|
+
summary: this.translate('auth.login.title'),
|
|
3214
|
+
detail: messageKey
|
|
3215
|
+
? this.translate(messageKey, { provider })
|
|
3216
|
+
: error?.error?.message,
|
|
3217
|
+
});
|
|
3218
|
+
},
|
|
3219
|
+
});
|
|
3220
|
+
}
|
|
3221
|
+
redirectTo(url) {
|
|
3222
|
+
this.document.location.assign(url);
|
|
3223
|
+
}
|
|
3224
|
+
translate(key, vars) {
|
|
3225
|
+
return this.translateAdapter?.translate(key, vars) ?? key;
|
|
3226
|
+
}
|
|
3227
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialLoginButtonsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3228
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: SocialLoginButtonsComponent, isStandalone: true, selector: "lib-social-login-buttons", inputs: { returnUrl: { classPropertyName: "returnUrl", publicName: "returnUrl", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
3229
|
+
@if (providers().length) {
|
|
3230
|
+
<div class="mt-6">
|
|
3231
|
+
<div class="flex items-center gap-3 mb-4">
|
|
3232
|
+
<span class="flex-1 h-px bg-surface-200 dark:bg-surface-700"></span>
|
|
3233
|
+
<span class="text-muted-color text-sm">{{
|
|
3234
|
+
'auth.social.continue.with' | translate
|
|
3235
|
+
}}</span>
|
|
3236
|
+
<span class="flex-1 h-px bg-surface-200 dark:bg-surface-700"></span>
|
|
3237
|
+
</div>
|
|
3238
|
+
|
|
3239
|
+
<div [class]="layoutClass()">
|
|
3240
|
+
@for (info of providers(); track info.provider) {
|
|
3241
|
+
<button
|
|
3242
|
+
fButton
|
|
3243
|
+
type="button"
|
|
3244
|
+
severity="secondary"
|
|
3245
|
+
[outlined]="true"
|
|
3246
|
+
[class]="showLabels() ? 'w-full' : 'shrink-0'"
|
|
3247
|
+
[attr.aria-label]="
|
|
3248
|
+
showLabels()
|
|
3249
|
+
? null
|
|
3250
|
+
: ('auth.social.provider.' + info.provider | translate)
|
|
3251
|
+
"
|
|
3252
|
+
[attr.title]="
|
|
3253
|
+
showLabels()
|
|
3254
|
+
? null
|
|
3255
|
+
: ('auth.social.provider.' + info.provider | translate)
|
|
3256
|
+
"
|
|
3257
|
+
[disabled]="pendingProvider() !== null && pendingProvider() !== info.provider"
|
|
3258
|
+
[loading]="pendingProvider() === info.provider"
|
|
3259
|
+
(click)="onSelect(info.provider)"
|
|
3260
|
+
>
|
|
3261
|
+
@if (pendingProvider() !== info.provider) {
|
|
3262
|
+
@switch (info.provider) {
|
|
3263
|
+
@case (providerEnum.GOOGLE) {
|
|
3264
|
+
<svg class="shrink-0" width="18" height="18" viewBox="0 0 48 48" aria-hidden="true">
|
|
3265
|
+
<path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" />
|
|
3266
|
+
<path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 3-2.26 5.54-4.78 7.25l7.73 6c4.51-4.18 7.09-10.36 7.09-17.72z" />
|
|
3267
|
+
<path fill="#FBBC05" d="M10.53 28.59A14.5 14.5 0 0 1 9.77 24c0-1.6.29-3.14.76-4.59l-7.98-6.19A23.94 23.94 0 0 0 0 24c0 3.88.93 7.54 2.56 10.78l7.97-6.19z" />
|
|
3268
|
+
<path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" />
|
|
3269
|
+
</svg>
|
|
3270
|
+
}
|
|
3271
|
+
@case (providerEnum.FACEBOOK) {
|
|
3272
|
+
<svg class="shrink-0" width="18" height="18" viewBox="0 0 24 24" aria-hidden="true">
|
|
3273
|
+
<path fill="#1877F2" d="M24 12.07C24 5.4 18.63 0 12 0S0 5.4 0 12.07C0 18.1 4.39 23.1 10.13 24v-8.44H7.08v-3.49h3.05V9.41c0-3.02 1.79-4.69 4.53-4.69 1.31 0 2.69.24 2.69.24v2.96h-1.51c-1.49 0-1.96.93-1.96 1.88v2.25h3.33l-.53 3.49h-2.8V24C19.61 23.1 24 18.1 24 12.07z" />
|
|
3274
|
+
</svg>
|
|
3275
|
+
}
|
|
3276
|
+
@case (providerEnum.LINKEDIN) {
|
|
3277
|
+
<svg class="shrink-0" width="18" height="18" viewBox="0 0 24 24" aria-hidden="true">
|
|
3278
|
+
<path fill="#0A66C2" d="M20.45 20.45h-3.56v-5.57c0-1.33-.03-3.04-1.85-3.04-1.86 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05a3.74 3.74 0 0 1 3.37-1.85c3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 1 1 0-4.13 2.06 2.06 0 0 1 0 4.13zM7.12 20.45H3.55V9h3.57v11.45zM22.22 0H1.77C.79 0 0 .78 0 1.73v20.54C0 23.22.79 24 1.77 24h20.45c.98 0 1.78-.78 1.78-1.73V1.73C24 .78 23.2 0 22.22 0z" />
|
|
3279
|
+
</svg>
|
|
3280
|
+
}
|
|
3281
|
+
@case (providerEnum.MICROSOFT) {
|
|
3282
|
+
<svg class="shrink-0" width="18" height="18" viewBox="0 0 23 23" aria-hidden="true">
|
|
3283
|
+
<path fill="#F25022" d="M0 0h11v11H0z" />
|
|
3284
|
+
<path fill="#7FBA00" d="M12 0h11v11H12z" />
|
|
3285
|
+
<path fill="#00A4EF" d="M0 12h11v11H0z" />
|
|
3286
|
+
<path fill="#FFB900" d="M12 12h11v11H12z" />
|
|
3287
|
+
</svg>
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
@if (showLabels()) {
|
|
3292
|
+
<span>{{
|
|
3293
|
+
'auth.social.provider.' + info.provider | translate
|
|
3294
|
+
}}</span>
|
|
3295
|
+
}
|
|
3296
|
+
</button>
|
|
3297
|
+
}
|
|
3298
|
+
</div>
|
|
3299
|
+
</div>
|
|
3300
|
+
}
|
|
3301
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: UiModule }, { kind: "directive", type: i1$1.FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
3302
|
+
}
|
|
3303
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialLoginButtonsComponent, decorators: [{
|
|
3304
|
+
type: Component,
|
|
3305
|
+
args: [{
|
|
3306
|
+
selector: 'lib-social-login-buttons',
|
|
3307
|
+
imports: [UiModule, TranslatePipe],
|
|
3308
|
+
template: `
|
|
3309
|
+
@if (providers().length) {
|
|
3310
|
+
<div class="mt-6">
|
|
3311
|
+
<div class="flex items-center gap-3 mb-4">
|
|
3312
|
+
<span class="flex-1 h-px bg-surface-200 dark:bg-surface-700"></span>
|
|
3313
|
+
<span class="text-muted-color text-sm">{{
|
|
3314
|
+
'auth.social.continue.with' | translate
|
|
3315
|
+
}}</span>
|
|
3316
|
+
<span class="flex-1 h-px bg-surface-200 dark:bg-surface-700"></span>
|
|
3317
|
+
</div>
|
|
3318
|
+
|
|
3319
|
+
<div [class]="layoutClass()">
|
|
3320
|
+
@for (info of providers(); track info.provider) {
|
|
3321
|
+
<button
|
|
3322
|
+
fButton
|
|
3323
|
+
type="button"
|
|
3324
|
+
severity="secondary"
|
|
3325
|
+
[outlined]="true"
|
|
3326
|
+
[class]="showLabels() ? 'w-full' : 'shrink-0'"
|
|
3327
|
+
[attr.aria-label]="
|
|
3328
|
+
showLabels()
|
|
3329
|
+
? null
|
|
3330
|
+
: ('auth.social.provider.' + info.provider | translate)
|
|
3331
|
+
"
|
|
3332
|
+
[attr.title]="
|
|
3333
|
+
showLabels()
|
|
3334
|
+
? null
|
|
3335
|
+
: ('auth.social.provider.' + info.provider | translate)
|
|
3336
|
+
"
|
|
3337
|
+
[disabled]="pendingProvider() !== null && pendingProvider() !== info.provider"
|
|
3338
|
+
[loading]="pendingProvider() === info.provider"
|
|
3339
|
+
(click)="onSelect(info.provider)"
|
|
3340
|
+
>
|
|
3341
|
+
@if (pendingProvider() !== info.provider) {
|
|
3342
|
+
@switch (info.provider) {
|
|
3343
|
+
@case (providerEnum.GOOGLE) {
|
|
3344
|
+
<svg class="shrink-0" width="18" height="18" viewBox="0 0 48 48" aria-hidden="true">
|
|
3345
|
+
<path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" />
|
|
3346
|
+
<path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 3-2.26 5.54-4.78 7.25l7.73 6c4.51-4.18 7.09-10.36 7.09-17.72z" />
|
|
3347
|
+
<path fill="#FBBC05" d="M10.53 28.59A14.5 14.5 0 0 1 9.77 24c0-1.6.29-3.14.76-4.59l-7.98-6.19A23.94 23.94 0 0 0 0 24c0 3.88.93 7.54 2.56 10.78l7.97-6.19z" />
|
|
3348
|
+
<path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" />
|
|
3349
|
+
</svg>
|
|
3350
|
+
}
|
|
3351
|
+
@case (providerEnum.FACEBOOK) {
|
|
3352
|
+
<svg class="shrink-0" width="18" height="18" viewBox="0 0 24 24" aria-hidden="true">
|
|
3353
|
+
<path fill="#1877F2" d="M24 12.07C24 5.4 18.63 0 12 0S0 5.4 0 12.07C0 18.1 4.39 23.1 10.13 24v-8.44H7.08v-3.49h3.05V9.41c0-3.02 1.79-4.69 4.53-4.69 1.31 0 2.69.24 2.69.24v2.96h-1.51c-1.49 0-1.96.93-1.96 1.88v2.25h3.33l-.53 3.49h-2.8V24C19.61 23.1 24 18.1 24 12.07z" />
|
|
3354
|
+
</svg>
|
|
3355
|
+
}
|
|
3356
|
+
@case (providerEnum.LINKEDIN) {
|
|
3357
|
+
<svg class="shrink-0" width="18" height="18" viewBox="0 0 24 24" aria-hidden="true">
|
|
3358
|
+
<path fill="#0A66C2" d="M20.45 20.45h-3.56v-5.57c0-1.33-.03-3.04-1.85-3.04-1.86 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05a3.74 3.74 0 0 1 3.37-1.85c3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 1 1 0-4.13 2.06 2.06 0 0 1 0 4.13zM7.12 20.45H3.55V9h3.57v11.45zM22.22 0H1.77C.79 0 0 .78 0 1.73v20.54C0 23.22.79 24 1.77 24h20.45c.98 0 1.78-.78 1.78-1.73V1.73C24 .78 23.2 0 22.22 0z" />
|
|
3359
|
+
</svg>
|
|
3360
|
+
}
|
|
3361
|
+
@case (providerEnum.MICROSOFT) {
|
|
3362
|
+
<svg class="shrink-0" width="18" height="18" viewBox="0 0 23 23" aria-hidden="true">
|
|
3363
|
+
<path fill="#F25022" d="M0 0h11v11H0z" />
|
|
3364
|
+
<path fill="#7FBA00" d="M12 0h11v11H12z" />
|
|
3365
|
+
<path fill="#00A4EF" d="M0 12h11v11H0z" />
|
|
3366
|
+
<path fill="#FFB900" d="M12 12h11v11H12z" />
|
|
3367
|
+
</svg>
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
@if (showLabels()) {
|
|
3372
|
+
<span>{{
|
|
3373
|
+
'auth.social.provider.' + info.provider | translate
|
|
3374
|
+
}}</span>
|
|
3375
|
+
}
|
|
3376
|
+
</button>
|
|
3377
|
+
}
|
|
3378
|
+
</div>
|
|
3379
|
+
</div>
|
|
3380
|
+
}
|
|
3381
|
+
`,
|
|
3382
|
+
}]
|
|
3383
|
+
}], ctorParameters: () => [], propDecorators: { returnUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "returnUrl", required: false }] }] } });
|
|
3384
|
+
|
|
3007
3385
|
class LoginPageComponent {
|
|
3008
3386
|
destroyRef = inject(DestroyRef);
|
|
3009
3387
|
appConfig = inject(APP_CONFIG);
|
|
@@ -3029,6 +3407,8 @@ class LoginPageComponent {
|
|
|
3029
3407
|
...(ngDevMode ? [{ debugName: "unverifiedEmail" }] : /* istanbul ignore next */ []));
|
|
3030
3408
|
savedUserName = signal(this.authState.getSavedUserName(), /* @ts-ignore */
|
|
3031
3409
|
...(ngDevMode ? [{ debugName: "savedUserName" }] : /* istanbul ignore next */ []));
|
|
3410
|
+
welcomeBackText = computed(() => this.translate('auth.login.welcome.back', { name: this.savedUserName() }), /* @ts-ignore */
|
|
3411
|
+
...(ngDevMode ? [{ debugName: "welcomeBackText" }] : /* istanbul ignore next */ []));
|
|
3032
3412
|
loginFormModel = signal({
|
|
3033
3413
|
email: '',
|
|
3034
3414
|
password: '',
|
|
@@ -3379,12 +3759,12 @@ class LoginPageComponent {
|
|
|
3379
3759
|
<!-- Step 1: Credentials -->
|
|
3380
3760
|
@if (currentStep() === 'credentials') {
|
|
3381
3761
|
<div class="text-center mb-8">
|
|
3382
|
-
<div
|
|
3762
|
+
<div
|
|
3763
|
+
class="text-3xl font-medium mb-4 text-color truncate"
|
|
3764
|
+
[title]="savedUserName() ? welcomeBackText() : null"
|
|
3765
|
+
>
|
|
3383
3766
|
@if (savedUserName()) {
|
|
3384
|
-
{{
|
|
3385
|
-
'auth.login.welcome.back'
|
|
3386
|
-
| translate: { name: savedUserName()! }
|
|
3387
|
-
}}
|
|
3767
|
+
{{ welcomeBackText() }}
|
|
3388
3768
|
} @else {
|
|
3389
3769
|
{{ 'auth.login.welcome' | translate }}
|
|
3390
3770
|
}
|
|
@@ -3465,6 +3845,8 @@ class LoginPageComponent {
|
|
|
3465
3845
|
<span pButtonLabel>{{ 'auth.login.sign.in' | translate }}</span>
|
|
3466
3846
|
</button>
|
|
3467
3847
|
|
|
3848
|
+
<lib-social-login-buttons [returnUrl]="returnUrl()" />
|
|
3849
|
+
|
|
3468
3850
|
@if (signUpEnabled()) {
|
|
3469
3851
|
<div class="flex items-center justify-center mt-4 gap-2">
|
|
3470
3852
|
<span class="font-medium">{{
|
|
@@ -3596,7 +3978,7 @@ class LoginPageComponent {
|
|
|
3596
3978
|
</div>
|
|
3597
3979
|
</div>
|
|
3598
3980
|
</div>
|
|
3599
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: UiModule }, { kind: "directive", type: i1$1.FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "component", type: i1$1.CheckboxComponent, selector: "f-checkbox", inputs: ["label", "binary", "disabled", "readonly", "inputId", "invalid", "indeterminate", "variant", "size", "touched", "styleClass", "style", "checked"], outputs: ["onChange", "onBlur", "touch", "checkedChange"] }, { kind: "directive", type: i1$1.InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "component", type: i1$1.PasswordComponent, selector: "f-password", inputs: ["toggleMask", "feedback", "disabled", "placeholder", "inputId", "invalid", "touched", "inputStyleClass", "styleClass", "style", "ariaDescribedby", "autocomplete", "variant", "size", "fluid", "appendTo", "promptLabel", "weakLabel", "mediumLabel", "strongLabel", "mediumRegex", "strongRegex"], outputs: ["onBlur", "onFocus"] }, { kind: "component", type: i1$1.SelectComponent, selector: "f-select", inputs: ["options", "optionLabel", "optionValue", "placeholder", "disabled", "filter", "filterBy", "filterMatchMode", "filterLocale", "filterPlaceholder", "resetFilterOnHide", "autofocusFilter", "emptyMessage", "emptyFilterMessage", "showClear", "loading", "loadingIcon", "dropdownIcon", "clearIcon", "inputId", "invalid", "touched", "styleClass", "style", "appendTo", "editable", "readonly", "autofocus", "checkmark", "group", "optionGroupLabel", "optionGroupChildren", "virtualScroll", "virtualScrollItemSize", "lazy"], outputs: ["onFilter", "onChange", "onShow", "onHide", "onBlur", "onFocus", "onClear", "onLazyLoad"] }, { kind: "component", type: i1$1.UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "component", type: AppFloatingConfigurator, selector: "app-floating-configurator" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
3981
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: UiModule }, { kind: "directive", type: i1$1.FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "component", type: i1$1.CheckboxComponent, selector: "f-checkbox", inputs: ["label", "binary", "disabled", "readonly", "inputId", "invalid", "indeterminate", "variant", "size", "touched", "styleClass", "style", "checked"], outputs: ["onChange", "onBlur", "touch", "checkedChange"] }, { kind: "directive", type: i1$1.InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "component", type: i1$1.PasswordComponent, selector: "f-password", inputs: ["toggleMask", "feedback", "disabled", "placeholder", "inputId", "invalid", "touched", "inputStyleClass", "styleClass", "style", "ariaDescribedby", "autocomplete", "variant", "size", "fluid", "appendTo", "promptLabel", "weakLabel", "mediumLabel", "strongLabel", "mediumRegex", "strongRegex"], outputs: ["onBlur", "onFocus"] }, { kind: "component", type: i1$1.SelectComponent, selector: "f-select", inputs: ["options", "optionLabel", "optionValue", "placeholder", "disabled", "filter", "filterBy", "filterMatchMode", "filterLocale", "filterPlaceholder", "resetFilterOnHide", "autofocusFilter", "emptyMessage", "emptyFilterMessage", "showClear", "loading", "loadingIcon", "dropdownIcon", "clearIcon", "inputId", "invalid", "touched", "styleClass", "style", "appendTo", "editable", "readonly", "autofocus", "checkmark", "group", "optionGroupLabel", "optionGroupChildren", "virtualScroll", "virtualScrollItemSize", "lazy"], outputs: ["onFilter", "onChange", "onShow", "onHide", "onBlur", "onFocus", "onClear", "onLazyLoad"] }, { kind: "component", type: i1$1.UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "component", type: AppFloatingConfigurator, selector: "app-floating-configurator" }, { kind: "component", type: SocialLoginButtonsComponent, selector: "lib-social-login-buttons", inputs: ["returnUrl"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
3600
3982
|
}
|
|
3601
3983
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: LoginPageComponent, decorators: [{
|
|
3602
3984
|
type: Component,
|
|
@@ -3605,6 +3987,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
3605
3987
|
imports: [UiModule, AngularModule,
|
|
3606
3988
|
FormField,
|
|
3607
3989
|
AppFloatingConfigurator,
|
|
3990
|
+
SocialLoginButtonsComponent,
|
|
3608
3991
|
TranslatePipe,],
|
|
3609
3992
|
template: `
|
|
3610
3993
|
<app-floating-configurator />
|
|
@@ -3658,12 +4041,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
3658
4041
|
<!-- Step 1: Credentials -->
|
|
3659
4042
|
@if (currentStep() === 'credentials') {
|
|
3660
4043
|
<div class="text-center mb-8">
|
|
3661
|
-
<div
|
|
4044
|
+
<div
|
|
4045
|
+
class="text-3xl font-medium mb-4 text-color truncate"
|
|
4046
|
+
[title]="savedUserName() ? welcomeBackText() : null"
|
|
4047
|
+
>
|
|
3662
4048
|
@if (savedUserName()) {
|
|
3663
|
-
{{
|
|
3664
|
-
'auth.login.welcome.back'
|
|
3665
|
-
| translate: { name: savedUserName()! }
|
|
3666
|
-
}}
|
|
4049
|
+
{{ welcomeBackText() }}
|
|
3667
4050
|
} @else {
|
|
3668
4051
|
{{ 'auth.login.welcome' | translate }}
|
|
3669
4052
|
}
|
|
@@ -3744,6 +4127,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
3744
4127
|
<span pButtonLabel>{{ 'auth.login.sign.in' | translate }}</span>
|
|
3745
4128
|
</button>
|
|
3746
4129
|
|
|
4130
|
+
<lib-social-login-buttons [returnUrl]="returnUrl()" />
|
|
4131
|
+
|
|
3747
4132
|
@if (signUpEnabled()) {
|
|
3748
4133
|
<div class="flex items-center justify-center mt-4 gap-2">
|
|
3749
4134
|
<span class="font-medium">{{
|
|
@@ -7125,6 +7510,542 @@ var resetPasswordPage_component = /*#__PURE__*/Object.freeze({
|
|
|
7125
7510
|
ResetPasswordPageComponent: ResetPasswordPageComponent
|
|
7126
7511
|
});
|
|
7127
7512
|
|
|
7513
|
+
class SocialCallbackPageComponent {
|
|
7514
|
+
destroyRef = inject(DestroyRef);
|
|
7515
|
+
route = inject(ActivatedRoute);
|
|
7516
|
+
router = inject(Router);
|
|
7517
|
+
platform = inject(PlatformService);
|
|
7518
|
+
socialApi = inject(SocialAuthApiService);
|
|
7519
|
+
authState = inject(AuthStateService);
|
|
7520
|
+
messageService = inject(MessageService);
|
|
7521
|
+
translateAdapter = inject(TranslateService, {
|
|
7522
|
+
optional: true,
|
|
7523
|
+
});
|
|
7524
|
+
_error = signal(null, /* @ts-ignore */
|
|
7525
|
+
...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
|
|
7526
|
+
errorDetail = computed(() => {
|
|
7527
|
+
const error = this._error();
|
|
7528
|
+
if (!error)
|
|
7529
|
+
return null;
|
|
7530
|
+
return 'key' in error ? this.translate(error.key, error.vars) : error.raw;
|
|
7531
|
+
}, /* @ts-ignore */
|
|
7532
|
+
...(ngDevMode ? [{ debugName: "errorDetail" }] : /* istanbul ignore next */ []));
|
|
7533
|
+
constructor() {
|
|
7534
|
+
if (this.platform.isServer)
|
|
7535
|
+
return;
|
|
7536
|
+
this.exchangeCode();
|
|
7537
|
+
}
|
|
7538
|
+
backToLogin() {
|
|
7539
|
+
this.router.navigate(['/auth/login']);
|
|
7540
|
+
}
|
|
7541
|
+
exchangeCode() {
|
|
7542
|
+
const params = this.route.snapshot.queryParamMap;
|
|
7543
|
+
const providerError = params.get('error_description') ?? params.get('error');
|
|
7544
|
+
if (providerError) {
|
|
7545
|
+
this._error.set({ raw: providerError });
|
|
7546
|
+
return;
|
|
7547
|
+
}
|
|
7548
|
+
const code = params.get('code');
|
|
7549
|
+
const state = params.get('state');
|
|
7550
|
+
if (!code || !state) {
|
|
7551
|
+
this._error.set({ key: 'auth.social.state.invalid' });
|
|
7552
|
+
return;
|
|
7553
|
+
}
|
|
7554
|
+
this.socialApi
|
|
7555
|
+
.callback({ code, state, redirectUri: this.socialApi.redirectUri })
|
|
7556
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7557
|
+
.subscribe({
|
|
7558
|
+
next: (response) => {
|
|
7559
|
+
if (!response.data) {
|
|
7560
|
+
this._error.set({ key: 'auth.social.failed' });
|
|
7561
|
+
return;
|
|
7562
|
+
}
|
|
7563
|
+
if (isCompanySelectionResponse(response)) {
|
|
7564
|
+
this.authState.setSelectionState(response.data);
|
|
7565
|
+
this.router.navigate(['/auth/login']);
|
|
7566
|
+
return;
|
|
7567
|
+
}
|
|
7568
|
+
if (isLoginResponse(response)) {
|
|
7569
|
+
this.authState.setLoginState(response.data);
|
|
7570
|
+
this.messageService.add({
|
|
7571
|
+
severity: 'success',
|
|
7572
|
+
summary: this.translate('shared.success'),
|
|
7573
|
+
detail: response.messageKey
|
|
7574
|
+
? this.translate(response.messageKey)
|
|
7575
|
+
: (response.message ?? this.translate('auth.login.success')),
|
|
7576
|
+
});
|
|
7577
|
+
this.authState.navigateAfterLogin(this.returnUrl());
|
|
7578
|
+
}
|
|
7579
|
+
},
|
|
7580
|
+
error: (error) => {
|
|
7581
|
+
const messageKey = error?.error?.messageKey;
|
|
7582
|
+
if (messageKey) {
|
|
7583
|
+
this._error.set({ key: messageKey, vars: error?.error?.messageVariables });
|
|
7584
|
+
}
|
|
7585
|
+
else if (error?.error?.message) {
|
|
7586
|
+
this._error.set({ raw: error.error.message });
|
|
7587
|
+
}
|
|
7588
|
+
else {
|
|
7589
|
+
this._error.set({ key: 'auth.social.failed' });
|
|
7590
|
+
}
|
|
7591
|
+
},
|
|
7592
|
+
});
|
|
7593
|
+
}
|
|
7594
|
+
returnUrl() {
|
|
7595
|
+
return this.socialApi.consumeReturnUrl();
|
|
7596
|
+
}
|
|
7597
|
+
translate(key, vars) {
|
|
7598
|
+
return this.translateAdapter?.translate(key, vars) ?? key;
|
|
7599
|
+
}
|
|
7600
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialCallbackPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7601
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: SocialCallbackPageComponent, isStandalone: true, selector: "lib-social-callback-page", ngImport: i0, template: `
|
|
7602
|
+
<app-floating-configurator />
|
|
7603
|
+
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
7604
|
+
<div class="flex flex-col items-center gap-4 text-center px-4">
|
|
7605
|
+
@if (errorDetail()) {
|
|
7606
|
+
<ui-icon name="exclamation-circle" class="text-6xl text-danger"></ui-icon>
|
|
7607
|
+
<div class="text-2xl font-medium text-color">
|
|
7608
|
+
{{ 'auth.social.failed' | translate }}
|
|
7609
|
+
</div>
|
|
7610
|
+
<p class="text-muted-color font-medium">{{ errorDetail() }}</p>
|
|
7611
|
+
<button fButton severity="secondary" [outlined]="true" (click)="backToLogin()">
|
|
7612
|
+
<span>{{ 'auth.login.back.to.sign.in' | translate }}</span>
|
|
7613
|
+
</button>
|
|
7614
|
+
} @else {
|
|
7615
|
+
<ui-icon name="spinner" [spin]="true" class="text-5xl text-primary"></ui-icon>
|
|
7616
|
+
<div class="text-2xl font-medium text-color">
|
|
7617
|
+
{{ 'auth.social.signing.in' | translate }}
|
|
7618
|
+
</div>
|
|
7619
|
+
}
|
|
7620
|
+
</div>
|
|
7621
|
+
</div>
|
|
7622
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: UiModule }, { kind: "directive", type: i1$1.FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "component", type: i1$1.UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "component", type: AppFloatingConfigurator, selector: "app-floating-configurator" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
7623
|
+
}
|
|
7624
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialCallbackPageComponent, decorators: [{
|
|
7625
|
+
type: Component,
|
|
7626
|
+
args: [{
|
|
7627
|
+
selector: 'lib-social-callback-page',
|
|
7628
|
+
imports: [UiModule, TranslatePipe, AppFloatingConfigurator],
|
|
7629
|
+
template: `
|
|
7630
|
+
<app-floating-configurator />
|
|
7631
|
+
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
7632
|
+
<div class="flex flex-col items-center gap-4 text-center px-4">
|
|
7633
|
+
@if (errorDetail()) {
|
|
7634
|
+
<ui-icon name="exclamation-circle" class="text-6xl text-danger"></ui-icon>
|
|
7635
|
+
<div class="text-2xl font-medium text-color">
|
|
7636
|
+
{{ 'auth.social.failed' | translate }}
|
|
7637
|
+
</div>
|
|
7638
|
+
<p class="text-muted-color font-medium">{{ errorDetail() }}</p>
|
|
7639
|
+
<button fButton severity="secondary" [outlined]="true" (click)="backToLogin()">
|
|
7640
|
+
<span>{{ 'auth.login.back.to.sign.in' | translate }}</span>
|
|
7641
|
+
</button>
|
|
7642
|
+
} @else {
|
|
7643
|
+
<ui-icon name="spinner" [spin]="true" class="text-5xl text-primary"></ui-icon>
|
|
7644
|
+
<div class="text-2xl font-medium text-color">
|
|
7645
|
+
{{ 'auth.social.signing.in' | translate }}
|
|
7646
|
+
</div>
|
|
7647
|
+
}
|
|
7648
|
+
</div>
|
|
7649
|
+
</div>
|
|
7650
|
+
`,
|
|
7651
|
+
}]
|
|
7652
|
+
}], ctorParameters: () => [] });
|
|
7653
|
+
|
|
7654
|
+
var socialCallbackPage_component = /*#__PURE__*/Object.freeze({
|
|
7655
|
+
__proto__: null,
|
|
7656
|
+
SocialCallbackPageComponent: SocialCallbackPageComponent
|
|
7657
|
+
});
|
|
7658
|
+
|
|
7659
|
+
const TENANT_PROVIDER = SocialProviderEnum.MICROSOFT;
|
|
7660
|
+
class SocialConfigPageComponent {
|
|
7661
|
+
configService = inject(SocialAuthConfigApiService);
|
|
7662
|
+
socialApi = inject(SocialAuthApiService);
|
|
7663
|
+
messageService = inject(MessageService);
|
|
7664
|
+
translateAdapter = inject(TranslateService, {
|
|
7665
|
+
optional: true,
|
|
7666
|
+
});
|
|
7667
|
+
tenantProvider = TENANT_PROVIDER;
|
|
7668
|
+
_forms = signal(SOCIAL_PROVIDERS.map(emptyForm), /* @ts-ignore */
|
|
7669
|
+
...(ngDevMode ? [{ debugName: "_forms" }] : /* istanbul ignore next */ []));
|
|
7670
|
+
forms = this._forms.asReadonly();
|
|
7671
|
+
_isLoading = signal(true, /* @ts-ignore */
|
|
7672
|
+
...(ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []));
|
|
7673
|
+
isLoading = this._isLoading.asReadonly();
|
|
7674
|
+
_isSaving = signal(false, /* @ts-ignore */
|
|
7675
|
+
...(ngDevMode ? [{ debugName: "_isSaving" }] : /* istanbul ignore next */ []));
|
|
7676
|
+
isSaving = this._isSaving.asReadonly();
|
|
7677
|
+
redirectUri = this.socialApi.redirectUri;
|
|
7678
|
+
constructor() {
|
|
7679
|
+
void this.loadConfigs();
|
|
7680
|
+
}
|
|
7681
|
+
updateField(provider, field, value) {
|
|
7682
|
+
this._forms.update((forms) => forms.map((form) => form.provider === provider ? { ...form, [field]: value } : form));
|
|
7683
|
+
}
|
|
7684
|
+
async onSave() {
|
|
7685
|
+
const invalid = this._forms().find((form) => form.isEnabled &&
|
|
7686
|
+
(!form.clientId.trim() || (!form.hasClientSecret && !form.clientSecret.trim())));
|
|
7687
|
+
if (invalid) {
|
|
7688
|
+
this.messageService.add({
|
|
7689
|
+
severity: 'warn',
|
|
7690
|
+
summary: this.translate('shared.validation'),
|
|
7691
|
+
detail: this.translate('social.config.credentials.required', {
|
|
7692
|
+
provider: this.translate(`auth.social.provider.${invalid.provider}`),
|
|
7693
|
+
}),
|
|
7694
|
+
});
|
|
7695
|
+
return;
|
|
7696
|
+
}
|
|
7697
|
+
this._isSaving.set(true);
|
|
7698
|
+
try {
|
|
7699
|
+
const pending = this._forms().filter((form) => form.id || form.isEnabled);
|
|
7700
|
+
await this.configService.bulkUpsert(pending.filter((form) => !form.id).map(toCreateDto), pending.filter((form) => form.id).map(toUpdateDto));
|
|
7701
|
+
this.messageService.add({
|
|
7702
|
+
severity: 'success',
|
|
7703
|
+
summary: this.translate('shared.success'),
|
|
7704
|
+
detail: this.translate('social.config.save.success'),
|
|
7705
|
+
});
|
|
7706
|
+
await this.loadConfigs();
|
|
7707
|
+
}
|
|
7708
|
+
catch {
|
|
7709
|
+
// Error toast handled by global interceptor
|
|
7710
|
+
}
|
|
7711
|
+
finally {
|
|
7712
|
+
this._isSaving.set(false);
|
|
7713
|
+
}
|
|
7714
|
+
}
|
|
7715
|
+
async loadConfigs() {
|
|
7716
|
+
this._isLoading.set(true);
|
|
7717
|
+
try {
|
|
7718
|
+
const response = await this.configService.getAll({
|
|
7719
|
+
pagination: { currentPage: 0, pageSize: SOCIAL_PROVIDERS.length },
|
|
7720
|
+
filter: {},
|
|
7721
|
+
select: [],
|
|
7722
|
+
sort: {},
|
|
7723
|
+
});
|
|
7724
|
+
this._forms.set(mergeConfigs(response.data ?? []));
|
|
7725
|
+
}
|
|
7726
|
+
catch {
|
|
7727
|
+
// Error toast handled by global interceptor
|
|
7728
|
+
}
|
|
7729
|
+
finally {
|
|
7730
|
+
this._isLoading.set(false);
|
|
7731
|
+
}
|
|
7732
|
+
}
|
|
7733
|
+
translate(key, vars) {
|
|
7734
|
+
return this.translateAdapter?.translate(key, vars) ?? key;
|
|
7735
|
+
}
|
|
7736
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialConfigPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7737
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: SocialConfigPageComponent, isStandalone: true, selector: "lib-social-config-page", providers: [MessageService], ngImport: i0, template: `
|
|
7738
|
+
<div class="card">
|
|
7739
|
+
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
|
|
7740
|
+
<div>
|
|
7741
|
+
<h3 class="text-lg sm:text-xl font-semibold m-0">
|
|
7742
|
+
{{ 'social.config.title' | translate }}
|
|
7743
|
+
</h3>
|
|
7744
|
+
<p class="text-sm text-muted-color mt-1">
|
|
7745
|
+
{{ 'social.config.subtitle' | translate }}
|
|
7746
|
+
</p>
|
|
7747
|
+
</div>
|
|
7748
|
+
<button fButton (click)="onSave()" [disabled]="isSaving()" [loading]="isSaving()">
|
|
7749
|
+
@if (!isSaving()) {
|
|
7750
|
+
<ui-icon pButtonIcon name="save"></ui-icon>
|
|
7751
|
+
}
|
|
7752
|
+
<span pButtonLabel>{{ 'shared.save' | translate }}</span>
|
|
7753
|
+
</button>
|
|
7754
|
+
</div>
|
|
7755
|
+
|
|
7756
|
+
<p class="text-sm text-muted-color mb-4">
|
|
7757
|
+
{{ 'social.config.redirect.uri' | translate }}:
|
|
7758
|
+
<code class="text-color">{{ redirectUri }}</code>
|
|
7759
|
+
</p>
|
|
7760
|
+
|
|
7761
|
+
@if (isLoading()) {
|
|
7762
|
+
<div class="flex justify-center py-8">
|
|
7763
|
+
<ui-icon name="spinner" [spin]="true" class="text-3xl text-primary"></ui-icon>
|
|
7764
|
+
</div>
|
|
7765
|
+
} @else {
|
|
7766
|
+
<div class="flex flex-col gap-4">
|
|
7767
|
+
@for (form of forms(); track form.provider) {
|
|
7768
|
+
<div class="border border-surface rounded p-4">
|
|
7769
|
+
<div class="flex items-center justify-between gap-3">
|
|
7770
|
+
<div>
|
|
7771
|
+
<div class="font-semibold">
|
|
7772
|
+
{{ 'auth.social.provider.' + form.provider | translate }}
|
|
7773
|
+
</div>
|
|
7774
|
+
<div class="text-sm text-muted-color">
|
|
7775
|
+
{{ 'social.config.provider.hint.' + form.provider | translate }}
|
|
7776
|
+
</div>
|
|
7777
|
+
</div>
|
|
7778
|
+
<f-toggleswitch
|
|
7779
|
+
[ngModel]="form.isEnabled"
|
|
7780
|
+
(ngModelChange)="updateField(form.provider, 'isEnabled', $event)"
|
|
7781
|
+
/>
|
|
7782
|
+
</div>
|
|
7783
|
+
|
|
7784
|
+
@if (form.isEnabled) {
|
|
7785
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 pt-4 border-t border-surface">
|
|
7786
|
+
<div class="field">
|
|
7787
|
+
<label [for]="form.provider + '-client-id'" class="block font-medium mb-2">
|
|
7788
|
+
{{ 'social.config.client.id' | translate }} *
|
|
7789
|
+
</label>
|
|
7790
|
+
<input
|
|
7791
|
+
fInputText
|
|
7792
|
+
[id]="form.provider + '-client-id'"
|
|
7793
|
+
class="w-full"
|
|
7794
|
+
[ngModel]="form.clientId"
|
|
7795
|
+
(ngModelChange)="updateField(form.provider, 'clientId', $event)"
|
|
7796
|
+
/>
|
|
7797
|
+
</div>
|
|
7798
|
+
|
|
7799
|
+
<div class="field">
|
|
7800
|
+
<label [for]="form.provider + '-client-secret'" class="block font-medium mb-2">
|
|
7801
|
+
{{ 'social.config.client.secret' | translate }}
|
|
7802
|
+
@if (!form.hasClientSecret) {
|
|
7803
|
+
*
|
|
7804
|
+
}
|
|
7805
|
+
</label>
|
|
7806
|
+
<f-password
|
|
7807
|
+
[inputId]="form.provider + '-client-secret'"
|
|
7808
|
+
styleClass="w-full"
|
|
7809
|
+
[placeholder]="
|
|
7810
|
+
form.hasClientSecret
|
|
7811
|
+
? ('social.config.client.secret.unchanged' | translate)
|
|
7812
|
+
: ''
|
|
7813
|
+
"
|
|
7814
|
+
[ngModel]="form.clientSecret"
|
|
7815
|
+
(ngModelChange)="updateField(form.provider, 'clientSecret', $event)"
|
|
7816
|
+
/>
|
|
7817
|
+
</div>
|
|
7818
|
+
|
|
7819
|
+
@if (form.provider === tenantProvider) {
|
|
7820
|
+
<div class="field">
|
|
7821
|
+
<label [for]="form.provider + '-tenant'" class="block font-medium mb-2">
|
|
7822
|
+
{{ 'social.config.tenant.id' | translate }}
|
|
7823
|
+
</label>
|
|
7824
|
+
<input
|
|
7825
|
+
fInputText
|
|
7826
|
+
[id]="form.provider + '-tenant'"
|
|
7827
|
+
class="w-full"
|
|
7828
|
+
[placeholder]="'social.config.tenant.id.example' | translate"
|
|
7829
|
+
[ngModel]="form.tenantId"
|
|
7830
|
+
(ngModelChange)="updateField(form.provider, 'tenantId', $event)"
|
|
7831
|
+
/>
|
|
7832
|
+
</div>
|
|
7833
|
+
}
|
|
7834
|
+
|
|
7835
|
+
<div class="field">
|
|
7836
|
+
<label [for]="form.provider + '-scopes'" class="block font-medium mb-2">
|
|
7837
|
+
{{ 'social.config.scopes' | translate }}
|
|
7838
|
+
</label>
|
|
7839
|
+
<input
|
|
7840
|
+
fInputText
|
|
7841
|
+
[id]="form.provider + '-scopes'"
|
|
7842
|
+
class="w-full"
|
|
7843
|
+
[placeholder]="'social.config.scopes.placeholder' | translate"
|
|
7844
|
+
[ngModel]="form.scopes"
|
|
7845
|
+
(ngModelChange)="updateField(form.provider, 'scopes', $event)"
|
|
7846
|
+
/>
|
|
7847
|
+
</div>
|
|
7848
|
+
|
|
7849
|
+
</div>
|
|
7850
|
+
}
|
|
7851
|
+
</div>
|
|
7852
|
+
}
|
|
7853
|
+
</div>
|
|
7854
|
+
}
|
|
7855
|
+
</div>
|
|
7856
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: UiModule }, { kind: "directive", type: i1$1.FButtonDirective, selector: "[fButton]", inputs: ["severity", "text", "outlined", "link", "variant", "rounded", "raised", "fluid", "disabled", "loading", "loadingIcon", "size", "label", "icon", "iconPos", "ariaLabel", "plain"] }, { kind: "directive", type: i1$1.InputTextDirective, selector: "[fInputText]", inputs: ["invalid", "touched", "variant", "size", "fluid"] }, { kind: "component", type: i1$1.PasswordComponent, selector: "f-password", inputs: ["toggleMask", "feedback", "disabled", "placeholder", "inputId", "invalid", "touched", "inputStyleClass", "styleClass", "style", "ariaDescribedby", "autocomplete", "variant", "size", "fluid", "appendTo", "promptLabel", "weakLabel", "mediumLabel", "strongLabel", "mediumRegex", "strongRegex"], outputs: ["onBlur", "onFocus"] }, { kind: "component", type: i1$1.ToggleSwitchComponent, selector: "f-toggleswitch", inputs: ["disabled", "readonly", "invalid", "ariaLabel", "trueValue", "falseValue", "inputId", "size", "tabindex", "styleClass", "style"] }, { kind: "component", type: i1$1.UiIconComponent, selector: "ui-icon", inputs: ["name", "size", "spin", "fill", "severity"] }, { kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
7857
|
+
}
|
|
7858
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: SocialConfigPageComponent, decorators: [{
|
|
7859
|
+
type: Component,
|
|
7860
|
+
args: [{
|
|
7861
|
+
selector: 'lib-social-config-page',
|
|
7862
|
+
imports: [UiModule, AngularModule, TranslatePipe],
|
|
7863
|
+
providers: [MessageService],
|
|
7864
|
+
template: `
|
|
7865
|
+
<div class="card">
|
|
7866
|
+
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
|
|
7867
|
+
<div>
|
|
7868
|
+
<h3 class="text-lg sm:text-xl font-semibold m-0">
|
|
7869
|
+
{{ 'social.config.title' | translate }}
|
|
7870
|
+
</h3>
|
|
7871
|
+
<p class="text-sm text-muted-color mt-1">
|
|
7872
|
+
{{ 'social.config.subtitle' | translate }}
|
|
7873
|
+
</p>
|
|
7874
|
+
</div>
|
|
7875
|
+
<button fButton (click)="onSave()" [disabled]="isSaving()" [loading]="isSaving()">
|
|
7876
|
+
@if (!isSaving()) {
|
|
7877
|
+
<ui-icon pButtonIcon name="save"></ui-icon>
|
|
7878
|
+
}
|
|
7879
|
+
<span pButtonLabel>{{ 'shared.save' | translate }}</span>
|
|
7880
|
+
</button>
|
|
7881
|
+
</div>
|
|
7882
|
+
|
|
7883
|
+
<p class="text-sm text-muted-color mb-4">
|
|
7884
|
+
{{ 'social.config.redirect.uri' | translate }}:
|
|
7885
|
+
<code class="text-color">{{ redirectUri }}</code>
|
|
7886
|
+
</p>
|
|
7887
|
+
|
|
7888
|
+
@if (isLoading()) {
|
|
7889
|
+
<div class="flex justify-center py-8">
|
|
7890
|
+
<ui-icon name="spinner" [spin]="true" class="text-3xl text-primary"></ui-icon>
|
|
7891
|
+
</div>
|
|
7892
|
+
} @else {
|
|
7893
|
+
<div class="flex flex-col gap-4">
|
|
7894
|
+
@for (form of forms(); track form.provider) {
|
|
7895
|
+
<div class="border border-surface rounded p-4">
|
|
7896
|
+
<div class="flex items-center justify-between gap-3">
|
|
7897
|
+
<div>
|
|
7898
|
+
<div class="font-semibold">
|
|
7899
|
+
{{ 'auth.social.provider.' + form.provider | translate }}
|
|
7900
|
+
</div>
|
|
7901
|
+
<div class="text-sm text-muted-color">
|
|
7902
|
+
{{ 'social.config.provider.hint.' + form.provider | translate }}
|
|
7903
|
+
</div>
|
|
7904
|
+
</div>
|
|
7905
|
+
<f-toggleswitch
|
|
7906
|
+
[ngModel]="form.isEnabled"
|
|
7907
|
+
(ngModelChange)="updateField(form.provider, 'isEnabled', $event)"
|
|
7908
|
+
/>
|
|
7909
|
+
</div>
|
|
7910
|
+
|
|
7911
|
+
@if (form.isEnabled) {
|
|
7912
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 pt-4 border-t border-surface">
|
|
7913
|
+
<div class="field">
|
|
7914
|
+
<label [for]="form.provider + '-client-id'" class="block font-medium mb-2">
|
|
7915
|
+
{{ 'social.config.client.id' | translate }} *
|
|
7916
|
+
</label>
|
|
7917
|
+
<input
|
|
7918
|
+
fInputText
|
|
7919
|
+
[id]="form.provider + '-client-id'"
|
|
7920
|
+
class="w-full"
|
|
7921
|
+
[ngModel]="form.clientId"
|
|
7922
|
+
(ngModelChange)="updateField(form.provider, 'clientId', $event)"
|
|
7923
|
+
/>
|
|
7924
|
+
</div>
|
|
7925
|
+
|
|
7926
|
+
<div class="field">
|
|
7927
|
+
<label [for]="form.provider + '-client-secret'" class="block font-medium mb-2">
|
|
7928
|
+
{{ 'social.config.client.secret' | translate }}
|
|
7929
|
+
@if (!form.hasClientSecret) {
|
|
7930
|
+
*
|
|
7931
|
+
}
|
|
7932
|
+
</label>
|
|
7933
|
+
<f-password
|
|
7934
|
+
[inputId]="form.provider + '-client-secret'"
|
|
7935
|
+
styleClass="w-full"
|
|
7936
|
+
[placeholder]="
|
|
7937
|
+
form.hasClientSecret
|
|
7938
|
+
? ('social.config.client.secret.unchanged' | translate)
|
|
7939
|
+
: ''
|
|
7940
|
+
"
|
|
7941
|
+
[ngModel]="form.clientSecret"
|
|
7942
|
+
(ngModelChange)="updateField(form.provider, 'clientSecret', $event)"
|
|
7943
|
+
/>
|
|
7944
|
+
</div>
|
|
7945
|
+
|
|
7946
|
+
@if (form.provider === tenantProvider) {
|
|
7947
|
+
<div class="field">
|
|
7948
|
+
<label [for]="form.provider + '-tenant'" class="block font-medium mb-2">
|
|
7949
|
+
{{ 'social.config.tenant.id' | translate }}
|
|
7950
|
+
</label>
|
|
7951
|
+
<input
|
|
7952
|
+
fInputText
|
|
7953
|
+
[id]="form.provider + '-tenant'"
|
|
7954
|
+
class="w-full"
|
|
7955
|
+
[placeholder]="'social.config.tenant.id.example' | translate"
|
|
7956
|
+
[ngModel]="form.tenantId"
|
|
7957
|
+
(ngModelChange)="updateField(form.provider, 'tenantId', $event)"
|
|
7958
|
+
/>
|
|
7959
|
+
</div>
|
|
7960
|
+
}
|
|
7961
|
+
|
|
7962
|
+
<div class="field">
|
|
7963
|
+
<label [for]="form.provider + '-scopes'" class="block font-medium mb-2">
|
|
7964
|
+
{{ 'social.config.scopes' | translate }}
|
|
7965
|
+
</label>
|
|
7966
|
+
<input
|
|
7967
|
+
fInputText
|
|
7968
|
+
[id]="form.provider + '-scopes'"
|
|
7969
|
+
class="w-full"
|
|
7970
|
+
[placeholder]="'social.config.scopes.placeholder' | translate"
|
|
7971
|
+
[ngModel]="form.scopes"
|
|
7972
|
+
(ngModelChange)="updateField(form.provider, 'scopes', $event)"
|
|
7973
|
+
/>
|
|
7974
|
+
</div>
|
|
7975
|
+
|
|
7976
|
+
</div>
|
|
7977
|
+
}
|
|
7978
|
+
</div>
|
|
7979
|
+
}
|
|
7980
|
+
</div>
|
|
7981
|
+
}
|
|
7982
|
+
</div>
|
|
7983
|
+
`,
|
|
7984
|
+
}]
|
|
7985
|
+
}], ctorParameters: () => [] });
|
|
7986
|
+
function emptyForm(provider) {
|
|
7987
|
+
return {
|
|
7988
|
+
provider,
|
|
7989
|
+
id: null,
|
|
7990
|
+
isEnabled: false,
|
|
7991
|
+
clientId: '',
|
|
7992
|
+
clientSecret: '',
|
|
7993
|
+
hasClientSecret: false,
|
|
7994
|
+
tenantId: '',
|
|
7995
|
+
scopes: '',
|
|
7996
|
+
};
|
|
7997
|
+
}
|
|
7998
|
+
function mergeConfigs(configs) {
|
|
7999
|
+
return SOCIAL_PROVIDERS.map((provider) => {
|
|
8000
|
+
const config = configs.find((entry) => entry.provider === provider);
|
|
8001
|
+
if (!config)
|
|
8002
|
+
return emptyForm(provider);
|
|
8003
|
+
return {
|
|
8004
|
+
provider,
|
|
8005
|
+
id: config.id,
|
|
8006
|
+
isEnabled: config.isEnabled,
|
|
8007
|
+
clientId: config.clientId ?? '',
|
|
8008
|
+
clientSecret: '',
|
|
8009
|
+
hasClientSecret: config.hasClientSecret,
|
|
8010
|
+
tenantId: config.extraConfig?.['tenantId'] ?? '',
|
|
8011
|
+
scopes: (config.scopes ?? []).join(', '),
|
|
8012
|
+
};
|
|
8013
|
+
});
|
|
8014
|
+
}
|
|
8015
|
+
/** clientSecret is omitted here; callers decide whether to include a newly typed value. */
|
|
8016
|
+
function toBaseDto(form) {
|
|
8017
|
+
const scopes = form.scopes
|
|
8018
|
+
.split(',')
|
|
8019
|
+
.map((scope) => scope.trim())
|
|
8020
|
+
.filter(Boolean);
|
|
8021
|
+
return {
|
|
8022
|
+
provider: form.provider,
|
|
8023
|
+
isEnabled: form.isEnabled,
|
|
8024
|
+
clientId: form.clientId.trim(),
|
|
8025
|
+
...(scopes.length ? { scopes } : {}),
|
|
8026
|
+
...(form.provider === TENANT_PROVIDER && form.tenantId.trim()
|
|
8027
|
+
? { extraConfig: { tenantId: form.tenantId.trim() } }
|
|
8028
|
+
: {}),
|
|
8029
|
+
};
|
|
8030
|
+
}
|
|
8031
|
+
function toCreateDto(form) {
|
|
8032
|
+
return { ...toBaseDto(form), clientSecret: form.clientSecret.trim() };
|
|
8033
|
+
}
|
|
8034
|
+
function toUpdateDto(form) {
|
|
8035
|
+
const trimmedSecret = form.clientSecret.trim();
|
|
8036
|
+
return {
|
|
8037
|
+
id: form.id,
|
|
8038
|
+
...toBaseDto(form),
|
|
8039
|
+
// Blank means "keep the previously stored secret" - never overwrite it with an empty value.
|
|
8040
|
+
...(trimmedSecret ? { clientSecret: trimmedSecret } : {}),
|
|
8041
|
+
};
|
|
8042
|
+
}
|
|
8043
|
+
|
|
8044
|
+
var socialConfigPage_component = /*#__PURE__*/Object.freeze({
|
|
8045
|
+
__proto__: null,
|
|
8046
|
+
SocialConfigPageComponent: SocialConfigPageComponent
|
|
8047
|
+
});
|
|
8048
|
+
|
|
7128
8049
|
class UserFormComponent {
|
|
7129
8050
|
destroyRef = inject(DestroyRef);
|
|
7130
8051
|
router = inject(Router);
|
|
@@ -7706,6 +8627,7 @@ class UserFormComponent {
|
|
|
7706
8627
|
<div class="flex justify-end gap-4 md:col-span-2 pt-4">
|
|
7707
8628
|
<button
|
|
7708
8629
|
fButton
|
|
8630
|
+
type="button"
|
|
7709
8631
|
severity="secondary"
|
|
7710
8632
|
[outlined]="true"
|
|
7711
8633
|
(click)="onCancel()"
|
|
@@ -8007,6 +8929,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
|
|
|
8007
8929
|
<div class="flex justify-end gap-4 md:col-span-2 pt-4">
|
|
8008
8930
|
<button
|
|
8009
8931
|
fButton
|
|
8932
|
+
type="button"
|
|
8010
8933
|
severity="secondary"
|
|
8011
8934
|
[outlined]="true"
|
|
8012
8935
|
(click)="onCancel()"
|
|
@@ -9780,11 +10703,11 @@ var verifyEmailPage_component = /*#__PURE__*/Object.freeze({
|
|
|
9780
10703
|
// =============================================================================
|
|
9781
10704
|
// @flusys/ng-auth - Authentication Package
|
|
9782
10705
|
// =============================================================================
|
|
9783
|
-
//
|
|
10706
|
+
// Enums
|
|
9784
10707
|
|
|
9785
10708
|
/**
|
|
9786
10709
|
* Generated bundle index. Do not edit.
|
|
9787
10710
|
*/
|
|
9788
10711
|
|
|
9789
|
-
export { ADMINISTRATION_ROUTES, AUTH_ENDPOINTS, AUTH_MESSAGES, AUTH_PROFILE_EXTENSION, AUTH_REGISTRATION_EXTENSION, AUTH_ROUTES, AUTH_USER_DETAIL_CONFIG, AUTH_USER_FORM_EXTENSION, AUTH_USER_LIST_EXTENSION, AUTH_VALIDATION_CONFIG, AdministrationPageComponent, AuthApiService, AuthCompanyApiProviderAdapter, AuthInitService, AuthLayoutApiAdapter, AuthLayoutStateAdapter, AuthStateProviderAdapter, AuthStateService, AuthUserPermissionProviderAdapter, AuthUserProviderAdapter, BranchApiService, BranchFormComponent, BranchListComponent, CompanyApiService, CompanyFormComponent, CompanyListComponent, ForgotPasswordPageComponent, LoginPageComponent, PROFILE_FIELD_CONTEXT, PROFILE_ROUTES, ProfilePageComponent, REGISTRATION_FIELD_CONTEXT, RegisterPageComponent, ResetPasswordPageComponent, TokenRefreshStateService, USER_FORM_FIELD_CONTEXT, UserApiService, UserBranchPermissionDialogComponent, UserCompanyPermissionDialogComponent, UserFormComponent, UserListComponent, UserPermissionApiService, VerifyEmailPageComponent, authGuard, authInterceptor, companyFeatureGuard, emailFeatureGuard, guestGuard, isCompanySelectionResponse, isEmailVerificationRequired, isEndpoint, isLoginResponse, provideAuthLayoutIntegration, provideAuthProviders, signUpFeatureGuard, tokenRefreshInterceptor, validateEmail, validatePassword };
|
|
10712
|
+
export { ADMINISTRATION_ROUTES, AUTH_ENDPOINTS, AUTH_MESSAGES, AUTH_PROFILE_EXTENSION, AUTH_REGISTRATION_EXTENSION, AUTH_ROUTES, AUTH_USER_DETAIL_CONFIG, AUTH_USER_FORM_EXTENSION, AUTH_USER_LIST_EXTENSION, AUTH_VALIDATION_CONFIG, AdministrationPageComponent, AuthApiService, AuthCompanyApiProviderAdapter, AuthInitService, AuthLayoutApiAdapter, AuthLayoutStateAdapter, AuthStateProviderAdapter, AuthStateService, AuthUserPermissionProviderAdapter, AuthUserProviderAdapter, BranchApiService, BranchFormComponent, BranchListComponent, CompanyApiService, CompanyFormComponent, CompanyListComponent, ForgotPasswordPageComponent, LoginPageComponent, PROFILE_FIELD_CONTEXT, PROFILE_ROUTES, ProfilePageComponent, REGISTRATION_FIELD_CONTEXT, RegisterPageComponent, ResetPasswordPageComponent, SOCIAL_PROVIDERS, SOCIAL_RETURN_URL_KEY, SocialAuthApiService, SocialAuthConfigApiService, SocialCallbackPageComponent, SocialConfigPageComponent, SocialLoginButtonsComponent, SocialProviderEnum, TokenRefreshStateService, USER_FORM_FIELD_CONTEXT, UserApiService, UserBranchPermissionDialogComponent, UserCompanyPermissionDialogComponent, UserFormComponent, UserListComponent, UserPermissionApiService, VerifyEmailPageComponent, authGuard, authInterceptor, companyFeatureGuard, emailFeatureGuard, guestGuard, isCompanySelectionResponse, isEmailVerificationRequired, isEndpoint, isLoginResponse, provideAuthLayoutIntegration, provideAuthProviders, signUpFeatureGuard, tokenRefreshInterceptor, validateEmail, validatePassword };
|
|
9790
10713
|
//# sourceMappingURL=flusys-ng-auth.mjs.map
|