# @classic-homes/auth 中文文档

规范地址：https://npmcn.com/package/%40classic-homes/auth/

版本：1\.47\.0
npm 元数据更新时间：2026-11-09T19:28:57.741Z
中文译文更新时间：2026-11-09T23:54:40.598Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40classic-homes/auth
原文对照：https://npmcn.com/package/%40classic-homes/auth/?lang=original
项目主页：https://github.com/Classic-Homes/classic-theme#readme
源码仓库：https://github.com/Classic-Homes/classic-theme

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：295 kB，18 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：0 个
- peer 依赖：svelte ^5\.0\.0
- 首次发布：2026-01-08T18:28:44.785Z
- 最新版本发布：2026-11-09T19:28:57.581Z
- 近 30 天下载量：1736（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：76（近 12 个月 76 个）

## 中文简介

经典主题应用程序的认证服务和 Svelte 绑定

## 安装

```sh
npm install @classic-homes/auth
```

也可以使用：`pnpm add @classic-homes/auth`、`yarn add @classic-homes/auth`、`bun add @classic-homes/auth`

## README

# @classic-homes/ auth (英语)

为经典主题设计系统配有斯维尔特捆绑的框架-不可知认证核心.

## 特征

-   基于 JWT 并自动刷新令牌的认证
-   可配置提供者的SSO(单一签名)支持和登录
-   多要素认证(MFA/TOTP)支持类型守护
-   成功登录时自动设置认证状态
-   插件存储适配器( localStorage, sessionStorage, 或自定义)
-   认证状态的 Svelte 反应存储
-   保护页面的路由守卫
-   TypeScript 第一种具有完全类型安全性

## 安装

```bash
npm install @classic-homes/auth
```

## 快速启动

### 1\. 初始化认证

在您的应用程序的切入点( 例如 ), `hooks.client.ts` 用于斯维尔特基特:

```typescript
import { initAuth } from '@classic-homes/auth';
import { goto } from '$app/navigation';
import { base } from '$app/paths';

initAuth({
  baseUrl: 'https://api.example.com',
  storage: {
    getItem: (key) => localStorage.getItem(key),
    setItem: (key, value) => localStorage.setItem(key, value),
    removeItem: (key) => localStorage.removeItem(key),
  },
  // ⚠️ SECURITY: localStorage (also the default when `storage` is omitted)
  // persists the long-lived refresh token where any XSS can read it.
  // Safer options exported from '@classic-homes/auth/core':
  //   storage: createSessionStorage()  // per-tab, cleared on close
  //   storage: createMemoryStorage()   // never persisted, lost on reload
  // or keep tokens out of JS entirely with an httpOnly-cookie flow.
  // SSO configuration (optional)
  sso: {
    enabled: true,
    provider: 'authentik',
  },
  // Callback when auth errors occur
  onAuthError: (error) => {
    console.error('Auth error:', error);
  },
  // Callback when tokens are refreshed
  onTokenRefresh: (tokens) => {
    console.log('Tokens refreshed');
  },
  // Callback when user is logged out
  onLogout: () => {
    goto(`${base}/auth/login`);
  },
});
```

### 2\. 登录自动设定 Auth

这个 `authService.login()` 方法在成功登录时自动设置认证状态 :

```typescript
import {
  authService,
  isMfaChallengeResponse,
  getMfaToken,
  getAvailableMethods,
} from '@classic-homes/auth/core';
import { goto } from '$app/navigation';

async function handleLogin(email: string, password: string) {
  const response = await authService.login({
    username: email,
    password: password,
  });

  // Check if MFA is required
  if (isMfaChallengeResponse(response)) {
    const mfaToken = getMfaToken(response);
    const methods = getAvailableMethods(response);
    // Redirect to MFA challenge page
    goto(`/auth/mfa-challenge?token=${mfaToken}&methods=${methods.join(',')}`);
    return;
  }

  // Auth state is automatically set - redirect to dashboard
  goto('/dashboard');
}
```

Email-MFA账户通过电子邮件获得代码: 发送它与挑战符, 然后与 `method: 'email'`冷却(一分钟一个代码,五分钟一个) 小时)抛出 `MFAChallengeEmailError` 与 `retryAfterSeconds`。 。 。 .

