@amaster.ai/auth-client

中文文档
1.1.72发布于 2026/11/05MITTypeScriptESM + CJS近 30 天下载 1938

Authentication SDK for Amaster platform

原始使用文档
文档目录22 节
  1. Features
  2. Installation
  3. Quick Start
  4. Configuration
  5. Zero Configuration (Recommended)
  6. Optional Configuration
  7. Authentication
  8. Register
  9. Login
  10. OAuth Login
  11. Mini Program Login
  12. Logout
  13. User Management
  14. Get Current User
  15. Update Profile
  16. Change Password
  17. Permission Checks
  18. Anonymous Access Support
  19. Local Permission Checks (Fast)
  20. Organization positions
  21. 微信手机号授权直接登录
  22. Embedded browser sessions

@amaster.ai/auth-client

Authentication SDK for Amaster Platform - Complete client-side authentication solution with OAuth, session management, and permission checks.

Features

  • 🔐 Multiple Authentication Methods: Username/Email/Phone + Password, Verification Code, OAuth
  • 🔄 Automatic Token Refresh: JWT token auto-refresh before expiration
  • 📦 Token Storage: localStorage or sessionStorage with SSR support
  • 🎯 Permission System: Role-based and permission-based access control
  • 👤 Anonymous Access: Automatic support for anonymous users with configurable permissions
  • 🔗 OAuth Integration: Google, GitHub, WeChat OAuth, WeChat Mini Program, and custom OAuth providers
  • 📡 Event System: Listen to login, logout, and token events
  • 💾 Session Management: View and revoke active sessions
  • 🔒 Type-Safe: Full TypeScript support

Installation

pnpm add @amaster.ai/auth-client
# or
npm install @amaster.ai/auth-client

Quick Start

import { createAuthClient } from "@amaster.ai/auth-client";

// Initialize the client (with optional callbacks)
const authClient = createAuthClient({
  onTokenExpired: () => {
    window.location.href = "/login";
  },
  onUnauthorized: () => {
    window.location.href = "/login";
  },
});

// Register a new user
await authClient.register({
  email: "[email protected]",
  password: "Password@123",
  displayName: "John Doe",
});

// Login
const result = await authClient.login({
  loginType: "email",
  email: "[email protected]",
  password: "Password@123",
});

if (result.data) {
  console.log("Logged in:", result.data.user);
}

// Get current user
const user = await authClient.getMe();

// Check permissions
if (authClient.hasPermission("user", "read")) {
  // Show user list
}

Configuration

The SDK works out of the box with zero configuration:

const authClient = createAuthClient();

// Storage auto-detects environment:
// - Browser → localStorage
// - WeChat Mini Program → wx.setStorageSync
// - SSR/Node.js → no-op (no persistence)

Optional Configuration

interface AuthClientOptions {
  baseURL?: string; // API base URL, defaults to window.location.origin
  headers?: Record<string, string>; // Custom headers for all requests
  onTokenExpired?: () => void; // Token expiration callback
  onUnauthorized?: () => void; // Unauthorized (401) callback
  autoHandleOAuthCallback?: boolean; // Auto-handle OAuth callback hash on init, default true
  autoRedirectAfterLogin?: boolean; // Auto-consume ?redirect=... after any successful login, default true
}

Example:

const authClient = createAuthClient({
  onTokenExpired: () => (window.location.href = "/login"),
  onUnauthorized: () => alert("Session expired"),
});

Built-in Features (Zero Config):

  • ✅ Auto environment detection (browser, WeChat Mini Program, SSR)
  • ✅ Auto token refresh (5 minutes before expiry)
  • ✅ Auto permission sync
  • ✅ Auto anonymous support
  • ✅ Smart storage (localStorage, wx.storage, no-op for SSR)

Authentication

Register

await authClient.register({
  email: "[email protected]",
  password: "Password@123",
  displayName: "John Doe",
});

With Captcha:

// 1. Get captcha
const captchaData = await authClient.getCaptcha();
document.getElementById("captcha-img").src = captchaData.data.captchaImage;

// 2. Register with captcha
const userInputCode = "AB12";
await authClient.register({
  email: "[email protected]",
  password: "Password@123",
  captcha: `${captchaData.data.captchaId}:${userInputCode}`,
});

Login

Password Login:

