@flusys/ng-auth 9.1.2 → 9.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/ng-auth",
3
- "version": "9.1.2",
3
+ "version": "9.2.0",
4
4
  "description": "Authentication module for FLUSYS Angular applications",
5
5
  "license": "MIT",
6
6
  "peerDependencies": {
@@ -8,10 +8,10 @@
8
8
  "@angular/core": ">=22.0.0",
9
9
  "@angular/forms": ">=22.0.0",
10
10
  "@angular/router": ">=22.0.0",
11
- "@flusys/ng-core": ">=9.1.2",
12
- "@flusys/ng-layout": ">=9.1.2",
13
- "@flusys/ng-shared": ">=9.1.2",
14
- "@flusys/ng-ui": ">=9.1.2",
11
+ "@flusys/ng-core": ">=9.2.0",
12
+ "@flusys/ng-layout": ">=9.2.0",
13
+ "@flusys/ng-shared": ">=9.2.0",
14
+ "@flusys/ng-ui": ">=9.2.0",
15
15
  "rxjs": "^7.8.0"
16
16
  },
17
17
  "sideEffects": false,
@@ -452,9 +452,11 @@ export declare class TokenRefreshStateService {
452
452
  readonly isRefreshing: import("@angular/core").Signal<boolean>;
453
453
  private readonly _refreshTokenSubject;
454
454
  readonly refreshTokenSubject$: import("rxjs").Observable<string | null>;
455
+ private readonly _refreshFailed;
456
+ readonly refreshFailed$: import("rxjs").Observable<unknown>;
455
457
  startRefresh(): void;
456
458
  completeRefresh(newToken: string): void;
457
- failRefresh(): void;
459
+ failRefresh(error?: unknown): void;
458
460
  static ɵfac: i0.ɵɵFactoryDeclaration<TokenRefreshStateService, never>;
459
461
  static ɵprov: i0.ɵɵInjectableDeclaration<any>;
460
462
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"flusys-ng-auth-social-config-page.component-DpGmcb9n.mjs","names":[],"sources":["../../../projects/ng-auth/pages/social-config/social-config-page.component.ts"],"sourcesContent":["import { Component, inject, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport {\n TranslatePipe,\n TranslateService,\n} from '@flusys/ng-shared';\nimport {\n FButtonDirective,\n InputTextDirective,\n MessageService,\n PasswordComponent,\n ToggleSwitchComponent,\n UiIconComponent,\n} from '@flusys/ng-ui';\nimport {\n SOCIAL_PROVIDERS,\n SocialProviderEnum,\n} from '../../enums/social-provider.enum';\nimport {\n ICreateSocialAuthConfigDto,\n ISocialAuthConfig,\n IUpdateSocialAuthConfigDto,\n} from '../../interfaces/social-auth.interface';\nimport { SocialAuthApiService } from '../../services/social-auth-api.service';\nimport { SocialAuthConfigApiService } from '../../services/social-auth-config-api.service';\n\ninterface IProviderFormModel {\n provider: SocialProviderEnum;\n id: string | null;\n isEnabled: boolean;\n clientId: string;\n /** A newly typed secret to save. Left blank keeps the previously stored one. */\n clientSecret: string;\n hasClientSecret: boolean;\n tenantId: string;\n scopes: string;\n}\n\nconst TENANT_PROVIDER = SocialProviderEnum.MICROSOFT;\n\n@Component({\n selector: 'lib-social-config-page',\n imports: [\n FButtonDirective,\n InputTextDirective,\n PasswordComponent,\n ToggleSwitchComponent,\n UiIconComponent,\n FormsModule,\n TranslatePipe,\n ],\n providers: [MessageService],\n template: `\n <div class=\"card\">\n <div class=\"flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4\">\n <div>\n <h3 class=\"text-lg sm:text-xl font-semibold m-0\">\n {{ 'social.config.title' | translate }}\n </h3>\n <p class=\"text-sm text-muted-color mt-1\">\n {{ 'social.config.subtitle' | translate }}\n </p>\n </div>\n <button fButton (click)=\"onSave()\" [disabled]=\"isSaving()\" [loading]=\"isSaving()\">\n @if (!isSaving()) {\n <ui-icon pButtonIcon name=\"save\"></ui-icon>\n }\n <span pButtonLabel>{{ 'shared.save' | translate }}</span>\n </button>\n </div>\n\n <p class=\"text-sm text-muted-color mb-4\">\n {{ 'social.config.redirect.uri' | translate }}:\n <code class=\"text-color\">{{ redirectUri }}</code>\n </p>\n\n @if (isLoading()) {\n <div class=\"flex justify-center py-8\">\n <ui-icon name=\"spinner\" [spin]=\"true\" class=\"text-3xl text-primary\"></ui-icon>\n </div>\n } @else {\n <div class=\"flex flex-col gap-4\">\n @for (form of forms(); track form.provider) {\n <div class=\"border border-surface rounded p-4\">\n <div class=\"flex items-center justify-between gap-3\">\n <div>\n <div class=\"font-semibold\">\n {{ 'auth.social.provider.' + form.provider | translate }}\n </div>\n <div class=\"text-sm text-muted-color\">\n {{ 'social.config.provider.hint.' + form.provider | translate }}\n </div>\n </div>\n <f-toggleswitch\n [ngModel]=\"form.isEnabled\"\n (ngModelChange)=\"updateField(form.provider, 'isEnabled', $event)\"\n />\n </div>\n\n @if (form.isEnabled) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 pt-4 border-t border-surface\">\n <div class=\"field\">\n <label [for]=\"form.provider + '-client-id'\" class=\"block font-medium mb-2\">\n {{ 'social.config.client.id' | translate }} *\n </label>\n <input\n fInputText\n [id]=\"form.provider + '-client-id'\"\n class=\"w-full\"\n [ngModel]=\"form.clientId\"\n (ngModelChange)=\"updateField(form.provider, 'clientId', $event)\"\n />\n </div>\n\n <div class=\"field\">\n <label [for]=\"form.provider + '-client-secret'\" class=\"block font-medium mb-2\">\n {{ 'social.config.client.secret' | translate }}\n @if (!form.hasClientSecret) {\n *\n }\n </label>\n <f-password\n [inputId]=\"form.provider + '-client-secret'\"\n styleClass=\"w-full\"\n [placeholder]=\"\n form.hasClientSecret\n ? ('social.config.client.secret.unchanged' | translate)\n : ''\n \"\n [ngModel]=\"form.clientSecret\"\n (ngModelChange)=\"updateField(form.provider, 'clientSecret', $event)\"\n />\n </div>\n\n @if (form.provider === tenantProvider) {\n <div class=\"field\">\n <label [for]=\"form.provider + '-tenant'\" class=\"block font-medium mb-2\">\n {{ 'social.config.tenant.id' | translate }}\n </label>\n <input\n fInputText\n [id]=\"form.provider + '-tenant'\"\n class=\"w-full\"\n [placeholder]=\"'social.config.tenant.id.example' | translate\"\n [ngModel]=\"form.tenantId\"\n (ngModelChange)=\"updateField(form.provider, 'tenantId', $event)\"\n />\n </div>\n }\n\n <div class=\"field\">\n <label [for]=\"form.provider + '-scopes'\" class=\"block font-medium mb-2\">\n {{ 'social.config.scopes' | translate }}\n </label>\n <input\n fInputText\n [id]=\"form.provider + '-scopes'\"\n class=\"w-full\"\n [placeholder]=\"'social.config.scopes.placeholder' | translate\"\n [ngModel]=\"form.scopes\"\n (ngModelChange)=\"updateField(form.provider, 'scopes', $event)\"\n />\n </div>\n\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n `,\n})\nexport class SocialConfigPageComponent {\n private readonly configService = inject(SocialAuthConfigApiService);\n private readonly socialApi = inject(SocialAuthApiService);\n private readonly messageService = inject(MessageService);\n private readonly translateAdapter = inject(TranslateService, {\n optional: true,\n });\n\n readonly tenantProvider = TENANT_PROVIDER;\n\n private readonly _forms = signal<IProviderFormModel[]>(\n SOCIAL_PROVIDERS.map(emptyForm),\n );\n readonly forms = this._forms.asReadonly();\n\n private readonly _isLoading = signal(true);\n readonly isLoading = this._isLoading.asReadonly();\n\n private readonly _isSaving = signal(false);\n readonly isSaving = this._isSaving.asReadonly();\n\n readonly redirectUri = this.socialApi.redirectUri;\n\n constructor() {\n void this.loadConfigs();\n }\n\n updateField<K extends keyof IProviderFormModel>(\n provider: SocialProviderEnum,\n field: K,\n value: IProviderFormModel[K],\n ): void {\n this._forms.update((forms) =>\n forms.map((form) =>\n form.provider === provider ? { ...form, [field]: value } : form,\n ),\n );\n }\n\n async onSave(): Promise<void> {\n const invalid = this._forms().find(\n (form) =>\n form.isEnabled &&\n (!form.clientId.trim() || (!form.hasClientSecret && !form.clientSecret.trim())),\n );\n\n if (invalid) {\n this.messageService.add({\n severity: 'warn',\n summary: this.translate('shared.validation'),\n detail: this.translate('social.config.credentials.required', {\n provider: this.translate(`auth.social.provider.${invalid.provider}`),\n }),\n });\n return;\n }\n\n this._isSaving.set(true);\n try {\n const pending = this._forms().filter((form) => form.id || form.isEnabled);\n await this.configService.bulkUpsert(\n pending.filter((form) => !form.id).map(toCreateDto),\n pending.filter((form) => form.id).map(toUpdateDto),\n );\n\n this.messageService.add({\n severity: 'success',\n summary: this.translate('shared.success'),\n detail: this.translate('social.config.save.success'),\n });\n await this.loadConfigs();\n } catch {\n // Error toast handled by global interceptor\n } finally {\n this._isSaving.set(false);\n }\n }\n\n private async loadConfigs(): Promise<void> {\n this._isLoading.set(true);\n try {\n const response = await this.configService.getAll({\n pagination: { currentPage: 0, pageSize: SOCIAL_PROVIDERS.length },\n filter: {},\n select: [],\n sort: {},\n });\n this._forms.set(mergeConfigs(response.data ?? []));\n } catch {\n // Error toast handled by global interceptor\n } finally {\n this._isLoading.set(false);\n }\n }\n\n private translate(\n key: string,\n vars?: Record<string, string | number>,\n ): string {\n return this.translateAdapter?.translate(key, vars) ?? key;\n }\n}\n\nfunction emptyForm(provider: SocialProviderEnum): IProviderFormModel {\n return {\n provider,\n id: null,\n isEnabled: false,\n clientId: '',\n clientSecret: '',\n hasClientSecret: false,\n tenantId: '',\n scopes: '',\n };\n}\n\nfunction mergeConfigs(configs: ISocialAuthConfig[]): IProviderFormModel[] {\n return SOCIAL_PROVIDERS.map((provider) => {\n const config = configs.find((entry) => entry.provider === provider);\n if (!config) return emptyForm(provider);\n\n return {\n provider,\n id: config.id,\n isEnabled: config.isEnabled,\n clientId: config.clientId ?? '',\n clientSecret: '',\n hasClientSecret: config.hasClientSecret,\n tenantId: (config.extraConfig?.['tenantId'] as string) ?? '',\n scopes: (config.scopes ?? []).join(', '),\n };\n });\n}\n\n/** clientSecret is omitted here; callers decide whether to include a newly typed value. */\nfunction toBaseDto(form: IProviderFormModel): Omit<ICreateSocialAuthConfigDto, 'clientSecret'> {\n const scopes = form.scopes\n .split(',')\n .map((scope) => scope.trim())\n .filter(Boolean);\n\n return {\n provider: form.provider,\n isEnabled: form.isEnabled,\n clientId: form.clientId.trim(),\n ...(scopes.length ? { scopes } : {}),\n ...(form.provider === TENANT_PROVIDER && form.tenantId.trim()\n ? { extraConfig: { tenantId: form.tenantId.trim() } }\n : {}),\n };\n}\n\nfunction toCreateDto(form: IProviderFormModel): ICreateSocialAuthConfigDto {\n return { ...toBaseDto(form), clientSecret: form.clientSecret.trim() };\n}\n\nfunction toUpdateDto(form: IProviderFormModel): IUpdateSocialAuthConfigDto {\n const trimmedSecret = form.clientSecret.trim();\n return {\n id: form.id!,\n ...toBaseDto(form),\n // Blank means \"keep the previously stored secret\" - never overwrite it with an empty value.\n ...(trimmedSecret ? { clientSecret: trimmedSecret } : {}),\n };\n}\n"],"mappings":"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"}