```typescript
import { authService, MFAChallengeEmailError } from '@classic-homes/auth';

try {
  const { email } = await authService.sendMFAChallengeEmail(mfaToken); // "jo***@example.com"
} catch (error) {
  if (error instanceof MFAChallengeEmailError && error.retryAfterSeconds) {
    // Show "Resend in Ns"
  }
}

await authService.verifyMFAChallenge({ mfaToken, code, method: 'email' });
```

要禁用自动设置认证( 用于手动控制) :

```typescript
const response = await authService.login(credentials, { autoSetAuth: false });
// Manually set auth state
authActions.setAuth(response.accessToken, response.refreshToken, response.user);
```

### 3\. 使用 Auth 存储器( Svelte)

```svelte
<script lang="ts">
  import { authStore, authActions, isAuthenticated, currentUser } from '@classic-homes/auth/svelte';

  // Using derived stores
  // $isAuthenticated - boolean
  // $currentUser - User | null

  async function handleLogout() {
    // SSO-aware logout
    const result = await authActions.logoutWithSSO();
    if (result.ssoLogoutUrl) {
      // Redirect to SSO provider logout
      window.location.href = result.ssoLogoutUrl;
    } else {
      goto('/auth/login');
    }
  }
</script>

{#if $isAuthenticated}
  <p>Welcome, {$currentUser?.firstName}</p>
  <button onclick={handleLogout}>Logout</button>
{:else}
  <a href="/auth/login">Login</a>
{/if}
```

### 4\. 包含重定向 URL 的 SSO 登录

```typescript
import { authService } from '@classic-homes/auth/core';

function handleSSOLogin(redirectUrl: string) {
  // Specify where to redirect after SSO callback
  authService.initiateSSOLogin({
    callbackUrl: `${window.location.origin}/auth/sso-callback`,
    redirectUrl: redirectUrl, // Final destination after auth
  });
}
```

`initiateSSOLogin` 也可以随机发送 `client_state`,保留在这个标签的 sessionStorage(单用,10分钟TTL). API在回电中回响 散列( 散列)`client_state`\- 检查 这个 **在此之前** 存储散列中的任何符号,所以一个精心设计的回调 URL 无法 将用户签入他人账户( 登录 CSRF ):

```typescript
import { verifySsoClientState } from '@classic-homes/auth/core';

const params = new URLSearchParams(window.location.hash.slice(1));
const { accepted, result } = verifySsoClientState(params); // 'ok' | 'mismatch' | 'missing'
if (!accepted) {
  window.history.replaceState({}, '', window.location.pathname);
  throw new Error('This sign-in link was not started from this browser tab. Please sign in again.');
}
```

设定 `sso.requireClientState: true` 一旦API回响 `client_state` 实值 部署在那个环境中 在此之前(默认),每个结果都是 已接受,所以日志 `result` 以观看推出.

### 5\. 外交部挑战核查

```typescript
import { authService } from '@classic-homes/auth/core';

async function handleMFAVerify(mfaToken: string, code: string, trustDevice: boolean) {
  // Auto-sets auth state on success
  const response = await authService.verifyMFAChallenge({
    mfaToken,
    code,
    method: 'totp',
    trustDevice,
  });

  // Auth state is automatically set - redirect to dashboard
  goto('/dashboard');
}
```

### 6\. 保护路线

```typescript
// src/routes/dashboard/+page.ts
import { checkAuth, requireRole } from '@classic-homes/auth/svelte';
import { redirect } from '@sveltejs/kit';
import { browser } from '$app/environment';

export function load({ url }) {
  if (browser) {
    const result = checkAuth();
    if (!result.allowed) {
      throw redirect(302, `/auth/login?redirect=${encodeURIComponent(url.pathname)}`);
    }
  }
  return {};
}

// For role-based access:
export function load({ url }) {
  if (browser) {
    const result = checkAuth({ roles: ['admin', 'manager'] });
    if (!result.allowed) {
      if (result.reason === 'not_authenticated') {
        throw redirect(302, `/auth/login?redirect=${encodeURIComponent(url.pathname)}`);
      }
      if (result.reason === 'missing_role') {
        throw redirect(302, '/unauthorized');
      }
    }
  }
  return {};
}
```

## 作用

用户持有 **设定** 作用问题`user.roles`) (中文(简体) ). 他们的许可是工会 因此用户的访问比任何单一的角色都要广泛.

```typescript
import { authStore } from '@classic-homes/auth/svelte';

// A manager who is also an agent
authStore.user?.roles; // ['manager', 'agent']

authStore.hasRole('manager'); // true
authStore.hasRole('agent'); // true — secondary roles count
authStore.hasAnyRole(['admin', 'agent']); // true
authStore.hasAllRoles(['manager', 'agent']); // true
```