await authClient.login({
  loginType: "email",
  email: "[email protected]",
  password: "Password@123",
});

Verification Code Login:

// 1. Send code
await authClient.sendCode({
  type: "email",
  email: "[email protected]",
});

// 2. Login with code
await authClient.loginWithCode({
  loginType: "email",
  email: "[email protected]",
  code: "123456",
});

OAuth Login

Full-page redirect with manual callback handling:

const authClient = createAuthClient({
  autoHandleOAuthCallback: false,
});

// Login page
sessionStorage.setItem("oauth_redirect", "/dashboard");
authClient.loginWithOAuth("google");

// Callback page (/auth/callback)
const result = await authClient.handleOAuthCallback();
if (result.error) {
  console.error(result.error.message);
  return;
}
const redirectUrl = sessionStorage.getItem("oauth_redirect") || "/";
window.location.href = redirectUrl;

OAuth callbacks are handled automatically by default. Do not also call handleOAuthCallback() unless the client was created with autoHandleOAuthCallback: false. Repeated calls are instance-idempotent, but choosing one mode keeps callback ownership explicit.

If the callback page URL already carries ?redirect=... such as /login?redirect=%2Fapi%2Foauth%2Fauthorize..., the SDK will reuse that redirect target automatically after a successful OAuth callback. Both #access_token=... and #accessToken=... callback hash formats are supported.

By default, the same ?redirect=... handling is also applied to password login, verification-code login, registration auto-login, and mini-program login. Set autoRedirectAfterLogin: false to disable this behavior and fully control navigation in the application layer.

When the SDK actually consumes a redirect target, successful login results will include result.data.redirectHandled === true and result.data.redirectTarget. Applications that still run their own post-login navigation should short-circuit on that flag to avoid double redirects.

WeCom in-client H5 login:

authClient.loginWithOAuth("wecom", "/dashboard");

This starts enterprise-member webpage authorization inside the WeCom client, not QR login in a normal browser. Configure the application's collaboration.wecom credentials through the enterprise-collaboration AddOn, enable wecom in auth.loginMethods, and initialize the auth client on the return page. The default callback handler consumes the token hash and loads the current user. Generic OAuth redirect targets are sent as redirect_uri and must be application-local paths or URLs with the same origin as the Runtime callback.

Start WeCom login from a public login page. The SDK records that page's path and query as error_redirect_uri, excluding stale OAuth error parameters. Runtime returns cancelled/failed login there with oauth_error and error_description; missing or expired state falls back to /login. Provide that public route (or an alias), render the error as escaped text and offer retry. Automatic callback handling does not render error UI. For authref SSO, call WeCom in the referenced authentication app and preserve the login page's pathname and query as the success return target so its authorization continuation can resume.

Application-scoped Feishu login:

// The SDK uses requestAccess in the Feishu client and regular OAuth elsewhere.
// Pass a local path to restore the page that initiated login.
const result = await authClient.loginWithFeishu("/dashboard");

// Browser OAuth returns null because navigation has started. The callback page
// exchanges the short-lived, one-time login_ticket automatically.
if (result?.error) {
  console.error(result.error.message);
}

// /auth/callback, when autoHandleOAuthCallback is false
const callbackResult = await authClient.handleOAuthCallback();

The Feishu App ID and App Secret belong to the current application environment, not to the Platform process. The SDK only receives the public App ID. The App Secret stays in Runtime, and a browser callback carries a 60-second one-time ticket instead of an application access token. Inside Feishu, the SDK waits for window.h5sdk.ready() before requesting a code. If the client does not support requestAccess, the SDK falls back to requestAuthCode.

Applications that already obtain a Feishu code may call loginWithFeishuCode(code, returnUrl). A callback can also explicitly call exchangeOAuthLoginTicket(ticket), although handleOAuthCallback() normally handles this automatically.

Popup window:

// Main page
const popup = window.open("/api/auth/oauth/google", "oauth-login", "width=600,height=700");

window.addEventListener("message", (event) => {
  if (event.data.type === "oauth-success") {
    authClient.setAccessToken(event.data.accessToken);
    popup?.close();
  }
});

// Callback page
if (window.opener) {
  const result = await authClient.handleOAuthCallback();
  if (result.data?.accessToken) {
    window.opener.postMessage(
      { type: "oauth-success", accessToken: result.data.accessToken },
      origin
    );
  }
}

