@flusys/ng-auth 7.0.2 → 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.
@@ -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 */
@@ -3006,6 +3148,240 @@ var forgotPasswordPage_component = /*#__PURE__*/Object.freeze({
3006
3148
  ForgotPasswordPageComponent: ForgotPasswordPageComponent
3007
3149
  });
3008
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
+
3009
3385
  class LoginPageComponent {
3010
3386
  destroyRef = inject(DestroyRef);
3011
3387
  appConfig = inject(APP_CONFIG);
@@ -3031,6 +3407,8 @@ class LoginPageComponent {
3031
3407
  ...(ngDevMode ? [{ debugName: "unverifiedEmail" }] : /* istanbul ignore next */ []));
3032
3408
  savedUserName = signal(this.authState.getSavedUserName(), /* @ts-ignore */
3033
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 */ []));
3034
3412
  loginFormModel = signal({
3035
3413
  email: '',
3036
3414
  password: '',
@@ -3381,12 +3759,12 @@ class LoginPageComponent {
3381
3759
  <!-- Step 1: Credentials -->
3382
3760
  @if (currentStep() === 'credentials') {
3383
3761
  <div class="text-center mb-8">
3384
- <div class="text-3xl font-medium mb-4 text-color">
3762
+ <div
3763
+ class="text-3xl font-medium mb-4 text-color truncate"
3764
+ [title]="savedUserName() ? welcomeBackText() : null"
3765
+ >
3385
3766
  @if (savedUserName()) {
3386
- {{
3387
- 'auth.login.welcome.back'
3388
- | translate: { name: savedUserName()! }
3389
- }}
3767
+ {{ welcomeBackText() }}
3390
3768
  } @else {
3391
3769
  {{ 'auth.login.welcome' | translate }}
3392
3770
  }
@@ -3467,6 +3845,8 @@ class LoginPageComponent {
3467
3845
  <span pButtonLabel>{{ 'auth.login.sign.in' | translate }}</span>
3468
3846
  </button>
3469
3847
 
3848
+ <lib-social-login-buttons [returnUrl]="returnUrl()" />
3849
+
3470
3850
  @if (signUpEnabled()) {
3471
3851
  <div class="flex items-center justify-center mt-4 gap-2">
3472
3852
  <span class="font-medium">{{
@@ -3598,7 +3978,7 @@ class LoginPageComponent {
3598
3978
  </div>
3599
3979
  </div>
3600
3980
  </div>
3601
- `, 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" }] });
3602
3982
  }
3603
3983
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: LoginPageComponent, decorators: [{
3604
3984
  type: Component,
@@ -3607,6 +3987,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
3607
3987
  imports: [UiModule, AngularModule,
3608
3988
  FormField,
3609
3989
  AppFloatingConfigurator,
3990
+ SocialLoginButtonsComponent,
3610
3991
  TranslatePipe,],
3611
3992
  template: `
3612
3993
  <app-floating-configurator />
@@ -3660,12 +4041,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
3660
4041
  <!-- Step 1: Credentials -->
3661
4042
  @if (currentStep() === 'credentials') {
3662
4043
  <div class="text-center mb-8">
3663
- <div class="text-3xl font-medium mb-4 text-color">
4044
+ <div
4045
+ class="text-3xl font-medium mb-4 text-color truncate"
4046
+ [title]="savedUserName() ? welcomeBackText() : null"
4047
+ >
3664
4048
  @if (savedUserName()) {
3665
- {{
3666
- 'auth.login.welcome.back'
3667
- | translate: { name: savedUserName()! }
3668
- }}
4049
+ {{ welcomeBackText() }}
3669
4050
  } @else {
3670
4051
  {{ 'auth.login.welcome' | translate }}
3671
4052
  }
@@ -3746,6 +4127,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
3746
4127
  <span pButtonLabel>{{ 'auth.login.sign.in' | translate }}</span>
3747
4128
  </button>
3748
4129
 
4130
+ <lib-social-login-buttons [returnUrl]="returnUrl()" />
4131
+
3749
4132
  @if (signUpEnabled()) {
3750
4133
  <div class="flex items-center justify-center mt-4 gap-2">
3751
4134
  <span class="font-medium">{{
@@ -7127,6 +7510,542 @@ var resetPasswordPage_component = /*#__PURE__*/Object.freeze({
7127
7510
  ResetPasswordPageComponent: ResetPasswordPageComponent
7128
7511
  });
7129
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
+
7130
8049
  class UserFormComponent {
7131
8050
  destroyRef = inject(DestroyRef);
7132
8051
  router = inject(Router);
@@ -9784,11 +10703,11 @@ var verifyEmailPage_component = /*#__PURE__*/Object.freeze({
9784
10703
  // =============================================================================
9785
10704
  // @flusys/ng-auth - Authentication Package
9786
10705
  // =============================================================================
9787
- // Interfaces (matching NestJS auth API)
10706
+ // Enums
9788
10707
 
9789
10708
  /**
9790
10709
  * Generated bundle index. Do not edit.
9791
10710
  */
9792
10711
 
9793
- 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 };
9794
10713
  //# sourceMappingURL=flusys-ng-auth.mjs.map