**角色检查是准确的成员.** 持有 `admin` 并不意味着 `agent`数字 :

```typescript
authStore.user?.roles; // ['admin']
authStore.hasRole('agent'); // false
```

这个镜像了API,所以UI不能提供服务器拒绝的东西. 也让管理员通过,列出你接受的角色: `checkAuth({ roles: ['admin', 'agent'] })`。 。 。 .

### 显示一个角色

`computePrimaryRole` 解决用于显示的最高级别角色。 这是一个标签, 不是一种能力,绝不是任何一种能力:

```typescript
import { computePrimaryRole, getEffectiveLevel } from '@classic-homes/auth/core';

computePrimaryRole(['user', 'admin']); // 'admin'
computePrimaryRole([]); // 'guest'
getEffectiveLevel(['user', 'manager']); // 3

// Or format the whole set
import { formatUserRoles } from '@classic-homes/auth/core';
formatUserRoles(user); // 'Manager, Agent'
formatUserRoles(user, { max: 1 }); // 'Manager +1 more'
```

### 管理另一个用户的角色

要求数 `users:read` 列表和 `users:update` 改为:

```typescript
import { authApi } from '@classic-homes/auth/core';

const { roles, primaryRole } = await authApi.getUserRoles(userId);

await authApi.grantUserRole(userId, 'agent'); // idempotent
await authApi.revokeUserRole(userId, 'agent');
await authApi.setUserRoles(userId, ['manager', 'agent']); // replaces the set
```

围绕设计的两个服务器规则:

-   **角色变化不影响该用户的活会话.** 权限是 在铸造一个符号时计算,所以补助金在他们的下个符号上生效 刷新——不是他们的下个请求.
-   服务器拒绝删除用户的 **最后一个** 作用,或授予在 高于代理用户自己的级别。 两者都是错误的.

## 托肯刷新

访问令牌是短寿命并刷新令牌旋转(单用),所以 a 刷新永远不能为同一目的运行两次. `refreshSession()` 是那个 刷新路径——客户端自己的401重试使用它,任何代码也应该使用 主动刷新 :

```typescript
import { refreshSession } from '@classic-homes/auth/core';

// Before a route guard reads claims (roles/permissions live in the JWT):
const outcome = await refreshSession({ onlyIfExpired: true });
// 'fresh'       — token still valid, nothing sent
// 'refreshed'   — tokens stored and the Svelte store already updated
// 'rejected'    — session cleared (handleSessionExpired/onSessionExpired/onLogout ran)
// 'unavailable' — server unreachable/5xx even after retries; session kept, try later
```

保证:每分页单飞(目前呼叫者共用一个呼叫), 以 Web 锁定在标签间序列化, 如果旋转了另一个标签, 则调节 首先 短暂的失败 `refreshRetryDelays` 备份 (默认) `[1000, 3000]`) (中文(简体) ). 不要打电话 `authApi.refreshToken` + 键 `updateStoredTokens` 绕过上面所有的东西 并让一个旋转的孤儿 符号 .

打电话 `initCrossTabSync()` 一旦在应用中开始这样一个刷新或签出另一个 选项卡将这个标签的斯维尔特商店补水.

## API 错误和访问调试

非-2xx的答复 `apiRequest` 编号 : `api.*` 丢出一个 `ApiError`。 。 `message` 是服务器的消息(如以前),加 `status`,《宪章》 `code`, (中文(简体) ), `details`, (中文(简体) ), `method` 财务报告和审定财务报表 `endpoint`数字 :

```typescript
import { isApiError } from '@classic-homes/auth/core';

try {
  await api.get('/homes', true);
} catch (e) {
  if (isApiError(e) && e.status === 403) {
    console.log(e.details?.required); // e.g. ['homes:read']
  }
}
```

### 访问探测器

钩子,比如搜索器 他们是惰性的 直到安装,在服务器上什么都不做.