Mini Program Login

The SDK exposes a unified Mini Program API for both WeChat and Douyin.

  • WeChat runtime: auto-detects wx and uses WeChat endpoints
  • Douyin Mini Program: auto-detects the tt runtime and uses Douyin endpoints
  • Non-runtime or mixed environments: you can explicitly pass platform

The SDK automatically detects the Mini Program storage runtime and uses WeChat wx.storage or Douyin tt.storage. For most app code, prefer the high-level methods:

  • miniLogin() - automatically calls Taro.login / Douyin tt.login / WeChat wx.login, then completes backend login
  • miniGetPhone(...) - accepts a raw code or event object, extracts the phone code, then binds the phone number
import { createAuthClient } from "@amaster.ai/auth-client";
import Taro from "@tarojs/taro";

// Zero configuration - auto-detects mini-program environment
const authClient = createAuthClient();

const result = await authClient.miniLogin();

if (result.data) {
  console.log("Logged in:", result.data.user);
  // Token automatically saved to mini-program storage
  Taro.switchTab({ url: "/pages/index/index" });
} else if (result.error) {
  Taro.showToast({
    title: result.error.message || "Login failed",
    icon: "none",
  });
}

Login with Douyin Mini Program:

const result = await authClient.miniLogin({
  platform: "douyin",
});

if (result.data) {
  console.log("Logged in:", result.data.user);
} else if (result.error) {
  Taro.showToast({
    title: result.error.message || "Login failed",
    icon: "none",
  });
}

Get user phone number (optional):

<!-- WXML -->
<button
  open-type="getPhoneNumber"
  bindgetphonenumber="onGetPhoneNumber"
>
  Get Phone Number
</button>
// JS
async onGetPhoneNumber(e) {
  const result = await authClient.miniGetPhone(e);

  if (result.data) {
    console.log("Phone number:", result.data.phone);
    console.log("Verified:", result.data.phoneVerified);

    // Update UI with phone number
    this.setData({
      phone: result.data.phone
    });
  } else if (result.error) {
    Taro.showToast({
      title: result.error.message || 'Failed to get phone number',
      icon: 'none'
    });
  } else {
    // User denied authorization
    console.log("User cancelled phone number authorization");
  }
}

Get Douyin Mini Program phone number:

async function onGetPhoneNumber(e) {
  const result = await authClient.miniGetPhone({
    ...e,
    platform: "douyin",
  });

  if (result.data) {
    console.log("Phone number:", result.data.phone);
  }
}

Complete Mini Program example:

// pages/login/login.js
import { createAuthClient } from "@amaster.ai/auth-client";
import Taro from "@tarojs/taro";

const authClient = createAuthClient();

Page({
  data: {
    userInfo: null,
    hasPhone: false,
  },

  // Auto-login on page load
  onLoad() {
    this.handleLogin();
  },

  // WeChat Mini Program login
  async handleLogin() {
    Taro.showLoading({ title: "Logging in..." });

    const result = await authClient.miniLogin();

    Taro.hideLoading();

    if (result.data) {
      this.setData({
        userInfo: result.data.user,
      });

      // Navigate to home
      Taro.switchTab({ url: "/pages/index/index" });
    } else {
      Taro.showToast({
        title: "Login failed",
        icon: "none",
      });
    }
  },

  // Get phone number with user authorization
  async onGetPhoneNumber(e) {
    const { code } = e.detail;

    if (!code) {
      Taro.showToast({
        title: "Authorization cancelled",
        icon: "none",
      });
      return;
    }

    Taro.showLoading({ title: "Getting phone..." });

    const result = await authClient.miniGetPhone(e);

    Taro.hideLoading();

    if (result.data) {
      this.setData({
        hasPhone: true,
      });

      Taro.showToast({
        title: "Phone number obtained",
        icon: "success",
      });
    } else {
      Taro.showToast({
        title: result.error?.message || "Failed to get phone",
        icon: "none",
      });
    }
  },
});

Low-level compatibility APIs (deprecated, advanced use only):

If your app already has a login code or phone code, you can still call:

  • loginWithMiniProgram(code | { code, platform })
  • getMiniProgramPhoneNumber(code | { code, platform })

Logout

await authClient.logout();

User Management

Get Current User

const result = await authClient.getMe();
if (result.data) {
  console.log(result.data); // User object
}

