{"version":3,"file":"sdcorejs-angular-modules-keycloak.mjs","sources":["../../../projects/sdcorejs-angular/modules/keycloak/keycloak.configuration.ts","../../../projects/sdcorejs-angular/modules/keycloak/keycloak.service.ts","../../../projects/sdcorejs-angular/modules/keycloak/keycloak.interceptor.ts","../../../projects/sdcorejs-angular/modules/keycloak/keycloak.module.ts","../../../projects/sdcorejs-angular/modules/keycloak/sdcorejs-angular-modules-keycloak.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport interface SdKeycloakTenantConfig {\n  url: string;\n  realm: string;\n  clientId: string;\n  secureRoutes?: string[]; // Các API cần đính token (vd: ['/api/v1'])\n\n  /**\n   * Basename (KHÔNG kèm đuôi `.html`) của file Keycloak **silent SSO redirect** mà consumer\n   * BẮT BUỘC đặt trong thư mục `public/`. Mặc định `'silent-renew'`.\n   *\n   * Service sẽ truyền `${origin}/<silentRenewUrl>.html` vào `silentCheckSsoRedirectUri`. Với giá trị\n   * mặc định, Keycloak gọi `${origin}/silent-renew.html` (giống hành vi cũ — không phá vỡ project hiện có).\n   *\n   * Consumer cần tạo file tĩnh này (nội dung mẫu ở `modules/keycloak/htmls/silent-renew.html`); nếu THIẾU,\n   * silent `check-sso` thất bại và `init()` trả về `false` không mong muốn (user bị coi là chưa đăng nhập).\n   * Cho phép truyền kèm `/` đầu hoặc đuôi `.html` (tự chuẩn hoá), hoặc đặt sâu hơn:\n   * `'assets/silent-renew'` → `${origin}/assets/silent-renew.html`.\n   */\n  silentRenewUrl?: string;\n\n  /**\n   * Basename (KHÔNG kèm đuôi `.html`) của **trang lỗi tĩnh** trong `public/`, dùng làm đích redirect khi\n   * `keycloak.init()` NÉM LỖI (Keycloak sập / mạng lỗi / cấu hình realm sai). Mặc định `'auth-keycloak-error'`.\n   *\n   * Khi init thất bại, app điều hướng full-page tới `${origin}/<authErrorUrl>.html`. Vì là file tĩnh nên\n   * trang lỗi vẫn hiển thị kể cả khi bundle Angular chưa khởi tạo được. Service cũng tự bỏ qua `init()` nếu\n   * đang đứng sẵn ở trang lỗi này (chống vòng lặp redirect).\n   *\n   * Consumer nên tạo file (nội dung mẫu ở `modules/keycloak/htmls/auth-keycloak-error.html`) và viết lại\n   * branding/nội dung. Nếu THIẾU, redirect rơi vào trang 404 của host. Chuẩn hoá giá trị giống `silentRenewUrl`.\n   */\n  authErrorUrl?: string;\n}\n\nexport interface ISdKeycloakConfiguration {\n  loadTenantConfig: () => Promise<SdKeycloakTenantConfig>;\n}\n\nexport const SD_KEYCLOAK_CONFIGURATION = new InjectionToken<ISdKeycloakConfiguration>('sd-keycloak.configuration');\n","import { Injectable } from '@angular/core';\nimport Keycloak from 'keycloak-js'; // Import trực tiếp SDK gốc của Keycloak\nimport { SdKeycloakTenantConfig } from './keycloak.configuration';\n\n@Injectable({ providedIn: 'root' })\nexport class SdKeycloakService {\n  public keycloak!: Keycloak;\n  public config!: SdKeycloakTenantConfig;\n\n  async init(config: SdKeycloakTenantConfig): Promise<boolean> {\n    this.config = config;\n\n    // Đường dẫn 2 file tĩnh trong public/ (consumer cấu hình qua silentRenewUrl / authErrorUrl).\n    const silentRenewHref = this.toPublicHtmlUrl(config.silentRenewUrl || 'silent-renew');\n    const authErrorHref = this.toPublicHtmlUrl(config.authErrorUrl || 'auth-keycloak-error');\n\n    // Nếu đang đứng ở chính trang lỗi → KHÔNG init lại (chống vòng lặp redirect khi init liên tục lỗi).\n    if (window.location.pathname === new URL(authErrorHref).pathname) {\n      return false;\n    }\n\n    // 1. Khởi tạo instance\n    this.keycloak = new Keycloak({\n      url: config.url,\n      realm: config.realm,\n      clientId: config.clientId,\n    });\n\n    // 2. Lắng nghe sự kiện hết hạn token để tự động làm mới ngầm\n    this.keycloak.onTokenExpired = () => {\n      this.keycloak.updateToken(30).catch(() => {\n        // @i18n-ignore — dev console warning\n        console.warn('Token refresh failed. Re-authentication required.');\n        this.keycloak.login();\n      });\n    };\n\n    // 3. Thực thi quá trình boot Keycloak; nếu lỗi → điều hướng tới trang lỗi tĩnh trong public/.\n    try {\n      return await this.keycloak.init({\n        onLoad: 'check-sso',\n        silentCheckSsoRedirectUri: silentRenewHref,\n        checkLoginIframe: false, // Tắt check Iframe để chống lỗi vòng lặp\n      });\n    } catch (error) {\n      // @i18n-ignore — dev console error\n      console.error('Keycloak init failed:', error);\n      window.location.href = authErrorHref;\n      return false;\n    }\n  }\n\n  /**\n   * Chuẩn hoá basename file tĩnh trong public/ thành URL tuyệt đối.\n   * `'silent-renew'` | `'/silent-renew'` | `'silent-renew.html'` → `${origin}/silent-renew.html`.\n   */\n  private toPublicHtmlUrl(name: string): string {\n    const base = name\n      .trim()\n      .replace(/^\\/+/, '')\n      .replace(/\\.html$/i, '');\n    return `${window.location.origin}/${base}.html`;\n  }\n\n  // Tiện ích nhanh cho Dev sử dụng\n  login() {\n    return this.keycloak.login();\n  }\n  logout() {\n    return this.keycloak.logout({ redirectUri: window.location.origin });\n  }\n  getToken() {\n    return this.keycloak.token;\n  }\n  getIsAuthenticated() {\n    return this.keycloak.authenticated;\n  }\n}\n","import { HttpInterceptorFn } from '@angular/common/http';\nimport { inject } from '@angular/core';\nimport { from, switchMap } from 'rxjs';\nimport { SdKeycloakService } from './keycloak.service';\n\nexport const SdKeycloakInterceptor: HttpInterceptorFn = (req, next) => {\n  const keycloakService = inject(SdKeycloakService);\n  const { keycloak, config } = keycloakService;\n\n  // Nếu chưa init xong hoặc chưa đăng nhập -> cho request đi qua bình thường\n  if (!keycloak || !keycloak.authenticated || !config) {\n    return next(req);\n  }\n\n  // Kiểm tra xem URL của request có nằm trong mảng secureRoutes không\n  const isSecure = config.secureRoutes?.some(route => req.url.includes(route));\n  if (!isSecure) {\n    return next(req);\n  }\n\n  // Đảm bảo token luôn hợp lệ (cập nhật nếu token sẽ hết hạn trong 30s tới)\n  return from(keycloak.updateToken(30)).pipe(\n    switchMap(() => {\n      const authReq = req.clone({\n        headers: req.headers.set('Authorization', `Bearer ${keycloak.token}`),\n      });\n      return next(authReq);\n    })\n  );\n};\n","import {\n  ModuleWithProviders,\n  NgModule,\n  Provider,\n  Type,\n  EnvironmentProviders,\n  makeEnvironmentProviders,\n  provideAppInitializer,\n  inject,\n  APP_INITIALIZER,\n} from '@angular/core';\nimport { ISdKeycloakConfiguration, SD_KEYCLOAK_CONFIGURATION } from './keycloak.configuration';\nimport { SdKeycloakService } from './keycloak.service';\n\n// =======================================================================\n// CÁCH 1: Dùng cho Angular 19 Standalone (Gọi trong app.config.ts)\n// =======================================================================\nexport function provideSdKeycloak(options: {\n  useClass?: Type<ISdKeycloakConfiguration>;\n\n  useFactory?: (...args: any[]) => ISdKeycloakConfiguration;\n\n  deps?: any[];\n}): EnvironmentProviders {\n  // FIX 1: Khai báo mảng nhận cả Provider (cho Service/Token) lẫn EnvironmentProviders (cho AppInitializer)\n  const providers: (Provider | EnvironmentProviders)[] = [SdKeycloakService];\n\n  if (options.useFactory) {\n    providers.push({ provide: SD_KEYCLOAK_CONFIGURATION, useFactory: options.useFactory, deps: options.deps || [] });\n  } else if (options.useClass) {\n    providers.push({ provide: SD_KEYCLOAK_CONFIGURATION, useClass: options.useClass });\n  }\n\n  // Standalone dùng được provideAppInitializer ngon lành\n  providers.push(\n    provideAppInitializer(() => {\n      const configLoader = inject(SD_KEYCLOAK_CONFIGURATION);\n      const keycloakService = inject(SdKeycloakService);\n      return configLoader.loadTenantConfig().then(config => keycloakService.init(config));\n    })\n  );\n\n  return makeEnvironmentProviders(providers);\n}\n\n// =======================================================================\n// CÁCH 2: Dùng cho kiến trúc NgModule cũ (Backward Compatibility)\n// =======================================================================\n@NgModule({})\nexport class SdKeycloakModule {\n  static forRoot(options: {\n    useClass?: Type<ISdKeycloakConfiguration>;\n\n    useFactory?: (...args: any[]) => ISdKeycloakConfiguration;\n\n    deps?: any[];\n  }): ModuleWithProviders<SdKeycloakModule> {\n    return {\n      ngModule: SdKeycloakModule,\n      providers: [\n        SdKeycloakService,\n        ...(options.useFactory\n          ? [{ provide: SD_KEYCLOAK_CONFIGURATION, useFactory: options.useFactory, deps: options.deps || [] }]\n          : [{ provide: SD_KEYCLOAK_CONFIGURATION, useClass: options.useClass! }]),\n\n        // FIX 2: NgModule bắt buộc phải dùng APP_INITIALIZER (nhưng viết kiểu xịn của Angular 14+, dùng inject)\n        {\n          provide: APP_INITIALIZER,\n          multi: true,\n          useFactory: () => {\n            const configLoader = inject(SD_KEYCLOAK_CONFIGURATION);\n            const keycloakService = inject(SdKeycloakService);\n            return () => configLoader.loadTenantConfig().then(config => keycloakService.init(config));\n          },\n        },\n      ],\n    };\n  }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}