-   `setAccessProbe(fn)` 收到每个访问决定:核心 `hasRole*`存储 `has*`, (中文(简体) ), `checkAuth` (单位:千美元)`source: 'route'`, (中文(简体) ), `filterByAccess`编号 :`canAccess` (单位:千美元)`'nav'`)和API 403s (英语:`'http'`,只有路径,没有查询字符串). 每次活动都有要求, `allowed` 和什么情况 `missing`。 。 。 .
-   在安装探测器时, 请求 API 源头( 从来没有其他主机) 发送 `X-Access-Debug: 1`。CHAPI 回复管理会话 `X-Access-Redacted: taxId=vendors:read:sensitive, notes=*`,作为 资料 `source: 'redacted'` 活动(`allowed: true`,只有路径) `fields: { name, permission }[]`· ; `*` 表示没有配置权限 (默认-否定). 它反映了真正的象征, 从来没有"视同"。 没有头,没有头 活动。 自定义头条使跨源 API 请求在飞行前完成 .
-   `setAccessSubject(userId, { roles, permissions })` 将 UI 预览为另一个 角色集 (“ 视图为” ) 仅针对该用户。 其他用户的检查是 未受影响, `authStore.user` 从未更改,预览结束于任意 登录、登录或会话过期。 签入角色标注( E)`allowedRoles`总是 使用真正的角色.
-   `evaluateAccess(subject, requirement)` 是共享的任意/所有决定 `checkAuth` 用途; `checkAuth` 成果现在包括 `missing` 拒绝.

这些改变只涉及UI的决定. API 授权每个请求与真实 符号,所以预览永远不能允许访问 .

## API 参考

### 核心出口

```typescript
import {
  // Initialization
  initAuth,
  getConfig,
  isInitialized,

  // Service
  authService,
  AuthService,

  // API
  authApi,

  // MFA Guards
  isMfaChallengeResponse,
  isLoginSuccessResponse,
  getMfaToken,
  getAvailableMethods,

  // JWT Utilities
  decodeJWT,
  isTokenExpired,
  getTokenRemainingTime,

  // Roles
  ROLE_NAMES,
  ROLE_HIERARCHY,
  getUserRoles,
  computePrimaryRole,
  getEffectiveLevel,
  hasRole,
  hasAnyRole,
  hasAllRoles,

  // Errors
  ApiError,
  isApiError,

  // Access probe (see "API errors and access debugging")
  setAccessProbe,
  setAccessSubject,
  clearAccessSubject,
  evaluateAccess,

  // Types
  type User,
  type UserRolesResponse,
  type RoleName,
  type AuthState,
  type LoginCredentials,
  type LoginResponse,
  type LogoutResponse,
  type RegisterData,
  type AuthConfig,
  type LoginOptions,
  type MFAVerifyOptions,
} from '@classic-homes/auth/core';
```

### Svelte 导出

```typescript
import {
  // Store
  authStore,
  isAuthenticated,
  currentUser,

  // Actions
  authActions,

  // Guards
  checkAuth,
  createAuthGuard,
  requireAuth,
  requireRole,
  requirePermission,
  protectedLoad,
} from '@classic-homes/auth/svelte';
```

## 配置选项

```typescript
interface AuthConfig {
  /** Base URL for the auth API */
  baseUrl: string;

  /** Custom fetch implementation (useful for SSR or testing) */
  fetch?: typeof fetch;

  /** Storage adapter for token persistence */
  storage?: StorageAdapter;

  /** Storage key prefix for auth data */
  storageKey?: string;

  /** SSO configuration */
  sso?: {
    enabled: boolean;
    provider: string;
    authorizeUrl?: string;
    /** Reject SSO callbacks whose client_state is missing or wrong (default false) */
    requireClientState?: boolean;
  };

  /** Callback when auth errors occur */
  onAuthError?: (error: Error) => void;

  /** Callback when tokens are refreshed */
  onTokenRefresh?: (tokens: { accessToken: string; refreshToken: string }) => void;

  /** Callback when user is logged out */
  onLogout?: () => void;
}
```

## Auth 动作

这个 `authActions` 对象提供认证操作的方法:

```typescript
// Set auth data after login
authActions.setAuth(accessToken, refreshToken, user, sessionToken);

// Update tokens after refresh
authActions.updateTokens(accessToken, refreshToken);

// Update user profile
authActions.updateUser(user);

// Clear auth state (local logout)
authActions.logout();

// SSO-aware logout (calls API, returns SSO logout URL if applicable)
const result = await authActions.logoutWithSSO();
if (result.ssoLogoutUrl) {
  window.location.href = result.ssoLogoutUrl;
}

// Permission and role checks
authActions.hasPermission('users:read');
authActions.hasRole('admin');
authActions.hasAnyRole(['admin', 'manager']);
authActions.hasAllRoles(['admin', 'manager']);
authActions.hasAnyPermission(['users:read', 'users:write']);
authActions.hasAllPermissions(['users:read', 'users:write']);

// Reload auth from storage
authActions.rehydrate();
```