Update Profile

await authClient.updateMe({
  displayName: "New Name",
  avatarUrl: "https://example.com/avatar.jpg",
});

Change Password

await authClient.changePassword({
  oldPassword: "OldPassword@123",
  newPassword: "NewPassword@123",
});

Permission Checks

Anonymous Access Support

The SDK automatically supports anonymous users with zero configuration:

// Create client
const authClient = createAuthClient();

// Permission checks work for both authenticated and anonymous users
if (authClient.hasPermission("article", "read")) {
  showArticleList();
}

// Check if user is anonymous
if (authClient.isAnonymous()) {
  showLoginPrompt();
}

// After login, permissions automatically update
await authClient.login({ ... });

Local Permission Checks (Fast)

Permissions are cached locally for fast UI checks:

// Check role
if (authClient.hasRole("admin")) {
  showAdminPanel();
}

if (authClient.isAnonymous()) {
  showLoginPrompt();
}

// Check permission
if (authClient.hasPermission("user", "read")) {
  showUserList();
}

## OAuth Bindings

### Get Bindings

```typescript
const result = await authClient.getOAuthBindings();
if (result.data) {
  console.log(result.data); // Array of OAuthBinding
}
```

### Bind OAuth Account

```typescript
authClient.bindOAuth("google"); // Redirects to OAuth flow
```

### Unbind OAuth Account

```typescript
await authClient.unbindOAuth("google");
```

## Session Management

### Get Sessions

```typescript
const result = await authClient.getSessions();
if (result.data) {
  result.data.forEach((session) => {
    console.log(session.ip, session.userAgent, session.isCurrent);
  });
}
```

### Revoke Session

```typescript
await authClient.revokeSession("session-id");
```

### Revoke All Sessions

```typescript
const result = await authClient.revokeAllSessions();
if (result.data) {
  console.log(`Revoked ${result.data.revokedCount} sessions`);
}
```

## Event System

### Available Events

- `login` - User logged in, callback: `(user: User) => void`
- `logout` - User logged out, callback: `() => void`
- `tokenExpired` - Token expired, callback: `() => void`
- `tokenRefreshed` - Token refreshed, callback: `(token: string) => void`
- `unauthorized` - Unauthorized (401), callback: `() => void`

### Usage

```typescript
// Subscribe to events
authClient.on("unauthorized", () => {
  authClient.clearAuth();
  window.location.href = "/login";
});

authClient.on("login", (user) => {
  console.log("User logged in:", user.displayName);
});

authClient.on("tokenRefreshed", (token) => {
  console.log("Token refreshed");
});

// Unsubscribe
const handler = () => console.log("Logged out");
authClient.on("logout", handler);
authClient.off("logout", handler);
```

## Utility Methods

### Check Authentication Status

```typescript
if (authClient.isAuthenticated()) {
  // User is logged in
}
```

### Get Access Token

```typescript
const token = authClient.getAccessToken();
```

### Set Access Token

```typescript
authClient.setAccessToken("your-token-here");
```

### Clear Auth Data

```typescript
authClient.clearAuth(); // Clears token and user data
```

## Error Handling

```typescript
try {
  await authClient.login({ ... });
} catch (error) {
  // Network error or exception
  console.error("Login failed:", error);
}

// Or use result pattern
const result = await authClient.login({ ... });
if (result.error) {
  // API error response
  if (result.error.status === 401) {
    alert("Invalid email or password");
  } else if (result.error.status === 403) {
    alert("Account is disabled");
  } else {
    alert(`Login failed: ${result.error.message}`);
  }
}
```

## Best Practices

### Token Management

- Tokens are automatically managed by the SDK
- Access Token refreshes 5 minutes before expiration
- No manual token handling required

### Permission Checks

- Use permission checks for UI control (show/hide buttons, menus)
- Frontend checks are NOT security measures
- Backend must always verify permissions

### SSR Support

The SDK works in both browser and SSR environments:

```typescript
const authClient = createAuthClient();
```

## TypeScript

Full TypeScript support:

```typescript
import type {
  User,
  LoginResponse,
  Session,
  OAuthBinding,
} from "@amaster.ai/auth-client";
```

## License

MIT

## Contributing

Contributions are welcome! Please read our contributing guidelines before submitting PRs.

Organization positions

