[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6JB15VFEVXKP62NJEB7RNP1":3,"skill-same:01M6JB15VFEVXKP62NJEB7RNP1":70},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":13,"repositoryHtmlUrl":46,"repositoryHomepageUrl":6,"ownerHtmlUrl":47,"ownerBlogUrl":6,"ownerDescription":48,"updatedAt":49,"install":50,"resources":63},"01M6JB15VFEVXKP62NJEB7RNP1","delon-auth-authentication-authorization",null,"Implement authentication and authorization using @delon\u002Fauth. Use this skill when adding login\u002Flogout flows, JWT token management, role-based access control (RBAC), route guards, HTTP interceptors, and session management. Integrates with Firebase Auth and custom permission systems. Ensures secure token storage, automatic token refresh, and consistent authorization checks across components and services.","superhealth\u002Fskills-hub","superhealth","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6292112?v=4","01M6JA6E7F21BYK5W3E4P270TT","01M6JA6EC0W8MPHKDJVBZDSEZ0",0,"MIT","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002F7spade\u002Fdelon-auth-authentication-authorization\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"auth","authentication","authorization","delon auth authentication authorization","permission","interceptors","login","token management","flows","session management","Access Control","token","across","storage","route","session","access","secure","rbac","components","2026-11-10 10:12:30",false,"---\nname: delon-auth-authentication-authorization\ndescription: \"Implement authentication and authorization using @delon\u002Fauth. Use this skill when adding login\u002Flogout flows, JWT token management, role-based access control (RBAC), route guards, HTTP interceptors, and session management. Integrates with Firebase Auth and custom permission systems. Ensures secure token storage, automatic token refresh, and consistent authorization checks across components and services.\"\nlicense: \"MIT\"\n---\n\n# @delon\u002Fauth Authentication & Authorization Skill\n\nThis skill helps implement authentication and authorization using @delon\u002Fauth library.\n\n## Core Principles\n\n### Token Management\n- **JWT Storage**: Secure token storage using DA_SERVICE_TOKEN\n- **Auto Refresh**: Automatic token renewal before expiration\n- **Interceptors**: Automatic token injection in HTTP requests\n- **Logout**: Clean token removal and session cleanup\n\n### Authorization\n- **RBAC**: Role-based access control\n- **Permissions**: Fine-grained permission checking\n- **Guards**: Route protection with role\u002Fpermission checks\n- **ACL Integration**: Works with @delon\u002Facl for UI-level controls\n\n## Configuration\n\n### App Config\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fapp.config.ts\nimport { ApplicationConfig } from '@angular\u002Fcore';\nimport { provideDelonAuth, DA_SERVICE_TOKEN, ITokenService } from '@delon\u002Fauth';\nimport { JWTTokenModel } from '@delon\u002Fauth';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideDelonAuth({\n      token_send_key: 'Authorization',\n      token_send_template: 'Bearer ${token}',\n      token_send_place: 'header',\n      login_url: '\u002Fpassport\u002Flogin',\n      ignores: [\u002F\\\u002Flogin\u002F, \u002Fassets\\\u002F\u002F],\n      allow_anonymous_key: 'allow_anonymous',\n      executeOtherInterceptors: true,\n      refreshTime: 3000, \u002F\u002F Refresh token check interval (ms)\n      refreshOffset: 6000 \u002F\u002F Refresh token before expiration (ms)\n    })\n  ]\n};\n```\n\n### Token Model\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fcore\u002Fmodels\u002Fauth-token.model.ts\nimport { JWTTokenModel } from '@delon\u002Fauth';\n\nexport interface AuthTokenModel extends JWTTokenModel {\n  token: string;\n  uid: string;\n  email: string;\n  displayName: string;\n  role: string;\n  permissions: string[];\n  exp: number;\n}\n```\n\n## Authentication Service\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fcore\u002Fservices\u002Fauth.service.ts\nimport { Injectable, inject } from '@angular\u002Fcore';\nimport { Router } from '@angular\u002Frouter';\nimport { DA_SERVICE_TOKEN, ITokenService } from '@delon\u002Fauth';\nimport { Auth, signInWithEmailAndPassword, signOut, User } from '@angular\u002Ffire\u002Fauth';\nimport { AuthTokenModel } from '@core\u002Fmodels\u002Fauth-token.model';\nimport { signal, computed } from '@angular\u002Fcore';\n\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n  private auth = inject(Auth);\n  private tokenService = inject\u003CITokenService>(DA_SERVICE_TOKEN);\n  private router = inject(Router);\n  \n  \u002F\u002F State\n  private currentUser = signal\u003CUser | null>(null);\n  private authToken = signal\u003CAuthTokenModel | null>(null);\n  \n  \u002F\u002F Computed\n  isAuthenticated = computed(() => this.tokenService.get()?.token != null);\n  userRole = computed(() => this.authToken()?.role);\n  userPermissions = computed(() => this.authToken()?.permissions || []);\n  \n  constructor() {\n    \u002F\u002F Listen to Firebase Auth state\n    this.auth.onAuthStateChanged(async (user) => {\n      this.currentUser.set(user);\n      if (user) {\n        await this.refreshToken();\n      } else {\n        this.clearSession();\n      }\n    });\n  }\n  \n  \u002F**\n   * Login with email and password\n   *\u002F\n  async login(email: string, password: string): Promise\u003Cvoid> {\n    try {\n      const credential = await signInWithEmailAndPassword(this.auth, email, password);\n      const token = await credential.user.getIdToken();\n      const tokenData = await this.parseToken(token);\n      \n      \u002F\u002F Store token in @delon\u002Fauth\n      this.tokenService.set(tokenData);\n      this.authToken.set(tokenData);\n      \n      \u002F\u002F Navigate to dashboard\n      await this.router.navigateByUrl('\u002Fdashboard');\n    } catch (error) {\n      console.error('Login failed:', error);\n      throw error;\n    }\n  }\n  \n  \u002F**\n   * Logout\n   *\u002F\n  async logout(): Promise\u003Cvoid> {\n    try {\n      await signOut(this.auth);\n      this.clearSession();\n      await this.router.navigateByUrl('\u002Fpassport\u002Flogin');\n    } catch (error) {\n      console.error('Logout failed:', error);\n      throw error;\n    }\n  }\n  \n  \u002F**\n   * Refresh token\n   *\u002F\n  async refreshToken(): Promise\u003Cvoid> {\n    const user = this.currentUser();\n    if (!user) return;\n    \n    try {\n      const token = await user.getIdToken(true); \u002F\u002F Force refresh\n      const tokenData = await this.parseToken(token);\n      \n      this.tokenService.set(tokenData);\n      this.authToken.set(tokenData);\n    } catch (error) {\n      console.error('Token refresh failed:', error);\n      await this.logout();\n    }\n  }\n  \n  \u002F**\n   * Check if user has specific role\n   *\u002F\n  hasRole(role: string): boolean {\n    return this.userRole() === role;\n  }\n  \n  \u002F**\n   * Check if user has specific permission\n   *\u002F\n  hasPermission(permission: string): boolean {\n    return this.userPermissions().includes(permission);\n  }\n  \n  \u002F**\n   * Check if user has any of the specified permissions\n   *\u002F\n  hasAnyPermission(permissions: string[]): boolean {\n    const userPerms = this.userPermissions();\n    return permissions.some(p => userPerms.includes(p));\n  }\n  \n  \u002F**\n   * Check if user has all of the specified permissions\n   *\u002F\n  hasAllPermissions(permissions: string[]): boolean {\n    const userPerms = this.userPermissions();\n    return permissions.every(p => userPerms.includes(p));\n  }\n  \n  \u002F**\n   * Parse JWT token\n   *\u002F\n  private async parseToken(token: string): Promise\u003CAuthTokenModel> {\n    \u002F\u002F Decode JWT payload\n    const payload = JSON.parse(atob(token.split('.')[1]));\n    \n    \u002F\u002F Load user permissions from Firestore (custom claims)\n    const permissions = await this.loadUserPermissions(payload.uid);\n    \n    return {\n      token,\n      uid: payload.uid,\n      email: payload.email,\n      displayName: payload.name || payload.email,\n      role: payload.role || 'member',\n      permissions,\n      exp: payload.exp\n    };\n  }\n  \n  \u002F**\n   * Load user permissions from Firestore\n   *\u002F\n  private async loadUserPermissions(uid: string): Promise\u003Cstring[]> {\n    \u002F\u002F Implementation: query user's permissions from Firestore\n    \u002F\u002F This is placeholder - implement based on your schema\n    return ['read:tasks', 'write:tasks'];\n  }\n  \n  \u002F**\n   * Clear session\n   *\u002F\n  private clearSession(): void {\n    this.tokenService.clear();\n    this.authToken.set(null);\n    this.currentUser.set(null);\n  }\n}\n```\n\n## Auth Guard\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fcore\u002Fguards\u002Fauth.guard.ts\nimport { inject } from '@angular\u002Fcore';\nimport { Router, CanActivateFn } from '@angular\u002Frouter';\nimport { AuthService } from '@core\u002Fservices\u002Fauth.service';\n\n\u002F**\n * Guard to protect routes requiring authentication\n *\u002F\nexport const authGuard: CanActivateFn = () => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n  \n  if (authService.isAuthenticated()) {\n    return true;\n  }\n  \n  return router.createUrlTree(['\u002Fpassport\u002Flogin']);\n};\n```\n\n## Role Guard\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fcore\u002Fguards\u002Frole.guard.ts\nimport { inject } from '@angular\u002Fcore';\nimport { Router, CanActivateFn, ActivatedRouteSnapshot } from '@angular\u002Frouter';\nimport { AuthService } from '@core\u002Fservices\u002Fauth.service';\n\n\u002F**\n * Guard to protect routes by role\n *\u002F\nexport const roleGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n  \n  const requiredRole = route.data['role'] as string;\n  \n  if (!requiredRole) {\n    console.warn('Role guard used without required role');\n    return true;\n  }\n  \n  if (authService.hasRole(requiredRole)) {\n    return true;\n  }\n  \n  \u002F\u002F Redirect to unauthorized page\n  return router.createUrlTree(['\u002Fexception\u002F403']);\n};\n\n\u002F**\n * Usage in routes:\n * {\n *   path: 'admin',\n *   component: AdminComponent,\n *   canActivate: [authGuard, roleGuard],\n *   data: { role: 'admin' }\n * }\n *\u002F\n```\n\n## Permission Guard\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fcore\u002Fguards\u002Fpermission.guard.ts\nimport { inject } from '@angular\u002Fcore';\nimport { Router, CanActivateFn, ActivatedRouteSnapshot } from '@angular\u002Frouter';\nimport { AuthService } from '@core\u002Fservices\u002Fauth.service';\n\n\u002F**\n * Guard to protect routes by permissions\n *\u002F\nexport const permissionGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n  \n  const requiredPermissions = route.data['permissions'] as string[];\n  const requireAll = route.data['requireAll'] as boolean ?? true;\n  \n  if (!requiredPermissions || requiredPermissions.length === 0) {\n    console.warn('Permission guard used without required permissions');\n    return true;\n  }\n  \n  const hasAccess = requireAll\n    ? authService.hasAllPermissions(requiredPermissions)\n    : authService.hasAnyPermission(requiredPermissions);\n  \n  if (hasAccess) {\n    return true;\n  }\n  \n  return router.createUrlTree(['\u002Fexception\u002F403']);\n};\n\n\u002F**\n * Usage in routes:\n * {\n *   path: 'tasks',\n *   component: TaskListComponent,\n *   canActivate: [authGuard, permissionGuard],\n *   data: { \n *     permissions: ['read:tasks'], \n *     requireAll: true \n *   }\n * }\n *\u002F\n```\n\n## Login Component\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Froutes\u002Fpassport\u002Flogin\u002Flogin.component.ts\nimport { Component, inject, signal } from '@angular\u002Fcore';\nimport { FormBuilder, FormGroup, Validators } from '@angular\u002Fforms';\nimport { AuthService } from '@core\u002Fservices\u002Fauth.service';\nimport { SHARED_IMPORTS } from '@shared';\n\n@Component({\n  selector: 'app-login',\n  standalone: true,\n  imports: [SHARED_IMPORTS],\n  template: `\n    \u003Cdiv class=\"login-container\">\n      \u003Cnz-card>\n        \u003Ch2>Login\u003C\u002Fh2>\n        \u003Cform nz-form [formGroup]=\"loginForm\" (ngSubmit)=\"handleLogin()\">\n          \u003Cnz-form-item>\n            \u003Cnz-form-control nzErrorTip=\"Please input your email!\">\n              \u003Cnz-input-group nzPrefixIcon=\"mail\">\n                \u003Cinput \n                  nz-input \n                  formControlName=\"email\" \n                  placeholder=\"Email\"\n                  type=\"email\"\n                \u002F>\n              \u003C\u002Fnz-input-group>\n            \u003C\u002Fnz-form-control>\n          \u003C\u002Fnz-form-item>\n          \n          \u003Cnz-form-item>\n            \u003Cnz-form-control nzErrorTip=\"Please input your password!\">\n              \u003Cnz-input-group nzPrefixIcon=\"lock\">\n                \u003Cinput \n                  nz-input \n                  formControlName=\"password\" \n                  placeholder=\"Password\"\n                  type=\"password\"\n                \u002F>\n              \u003C\u002Fnz-input-group>\n            \u003C\u002Fnz-form-control>\n          \u003C\u002Fnz-form-item>\n          \n          @if (error()) {\n            \u003Cnz-alert \n              nzType=\"error\" \n              [nzMessage]=\"error()!\" \n              nzShowIcon \n            \u002F>\n          }\n          \n          \u003Cbutton \n            nz-button \n            nzType=\"primary\" \n            nzBlock \n            [nzLoading]=\"loading()\"\n            [disabled]=\"loginForm.invalid\"\n          >\n            Login\n          \u003C\u002Fbutton>\n        \u003C\u002Fform>\n      \u003C\u002Fnz-card>\n    \u003C\u002Fdiv>\n  `\n})\nexport class LoginComponent {\n  private fb = inject(FormBuilder);\n  private authService = inject(AuthService);\n  \n  loading = signal(false);\n  error = signal\u003Cstring | null>(null);\n  \n  loginForm: FormGroup = this.fb.group({\n    email: ['', [Validators.required, Validators.email]],\n    password: ['', [Validators.required, Validators.minLength(6)]]\n  });\n  \n  async handleLogin(): Promise\u003Cvoid> {\n    if (this.loginForm.invalid) return;\n    \n    this.loading.set(true);\n    this.error.set(null);\n    \n    try {\n      const { email, password } = this.loginForm.value;\n      await this.authService.login(email, password);\n    } catch (err) {\n      this.error.set(err instanceof Error ? err.message : 'Login failed');\n    } finally {\n      this.loading.set(false);\n    }\n  }\n}\n```\n\n## HTTP Interceptor (Auto Token Injection)\n\n@delon\u002Fauth automatically provides an interceptor when configured. No manual implementation needed!\n\n```typescript\n\u002F\u002F Automatic token injection in all HTTP requests\n\u002F\u002F Configured in app.config.ts with provideDelonAuth()\n\n\u002F\u002F Example HTTP call - token automatically added\nthis.http.get('\u002Fapi\u002Ftasks').subscribe(tasks => {\n  \u002F\u002F Authorization: Bearer {token} header automatically added\n});\n```\n\n## Component-Level Permission Check\n\n```typescript\nimport { Component, inject, computed } from '@angular\u002Fcore';\nimport { AuthService } from '@core\u002Fservices\u002Fauth.service';\n\n@Component({\n  selector: 'app-task-list',\n  template: `\n    @if (canCreate()) {\n      \u003Cbutton nz-button nzType=\"primary\" (click)=\"createTask()\">\n        Create Task\n      \u003C\u002Fbutton>\n    }\n    \n    @if (canEdit()) {\n      \u003Cbutton nz-button (click)=\"editTask()\">Edit\u003C\u002Fbutton>\n    }\n    \n    @if (canDelete()) {\n      \u003Cbutton nz-button nzDanger (click)=\"deleteTask()\">Delete\u003C\u002Fbutton>\n    }\n  `\n})\nexport class TaskListComponent {\n  private authService = inject(AuthService);\n  \n  canCreate = computed(() => this.authService.hasPermission('create:tasks'));\n  canEdit = computed(() => this.authService.hasPermission('edit:tasks'));\n  canDelete = computed(() => this.authService.hasPermission('delete:tasks'));\n}\n```\n\n## Integration with @delon\u002Facl\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fapp.config.ts\nimport { provideDelonACL } from '@delon\u002Facl';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideDelonACL()\n  ]\n};\n```\n\n```typescript\n\u002F\u002F Sync permissions to ACL\nimport { ACLService } from '@delon\u002Facl';\n\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n  private aclService = inject(ACLService);\n  \n  private syncPermissionsToACL(permissions: string[], role: string): void {\n    this.aclService.setRole([role]);\n    this.aclService.setAbility(permissions);\n  }\n}\n```\n\n## Routes Configuration\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Froutes\u002Froutes.ts\nimport { Routes } from '@angular\u002Frouter';\nimport { authGuard } from '@core\u002Fguards\u002Fauth.guard';\nimport { roleGuard } from '@core\u002Fguards\u002Frole.guard';\nimport { permissionGuard } from '@core\u002Fguards\u002Fpermission.guard';\n\nexport const routes: Routes = [\n  {\n    path: '',\n    canActivate: [authGuard],\n    children: [\n      {\n        path: 'dashboard',\n        loadComponent: () => import('.\u002Fdashboard\u002Fdashboard.component')\n      },\n      {\n        path: 'admin',\n        canActivate: [roleGuard],\n        data: { role: 'admin' },\n        loadComponent: () => import('.\u002Fadmin\u002Fadmin.component')\n      },\n      {\n        path: 'tasks',\n        canActivate: [permissionGuard],\n        data: { \n          permissions: ['read:tasks'],\n          requireAll: true\n        },\n        loadComponent: () => import('.\u002Ftasks\u002Ftask-list.component')\n      }\n    ]\n  },\n  {\n    path: 'passport',\n    children: [\n      {\n        path: 'login',\n        loadComponent: () => import('.\u002Fpassport\u002Flogin\u002Flogin.component')\n      }\n    ]\n  }\n];\n```\n\n## Checklist\n\nWhen implementing authentication:\n\n- [ ] Configure @delon\u002Fauth in app.config.ts\n- [ ] Create AuthService with token management\n- [ ] Implement login\u002Flogout methods\n- [ ] Set up token refresh mechanism\n- [ ] Create auth guard for protected routes\n- [ ] Create role\u002Fpermission guards as needed\n- [ ] Integrate with Firebase Auth\n- [ ] Sync permissions with @delon\u002Facl\n- [ ] Handle token expiration\n- [ ] Test authentication flow\n- [ ] Test authorization checks\n- [ ] Handle error states (invalid credentials, network errors)\n\n## References\n\n- [@delon\u002Fauth Documentation](https:\u002F\u002Fng-alain.com\u002Fauth)\n- [@delon\u002Facl Documentation](https:\u002F\u002Fng-alain.com\u002Facl)\n- [Firebase Auth](https:\u002F\u002Ffirebase.google.com\u002Fdocs\u002Fauth)\n- [JWT Best Practices](https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc8725)\n","# @delon\u002Fauth Authentication & Authorization Skill\n\nThis skill helps implement authentication and authorization using @delon\u002Fauth library.\n\n## Core Principles\n\n### Token Management\n- **JWT Storage**: Secure token storage using DA_SERVICE_TOKEN\n- **Auto Refresh**: Automatic token renewal before expiration\n- **Interceptors**: Automatic token injection in HTTP requests\n- **Logout**: Clean token removal and session cleanup\n\n### Authorization\n- **RBAC**: Role-based access control\n- **Permissions**: Fine-grained permission checking\n- **Guards**: Route protection with role\u002Fpermission checks\n- **ACL Integration**: Works with @delon\u002Facl for UI-level controls\n\n## Configuration\n\n### App Config\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fapp.config.ts\nimport { ApplicationConfig } from '@angular\u002Fcore';\nimport { provideDelonAuth, DA_SERVICE_TOKEN, ITokenService } from '@delon\u002Fauth';\nimport { JWTTokenModel } from '@delon\u002Fauth';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideDelonAuth({\n      token_send_key: 'Authorization',\n      token_send_template: 'Bearer ${token}',\n      token_send_place: 'header',\n      login_url: '\u002Fpassport\u002Flogin',\n      ignores: [\u002F\\\u002Flogin\u002F, \u002Fassets\\\u002F\u002F],\n      allow_anonymous_key: 'allow_anonymous',\n      executeOtherInterceptors: true,\n      refreshTime: 3000, \u002F\u002F Refresh token check interval (ms)\n      refreshOffset: 6000 \u002F\u002F Refresh token before expiration (ms)\n    })\n  ]\n};\n```\n\n### Token Model\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fcore\u002Fmodels\u002Fauth-token.model.ts\nimport { JWTTokenModel } from '@delon\u002Fauth';\n\nexport interface AuthTokenModel extends JWTTokenModel {\n  token: string;\n  uid: string;\n  email: string;\n  displayName: string;\n  role: string;\n  permissions: string[];\n  exp: number;\n}\n```\n\n## Authentication Service\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fcore\u002Fservices\u002Fauth.service.ts\nimport { Injectable, inject } from '@angular\u002Fcore';\nimport { Router } from '@angular\u002Frouter';\nimport { DA_SERVICE_TOKEN, ITokenService } from '@delon\u002Fauth';\nimport { Auth, signInWithEmailAndPassword, signOut, User } from '@angular\u002Ffire\u002Fauth';\nimport { AuthTokenModel } from '@core\u002Fmodels\u002Fauth-token.model';\nimport { signal, computed } from '@angular\u002Fcore';\n\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n  private auth = inject(Auth);\n  private tokenService = inject\u003CITokenService>(DA_SERVICE_TOKEN);\n  private router = inject(Router);\n  \n  \u002F\u002F State\n  private currentUser = signal\u003CUser | null>(null);\n  private authToken = signal\u003CAuthTokenModel | null>(null);\n  \n  \u002F\u002F Computed\n  isAuthenticated = computed(() => this.tokenService.get()?.token != null);\n  userRole = computed(() => this.authToken()?.role);\n  userPermissions = computed(() => this.authToken()?.permissions || []);\n  \n  constructor() {\n    \u002F\u002F Listen to Firebase Auth state\n    this.auth.onAuthStateChanged(async (user) => {\n      this.currentUser.set(user);\n      if (user) {\n        await this.refreshToken();\n      } else {\n        this.clearSession();\n      }\n    });\n  }\n  \n  \u002F**\n   * Login with email and password\n   *\u002F\n  async login(email: string, password: string): Promise\u003Cvoid> {\n    try {\n      const credential = await signInWithEmailAndPassword(this.auth, email, password);\n      const token = await credential.user.getIdToken();\n      const tokenData = await this.parseToken(token);\n      \n      \u002F\u002F Store token in @delon\u002Fauth\n      this.tokenService.set(tokenData);\n      this.authToken.set(tokenData);\n      \n      \u002F\u002F Navigate to dashboard\n      await this.router.navigateByUrl('\u002Fdashboard');\n    } catch (error) {\n      console.error('Login failed:', error);\n      throw error;\n    }\n  }\n  \n  \u002F**\n   * Logout\n   *\u002F\n  async logout(): Promise\u003Cvoid> {\n    try {\n      await signOut(this.auth);\n      this.clearSession();\n      await this.router.navigateByUrl('\u002Fpassport\u002Flogin');\n    } catch (error) {\n      console.error('Logout failed:', error);\n      throw error;\n    }\n  }\n  \n  \u002F**\n   * Refresh token\n   *\u002F\n  async refreshToken(): Promise\u003Cvoid> {\n    const user = this.currentUser();\n    if (!user) return;\n    \n    try {\n      const token = await user.getIdToken(true); \u002F\u002F Force refresh\n      const tokenData = await this.parseToken(token);\n      \n      this.tokenService.set(tokenData);\n      this.authToken.set(tokenData);\n    } catch (error) {\n      console.error('Token refresh failed:', error);\n      await this.logout();\n    }\n  }\n  \n  \u002F**\n   * Check if user has specific role\n   *\u002F\n  hasRole(role: string): boolean {\n    return this.userRole() === role;\n  }\n  \n  \u002F**\n   * Check if user has specific permission\n   *\u002F\n  hasPermission(permission: string): boolean {\n    return this.userPermissions().includes(permission);\n  }\n  \n  \u002F**\n   * Check if user has any of the specified permissions\n   *\u002F\n  hasAnyPermission(permissions: string[]): boolean {\n    const userPerms = this.userPermissions();\n    return permissions.some(p => userPerms.includes(p));\n  }\n  \n  \u002F**\n   * Check if user has all of the specified permissions\n   *\u002F\n  hasAllPermissions(permissions: string[]): boolean {\n    const userPerms = this.userPermissions();\n    return permissions.every(p => userPerms.includes(p));\n  }\n  \n  \u002F**\n   * Parse JWT token\n   *\u002F\n  private async parseToken(token: string): Promise\u003CAuthTokenModel> {\n    \u002F\u002F Decode JWT payload\n    const payload = JSON.parse(atob(token.split('.')[1]));\n    \n    \u002F\u002F Load user permissions from Firestore (custom claims)\n    const permissions = await this.loadUserPermissions(payload.uid);\n    \n    return {\n      token,\n      uid: payload.uid,\n      email: payload.email,\n      displayName: payload.name || payload.email,\n      role: payload.role || 'member',\n      permissions,\n      exp: payload.exp\n    };\n  }\n  \n  \u002F**\n   * Load user permissions from Firestore\n   *\u002F\n  private async loadUserPermissions(uid: string): Promise\u003Cstring[]> {\n    \u002F\u002F Implementation: query user's permissions from Firestore\n    \u002F\u002F This is placeholder - implement based on your schema\n    return ['read:tasks', 'write:tasks'];\n  }\n  \n  \u002F**\n   * Clear session\n   *\u002F\n  private clearSession(): void {\n    this.tokenService.clear();\n    this.authToken.set(null);\n    this.currentUser.set(null);\n  }\n}\n```\n\n## Auth Guard\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fcore\u002Fguards\u002Fauth.guard.ts\nimport { inject } from '@angular\u002Fcore';\nimport { Router, CanActivateFn } from '@angular\u002Frouter';\nimport { AuthService } from '@core\u002Fservices\u002Fauth.service';\n\n\u002F**\n * Guard to protect routes requiring authentication\n *\u002F\nexport const authGuard: CanActivateFn = () => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n  \n  if (authService.isAuthenticated()) {\n    return true;\n  }\n  \n  return router.createUrlTree(['\u002Fpassport\u002Flogin']);\n};\n```\n\n## Role Guard\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fcore\u002Fguards\u002Frole.guard.ts\nimport { inject } from '@angular\u002Fcore';\nimport { Router, CanActivateFn, ActivatedRouteSnapshot } from '@angular\u002Frouter';\nimport { AuthService } from '@core\u002Fservices\u002Fauth.service';\n\n\u002F**\n * Guard to protect routes by role\n *\u002F\nexport const roleGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n  \n  const requiredRole = route.data['role'] as string;\n  \n  if (!requiredRole) {\n    console.warn('Role guard used without required role');\n    return true;\n  }\n  \n  if (authService.hasRole(requiredRole)) {\n    return true;\n  }\n  \n  \u002F\u002F Redirect to unauthorized page\n  return router.createUrlTree(['\u002Fexception\u002F403']);\n};\n\n\u002F**\n * Usage in routes:\n * {\n *   path: 'admin',\n *   component: AdminComponent,\n *   canActivate: [authGuard, roleGuard],\n *   data: { role: 'admin' }\n * }\n *\u002F\n```\n\n## Permission Guard\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fcore\u002Fguards\u002Fpermission.guard.ts\nimport { inject } from '@angular\u002Fcore';\nimport { Router, CanActivateFn, ActivatedRouteSnapshot } from '@angular\u002Frouter';\nimport { AuthService } from '@core\u002Fservices\u002Fauth.service';\n\n\u002F**\n * Guard to protect routes by permissions\n *\u002F\nexport const permissionGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => {\n  const authService = inject(AuthService);\n  const router = inject(Router);\n  \n  const requiredPermissions = route.data['permissions'] as string[];\n  const requireAll = route.data['requireAll'] as boolean ?? true;\n  \n  if (!requiredPermissions || requiredPermissions.length === 0) {\n    console.warn('Permission guard used without required permissions');\n    return true;\n  }\n  \n  const hasAccess = requireAll\n    ? authService.hasAllPermissions(requiredPermissions)\n    : authService.hasAnyPermission(requiredPermissions);\n  \n  if (hasAccess) {\n    return true;\n  }\n  \n  return router.createUrlTree(['\u002Fexception\u002F403']);\n};\n\n\u002F**\n * Usage in routes:\n * {\n *   path: 'tasks',\n *   component: TaskListComponent,\n *   canActivate: [authGuard, permissionGuard],\n *   data: { \n *     permissions: ['read:tasks'], \n *     requireAll: true \n *   }\n * }\n *\u002F\n```\n\n## Login Component\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Froutes\u002Fpassport\u002Flogin\u002Flogin.component.ts\nimport { Component, inject, signal } from '@angular\u002Fcore';\nimport { FormBuilder, FormGroup, Validators } from '@angular\u002Fforms';\nimport { AuthService } from '@core\u002Fservices\u002Fauth.service';\nimport { SHARED_IMPORTS } from '@shared';\n\n@Component({\n  selector: 'app-login',\n  standalone: true,\n  imports: [SHARED_IMPORTS],\n  template: `\n    \u003Cdiv class=\"login-container\">\n      \u003Cnz-card>\n        \u003Ch2>Login\u003C\u002Fh2>\n        \u003Cform nz-form [formGroup]=\"loginForm\" (ngSubmit)=\"handleLogin()\">\n          \u003Cnz-form-item>\n            \u003Cnz-form-control nzErrorTip=\"Please input your email!\">\n              \u003Cnz-input-group nzPrefixIcon=\"mail\">\n                \u003Cinput \n                  nz-input \n                  formControlName=\"email\" \n                  placeholder=\"Email\"\n                  type=\"email\"\n                \u002F>\n              \u003C\u002Fnz-input-group>\n            \u003C\u002Fnz-form-control>\n          \u003C\u002Fnz-form-item>\n          \n          \u003Cnz-form-item>\n            \u003Cnz-form-control nzErrorTip=\"Please input your password!\">\n              \u003Cnz-input-group nzPrefixIcon=\"lock\">\n                \u003Cinput \n                  nz-input \n                  formControlName=\"password\" \n                  placeholder=\"Password\"\n                  type=\"password\"\n                \u002F>\n              \u003C\u002Fnz-input-group>\n            \u003C\u002Fnz-form-control>\n          \u003C\u002Fnz-form-item>\n          \n          @if (error()) {\n            \u003Cnz-alert \n              nzType=\"error\" \n              [nzMessage]=\"error()!\" \n              nzShowIcon \n            \u002F>\n          }\n          \n          \u003Cbutton \n            nz-button \n            nzType=\"primary\" \n            nzBlock \n            [nzLoading]=\"loading()\"\n            [disabled]=\"loginForm.invalid\"\n          >\n            Login\n          \u003C\u002Fbutton>\n        \u003C\u002Fform>\n      \u003C\u002Fnz-card>\n    \u003C\u002Fdiv>\n  `\n})\nexport class LoginComponent {\n  private fb = inject(FormBuilder);\n  private authService = inject(AuthService);\n  \n  loading = signal(false);\n  error = signal\u003Cstring | null>(null);\n  \n  loginForm: FormGroup = this.fb.group({\n    email: ['', [Validators.required, Validators.email]],\n    password: ['', [Validators.required, Validators.minLength(6)]]\n  });\n  \n  async handleLogin(): Promise\u003Cvoid> {\n    if (this.loginForm.invalid) return;\n    \n    this.loading.set(true);\n    this.error.set(null);\n    \n    try {\n      const { email, password } = this.loginForm.value;\n      await this.authService.login(email, password);\n    } catch (err) {\n      this.error.set(err instanceof Error ? err.message : 'Login failed');\n    } finally {\n      this.loading.set(false);\n    }\n  }\n}\n```\n\n## HTTP Interceptor (Auto Token Injection)\n\n@delon\u002Fauth automatically provides an interceptor when configured. No manual implementation needed!\n\n```typescript\n\u002F\u002F Automatic token injection in all HTTP requests\n\u002F\u002F Configured in app.config.ts with provideDelonAuth()\n\n\u002F\u002F Example HTTP call - token automatically added\nthis.http.get('\u002Fapi\u002Ftasks').subscribe(tasks => {\n  \u002F\u002F Authorization: Bearer {token} header automatically added\n});\n```\n\n## Component-Level Permission Check\n\n```typescript\nimport { Component, inject, computed } from '@angular\u002Fcore';\nimport { AuthService } from '@core\u002Fservices\u002Fauth.service';\n\n@Component({\n  selector: 'app-task-list',\n  template: `\n    @if (canCreate()) {\n      \u003Cbutton nz-button nzType=\"primary\" (click)=\"createTask()\">\n        Create Task\n      \u003C\u002Fbutton>\n    }\n    \n    @if (canEdit()) {\n      \u003Cbutton nz-button (click)=\"editTask()\">Edit\u003C\u002Fbutton>\n    }\n    \n    @if (canDelete()) {\n      \u003Cbutton nz-button nzDanger (click)=\"deleteTask()\">Delete\u003C\u002Fbutton>\n    }\n  `\n})\nexport class TaskListComponent {\n  private authService = inject(AuthService);\n  \n  canCreate = computed(() => this.authService.hasPermission('create:tasks'));\n  canEdit = computed(() => this.authService.hasPermission('edit:tasks'));\n  canDelete = computed(() => this.authService.hasPermission('delete:tasks'));\n}\n```\n\n## Integration with @delon\u002Facl\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Fapp.config.ts\nimport { provideDelonACL } from '@delon\u002Facl';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideDelonACL()\n  ]\n};\n```\n\n```typescript\n\u002F\u002F Sync permissions to ACL\nimport { ACLService } from '@delon\u002Facl';\n\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n  private aclService = inject(ACLService);\n  \n  private syncPermissionsToACL(permissions: string[], role: string): void {\n    this.aclService.setRole([role]);\n    this.aclService.setAbility(permissions);\n  }\n}\n```\n\n## Routes Configuration\n\n```typescript\n\u002F\u002F src\u002Fapp\u002Froutes\u002Froutes.ts\nimport { Routes } from '@angular\u002Frouter';\nimport { authGuard } from '@core\u002Fguards\u002Fauth.guard';\nimport { roleGuard } from '@core\u002Fguards\u002Frole.guard';\nimport { permissionGuard } from '@core\u002Fguards\u002Fpermission.guard';\n\nexport const routes: Routes = [\n  {\n    path: '',\n    canActivate: [authGuard],\n    children: [\n      {\n        path: 'dashboard',\n        loadComponent: () => import('.\u002Fdashboard\u002Fdashboard.component')\n      },\n      {\n        path: 'admin',\n        canActivate: [roleGuard],\n        data: { role: 'admin' },\n        loadComponent: () => import('.\u002Fadmin\u002Fadmin.component')\n      },\n      {\n        path: 'tasks',\n        canActivate: [permissionGuard],\n        data: { \n          permissions: ['read:tasks'],\n          requireAll: true\n        },\n        loadComponent: () => import('.\u002Ftasks\u002Ftask-list.component')\n      }\n    ]\n  },\n  {\n    path: 'passport',\n    children: [\n      {\n        path: 'login',\n        loadComponent: () => import('.\u002Fpassport\u002Flogin\u002Flogin.component')\n      }\n    ]\n  }\n];\n```\n\n## Checklist\n\nWhen implementing authentication:\n\n- [ ] Configure @delon\u002Fauth in app.config.ts\n- [ ] Create AuthService with token management\n- [ ] Implement login\u002Flogout methods\n- [ ] Set up token refresh mechanism\n- [ ] Create auth guard for protected routes\n- [ ] Create role\u002Fpermission guards as needed\n- [ ] Integrate with Firebase Auth\n- [ ] Sync permissions with @delon\u002Facl\n- [ ] Handle token expiration\n- [ ] Test authentication flow\n- [ ] Test authorization checks\n- [ ] Handle error states (invalid credentials, network errors)\n\n## References\n\n- [@delon\u002Fauth Documentation](https:\u002F\u002Fng-alain.com\u002Fauth)\n- [@delon\u002Facl Documentation](https:\u002F\u002Fng-alain.com\u002Facl)\n- [Firebase Auth](https:\u002F\u002Ffirebase.google.com\u002Fdocs\u002Fauth)\n- [JWT Best Practices](https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc8725)\n",{"name":5,"license":14,"description":7},"master","08cc2532077058aa50e6e18f54695bc256c88152","skills\u002F7spade\u002Fdelon-auth-authentication-authorization","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub","https:\u002F\u002Fgithub.com\u002Fsuperhealth","java engineer","2026-04-06 22:07:23",{"source":8,"skillName":5,"commands":51,"prompt":61,"zipAvailable":62},[52,55,58],{"tool":53,"command":54},"npx","npx skills add superhealth\u002Fskills-hub --skill delon-auth-authentication-authorization",{"tool":56,"command":57},"bunx","bunx skills add superhealth\u002Fskills-hub --skill delon-auth-authentication-authorization",{"tool":59,"command":60},"pnpm","pnpm dlx skills add superhealth\u002Fskills-hub --skill delon-auth-authentication-authorization","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002F7spade\u002Fdelon-auth-authentication-authorization\u002FSKILL.md",true,[64],{"path":65,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":68,"sourceHtmlUrl":69},"skill-report.json","REFERENCE","参考文档",9094,"https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002F7spade\u002Fdelon-auth-authentication-authorization\u002Fskill-report.json",[71,102,130,142,158,182],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":82,"publishedAt":38},"01M6MC9F3WHV8S8FZ2W4TRB0FE","vercel-react-best-practices","React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React\u002FNext.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fzhanlincui\u002Fvercel-react-best-practices\u002FSKILL.md",[77],{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,37,100,101],"react","vercel react best practices","best practices","vercel","react best practices","data fetching","code","triggers","pages","next","patterns","refactoring","data","Optimization","tasks","guidelines","react components","performance","writing",{"skillNo":103,"name":104,"nameZh":6,"description":105,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":112,"publishedAt":38},"01M6MB8KWE1Q5A5KERR7RFTQWP","vue-testing-best-practices","Use for Vue.js testing. Covers Vitest, Vue Test Utils, component testing, mocking, testing patterns, and Playwright for E2E testing.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-testing-best-practices\u002FSKILL.md",[108,111],{"categoryNo":109,"categoryName":110,"taxonomyType":80,"taxonomyTypeDesc":81},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},[113,114,115,85,116,117,118,119,120,121,122,93,123,124,125,126,127,128,129],"vue testing best practices","testing best practices","vue","testing","testing patterns","js","e2e","vitest","use","component","test","mocking","for","utils","e2e testing","github","playwright",{"skillNo":131,"name":132,"nameZh":6,"description":133,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":134,"categories":135,"keywords":137,"publishedAt":38},"01M6MB8GK06M6GA4KVYNDV1BZP","vue-router-best-practices","Vue Router 4 patterns, navigation guards, route params, and route-component lifecycle interactions.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-router-best-practices\u002FSKILL.md",[136],{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},[85,115,138,139,32,93,140,141,122,128],"router","vue router best practices","navigation","lifecycle",{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":146,"categories":147,"keywords":149,"publishedAt":38},"01M6MB8DQF21TZMQ9XC3745ERW","vue-pinia-best-practices","Pinia stores, state management patterns, store setup, and reactivity with stores.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-pinia-best-practices\u002FSKILL.md",[148],{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},[150,151,115,85,152,153,93,154,155,156,157,128],"vue pinia best practices","pinia","with","store","management","state","state management","setup",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":173,"publishedAt":38},"01M6MB8A7MC08747J9TV4GWQ9A","vue-options-api-best-practices","Vue 3 Options API style (data(), methods, this context). Each reference shows Options API solution only.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-options-api-best-practices\u002FSKILL.md",[164,165,168],{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},{"categoryNo":166,"categoryName":167,"taxonomyType":80,"taxonomyTypeDesc":81},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":169,"categoryName":170,"taxonomyType":171,"taxonomyTypeDesc":172},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[115,174,175,176,85,177,178,179,180,95,128,181],"options","vue options api best practices","api","methods","reference","only","context","style",{"skillNo":183,"name":184,"nameZh":6,"description":185,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":186,"categories":187,"keywords":189,"publishedAt":38},"01M6MB85ZTQC24Q04X49MM4YXN","vue-development-guides","A collection of best practices and tips for developing applications using Vue.js. This skill MUST be apply when developing, refactoring or reviewing Vue.js or Nuxt projects.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-development-guides\u002FSKILL.md",[188],{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},[190,115,191,192,128,193,194,125,195,196,197,118,94,198,199,85],"development","guides","vue development guides","tips","apply","project","nuxt","projects","collection","applications"]