## Auth 存储状态

```typescript
interface AuthState {
  accessToken: string | null;
  refreshToken: string | null;
  user: User | null;
  isAuthenticated: boolean;
}
```

## 使用时使用@ classic- homes/ some- svelte

认证软件包与窗体验证结合 `@classic-homes/theme-svelte`数字 :

```svelte
<script lang="ts">
  import { useForm, loginSchema } from '@classic-homes/theme-svelte';
  import { authService, isMfaChallengeResponse, getMfaToken } from '@classic-homes/auth/core';
  import { goto } from '$app/navigation';

  const form = useForm({
    schema: loginSchema,
    initialValues: {
      email: '',
      password: '',
      rememberMe: false,
    },
    onSubmit: async (data) => {
      const response = await authService.login({
        username: data.email,
        password: data.password,
      });

      if (isMfaChallengeResponse(response)) {
        const mfaToken = getMfaToken(response);
        goto(`/auth/mfa-challenge?token=${mfaToken}`);
        return;
      }

      // Auth state automatically set
      goto('/dashboard');
    },
  });
</script>

<form onsubmit={form.handleSubmit}>
  <input bind:value={form.data.email} />
  {#if form.errors.email}
    <span class="error">{form.errors.email}</span>
  {/if}
  <!-- ... -->
</form>
```

## 自动刷新

当 :

-   API 请求返回 401 未授权
-   刷新令牌是有效的

Svelte 商店在刷新符号时会自动更新, 因此您的 UI 保持同步 .

## 测试公用事业

认证包包括用于单位的综合测试设施和整合测试.

### 安装

```bash
# The testing utilities are included in the main package
npm install @classic-homes/auth
```

### 快速启动

```typescript
import { describe, it, beforeEach, afterEach, expect } from 'vitest';
import {
  setupTestAuth,
  mockUser,
  configureMFAFlow,
  assertAuthenticated,
} from '@classic-homes/auth/testing';
import { authService, isMfaChallengeResponse } from '@classic-homes/auth/core';

describe('Login Flow', () => {
  let cleanup: () => void;
  let mockFetch;

  beforeEach(() => {
    const ctx = setupTestAuth();
    cleanup = ctx.cleanup;
    mockFetch = ctx.mockFetch;
  });

  afterEach(() => cleanup());

  it('handles successful login', async () => {
    const response = await authService.login({
      username: 'test@example.com',
      password: 'password',
    });

    expect(response.user).toMatchObject(mockUser);
    mockFetch.assertCalled('/auth/login');
  });

  it('handles MFA flow', async () => {
    configureMFAFlow(mockFetch);

    const response = await authService.login({
      username: 'test@example.com',
      password: 'password',
    });

    expect(isMfaChallengeResponse(response)).toBe(true);
  });
});
```

### 测试出口

```typescript
import {
  // Fixtures - Pre-defined test data
  mockUser,
  mockAdminUser,
  mockSSOUser,
  mockMFAUser,
  mockAccessToken,
  mockRefreshToken,
  mockLoginSuccess,
  mockMFARequired,
  createMockUser,
  createMockTokenPair,
  createMockLoginSuccess,

  // Mocks - Test doubles for dependencies
  MockStorageAdapter,
  MockFetchInstance,
  MockAuthStore,
  createMockStorage,
  createMockFetch,
  createMockAuthStore,

  // Setup Helpers
  setupTestAuth,
  createTestAuthHelpers,
  quickSetupAuth,
  withTestAuth,

  // State Simulation
  authScenarios,
  applyScenario,
  configureMFAFlow,
  configureTokenRefresh,
  configureSSOLogout,
  simulateLogin,
  simulateLogout,

  // Assertions
  assertAuthenticated,
  assertUnauthenticated,
  assertHasPermissions,
  assertHasRoles,
  assertTokenValid,
  assertApiCalled,
  assertStoreMethodCalled,
  assertRequiresMFA,
} from '@classic-homes/auth/testing';
```

### 雾取

这个 `MockFetchInstance` 提供带有预先定义的认证路径的可配置的模拟获取 :