const result = await client.auth.getPositions(); // unified application client
if (result.error) throw new Error(result.error.message);
// result.data: DepartmentPosition[] — { code, name, description? }

Standalone auth clients expose the same authClient.getPositions() method. It uses the public /api/org/positions endpoint with the current access token and requires org.read. Runtime resolves the current app/environment and authRef. The configured code is used for memberships and BPM rules; the name is for display. Empty configuration returns an empty array; authorization and service failures remain errors.

微信手机号授权直接登录

auth.miniPhoneLogin(e) 接收微信 getPhoneNumber 事件、授权 code 字符串或 { code, platform: 'wechat' }。无需预先调用 miniLogin(),不需要已有 Token,不支持抖音。

async function onPhoneLogin(e) {
  if (!e.detail?.code) return;
  const result = await auth.miniPhoneLogin(e);
  if (result.error) {
    wx.showToast({ title: result.error.message, icon: 'none' });
    return;
  }
  // 登录状态已经保存,按应用现有规则导航。
}

后端使用微信验证的手机号匹配已有账号;没有账号时遵循应用注册开关。停用账号、关闭注册时的新用户、重复手机号账号均拒绝登录。此方法不关联 OpenID,原有 miniLogin() 和 miniGetPhone() 保持不变。需要先部署支持 POST /api/auth/miniprogram/phone-login 的 runtime。

Embedded browser sessions

Use createAuthClient({ baseURL: applicationOrigin, withCredentials: true, storage: createMemoryStorage(), autoHandleOAuthCallback: false, autoRedirectAfterLogin: false }). restoreSession() uses the existing application refresh cookie, loads the application user and starts normal renewal. An explicit storage adapter or calling restoreSession() opts into refresh coalescing, renewal rescheduling and pending-response protection. Existing clients that use neither retain their storage, refresh request/timer behavior and profile response handling; cookie credentials remain opt-in.

getOAuthLoginURL(provider, applicationRelativeReturnPath) resolves against the API origin and lets a host open a popup. After the application's normal OAuth callback sets its HttpOnly refresh cookie, a static application-origin completion page can call completeOAuthPopup(). It consumes requestId and openerOrigin from its query, clears its query/fragment and emits only { type: "amaster:oauth-complete", requestId }. The caller must validate the message origin, exact popup and pending request, then restore the application session through its normal API. No token or refresh credential is sent by the message.

The existing loginWithOAuth() keeps its current-origin navigation and provider-specific behavior, including when baseURL points at another origin. Use the new URL method explicitly when application-origin navigation is needed.

The public AuthClient and AuthModule types keep their original method sets, so existing implementations and mocks do not need browser-session methods. The default factory signature and ReturnType<typeof createAuthClient> remain AuthClient. Explicit storage or withCredentials options select the BrowserAuthClient overload, exposing restoreSession() and getOAuthLoginURL() without a cast. Use BrowserAuthClientOptions for an explicitly typed browser configuration and BrowserAuthClient for the extended result; it remains assignable to AuthClient.

Cross-origin cookie requests require the application's existing gateway to allow the exact host origin and credentials. HTTPS and browser cookie policy still apply. The SDK never creates application permissions or substitutes service/developer credentials.

版本与发布记录

@amaster.ai/auth-client 共发布 203 个版本,首次发布于 2026/02/02;过去 12 个月发布了 203 个版本。

版本发布日期距今
1.1.72latest2026/11/052 天前
1.1.712026/10/2514 天前
1.1.702026/10/081 个月前
1.1.692026/09/241 个月前
1.1.69-beta.0beta2026/09/231 个月前
1.1.682026/09/221 个月前

关于 @amaster.ai/auth-client 的常见问题

@amaster.ai/auth-client 是什么?

Authentication SDK for Amaster platform

如何安装 @amaster.ai/auth-client?

在项目目录运行 npm install @amaster.ai/auth-client,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:axios(^1.11.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@amaster.ai/auth-client 支持 TypeScript 吗?

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

@amaster.ai/auth-client 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@amaster.ai/auth-client 可以商用吗?

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

@amaster.ai/auth-client 的下载量有多少?

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

@amaster.ai/auth-client 还在维护吗?

最新版本 1.1.72 发布于 2026/11/05(2 天前);至今共发布 203 个版本,其中过去 12 个月发布了 203 个;首次发布于 2026/02/02。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。