@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.
- package/README.md +36 -3
- package/fesm2022/flusys-ng-auth.mjs +960 -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 */
|
|
@@ -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
|
|
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
|
|
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
|
-
//
|
|
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
|