```typescript
const ctx = setupTestAuth();
const { mockFetch } = ctx;

// Default routes are pre-configured for all auth endpoints

// Customize responses
mockFetch.requireMFA(); // Login requires MFA
mockFetch.failLogin('Invalid credentials'); // Login fails
mockFetch.enableSSOLogout(); // Logout returns SSO URL

// Add custom routes
mockFetch.addRoute({
  method: 'GET',
  path: '/custom/endpoint',
  response: { data: 'custom response' },
});

// Fail specific endpoints
mockFetch.failEndpoint('GET', '/auth/profile', 403, 'Forbidden');

// Check call history
expect(mockFetch.wasCalled('/auth/login')).toBe(true);
mockFetch.assertCalled('/auth/profile');
mockFetch.assertNotCalled('/auth/logout');
```

### Mock Auth 存储器

这个 `MockAuthStore` 模仿 Svelte 认证商店 :

```typescript
const store = createMockAuthStore();

// Simulate states
store.simulateAuthenticated(mockAdminUser);
store.simulateUnauthenticated();

// Direct state manipulation
store.setState({ isAuthenticated: true, user: mockUser });

// Check method calls
store.assertMethodCalled('setAuth');
store.assertMethodNotCalled('logout');

// Get call history
const calls = store.getCallsFor('setAuth');
```

### 预定义情景

应用常见的认证方案进行测试 :

```typescript
import { authScenarios, applyScenario } from '@classic-homes/auth/testing';

// Available scenarios:
// - 'unauthenticated'
// - 'authenticated'
// - 'admin'
// - 'ssoUser'
// - 'mfaEnabled'
// - 'unverifiedEmail'
// - 'inactive'
// - 'expiredToken'

const store = createMockAuthStore();
applyScenario(store, 'admin');
expect(store.user?.roles).toContain('admin');
```

### 自定义请求

使用内置断言进行常见检查:

```typescript
import {
  assertAuthenticated,
  assertHasPermissions,
  assertTokenValid,
  assertApiCalled,
  assertRequiresMFA,
} from '@classic-homes/auth/testing';

// Auth state assertions
assertAuthenticated(store.getState());
assertUnauthenticated(store.getState());

// Permission assertions
assertHasPermissions(user, ['read:profile', 'write:profile']);
assertHasRoles(user, ['admin']);

// Token assertions
assertTokenValid(accessToken);
assertTokenExpired(oldToken);

// API call assertions
assertApiCalled(mockFetch, 'POST', '/auth/login', {
  times: 1,
  body: { username: 'test', password: 'pass' },
});

// MFA assertions
assertRequiresMFA(loginResponse);
assertNoMFARequired(loginResponse);
```

### 孤立的试验背景

在孤立的认证背景中运行测试 :

```typescript
import { withTestAuth } from '@classic-homes/auth/testing';

// Automatic setup and cleanup
await withTestAuth(async ({ mockFetch, mockStore }) => {
  mockFetch.requireMFA();
  const response = await authService.login({ username: 'test', password: 'pass' });
  expect(response.requiresMFA).toBe(true);
});
```

### 用户固定

创建自定义测试用户 :

```typescript
import {
  mockUser,
  mockAdminUser,
  createMockUser,
  createMockUserWithRoles,
} from '@classic-homes/auth/testing';

// Use pre-defined users
expect(mockUser.roles).toEqual(['user']);
expect(mockAdminUser.permissions).toContain('manage:system');

// Create custom users
const customUser = createMockUser({
  email: 'custom@example.com',
  firstName: 'Custom',
});

// Create users with specific RBAC
const managerUser = createMockUserWithRoles(['manager', 'user'], ['read:reports', 'write:reports']);
```

## 许可证

麻省理工学院

## 常见问题

### @classic-homes/auth 是什么？

经典主题应用程序的认证服务和 Svelte 绑定

### 如何安装 @classic-homes/auth？

在项目目录运行 npm install @classic-homes/auth，或使用 pnpm add / yarn add / bun add。

### @classic-homes/auth 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @classic-homes/auth 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @classic-homes/auth 可以商用吗？

@classic-homes/auth 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @classic-homes/auth 的下载量有多少？

根据 npm 官方下载统计，@classic-homes/auth 在 2026/10/10 – 2026/11/08 这 30 天内被下载 1,736 次，日均约 58 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @classic-homes/auth 还在维护吗？

最新版本 1\.47\.0 发布于 2026/11/10（今天）；至今共发布 76 个版本，其中过去 12 个月发布了 76 个；首次发布于 2026/01/09。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

