@amaster.ai/auth-客户端
Amaster 平台的认证 SDK - 与 OAuth 完成客户端认证解决方案,会话管理和权限检查.
特征
- 🔐 多重认证方法: 用户名/电子邮件/电话+密码,验证码,OAuth
- 🔄 自动刷新: JWT 证书在到期前自动更新
- 📦 托肯存储: 支持安全部门改革的本地Storage或会话Storage
- 🎯 权限系统: 基于角色和许可的访问控制
- 👤 匿名访问: 自动支持具有可配置权限的匿名用户
- 🔗 OAuth 整合: Google, GitHub, WeChat OAuth, WeChat Mini 程序, 定制 OAuth 供应商
- 📡 事件系统: 倾听登录、登录和象征性事件
- 💾 会话管理: 查看并撤销活动会话
- 🔒 类型安全: 完整类型脚本支持
安装
pnpm add @amaster.ai/auth-client
# or
npm install @amaster.ai/auth-client
快速启动
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
}
配置
零配置( 建议)
SDK从盒子里出来 零配置数字 :
const authClient = createAuthClient();
// Storage auto-detects environment:
// - Browser → localStorage
// - WeChat Mini Program → wx.setStorageSync
// - SSR/Node.js → no-op (no persistence)
可选配置
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
}
示例
const authClient = createAuthClient({
onTokenExpired: () => (window.location.href = "/login"),
onUnauthorized: () => alert("Session expired"),
});
已建地物( Zero Config) :
- * 自动环境检测(浏览器、微信程序、安全部门改革)
- * 自动令牌刷新(过期前5分钟)
- 自动权限同步
- QQ 自动匿名支持
- 智能存储(本地存储器, wx. storage, no-op for SSR)
认证
登记册
await authClient.register({
email: "[email protected]",
password: "Password@123",
displayName: "John Doe",
});
与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}`,
});
登录
密码登录 :
await authClient.login({
loginType: "email",
email: "[email protected]",
password: "Password@123",
});
校验码登录 :
// 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 登录
使用人工回调处理的全页重定向 :
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调用默认会自动处理. 不要同时打电话
handleOAuthCallback() 除非客户端是用
autoHandleOAuthCallback: false. 重复的电话是实例性的,但
选择一种模式可以使召回所有者明确无误.
如果回调页面 URL 已经携带 ?redirect=... 例如
/login?redirect=%2Fapi%2Foauth%2Fauthorize...SDK会重新使用
成功 OAuth 调用后自动重定向目标 。 两者
#access_token=... 财务报告和审定财务报表 #accessToken=... 支持调用hash格式.
默认,相同 ?redirect=... 处理也适用于密码登录,
校验码登录,注册自动登录,以及小型程序登录. 设定
autoRedirectAfterLogin: false 以禁用此行为并完全控制
应用层中的导航.
当 SDK 实际消耗方向目标时, 成功登录结果将
包含 result.data.redirectHandled === true 财务报告和审定财务报表 result.data.redirectTarget。 。 。 .
仍在运行自己的登录后导航的应用程序应当短路
在国旗上 避免双向.
WeCom 客户端 H5 登录 :
authClient.loginWithOAuth("wecom", "/dashboard");
这开始企业会员网页授权 在WeCom客户端,
不在普通浏览器中登录 QR 。 配置应用程序
collaboration.wecom 全权证书 enterprise-collaboration 添加 开始,
启用 wecom 内 auth.loginMethods,并初始化
返回页面。 默认的回调处理器会消耗符号散列并加载
当前用户。 一般 OAuth 重定向目标作为 redirect_uri 并且必须
成为应用程序本地路径或URL,其来源与运行时回调相同.
从一个公共登录页面开始 WeCom 登录. SDK记录了页面的路径和
查询为 error_redirect_uri,不包括 Stale OAuth 错误参数。 运行时间
返回已取消/ 登录失败 oauth_error 财务报告和审定财务报表 error_description· ;
缺少或过期状态又回到 /login提供公共路线(或
),使错误成为隐藏的文本,并提议重试。 自动回调
处理不会产生错误 UI 。 对于授权的SSO,请在引用中呼叫 WeCom
认证应用并保存登录页面的路径名和查询为
成功返回目标,以便恢复其授权的继续.
应用范围 Feishu 登录 :
// 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();
Feishu App ID和 App 秘密属于当前应用环境,
不对平台进程。 SDK只接收公开的AppID. 应用程序
秘密停留在运行时,浏览器的回调带60秒的一次性
票而不是应用程序访问符。 飞须,SDK在等
window.h5sdk.ready() 在请求代码之前。 如果客户端不支持
requestAccess,SDK掉回 requestAuthCode。 。 。 .
已经获得飞虎代码的应用程序可以调用
loginWithFeishuCode(code, returnUrl)。回调也可以明确调用
exchangeOAuthLoginTicket(ticket),尽管 handleOAuthCallback() 一般情况
自动处理.
弹出窗口 :
// 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
);
}
}
迷你程序登录
SDK曝光了针对WeChat和Douyin的统一的Mini Program API.
- WeChat 运行时间: 自动检测
wx并使用 WeChat 端点 - Douyin 迷你程序:自动检测
tt运行时间和使用 Douyin 端点 - 非运行时间或混合环境:可以明确通过
platform
SDK 自动检测 Mini 程序存储运行时间并使用 WeChat wx.storage 或杜因语 tt.storage。 。 。 .
对于大多数应用代码,偏好高端方法:
miniLogin()- 自动打电话Taro.login/ 杜因语tt.login/ 微调wx.login后端登录miniGetPhone(...)- 接受原始代码或事件对象,提取电话代码,然后绑定电话号码
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",
});
}
登录 Douyin 迷你程序 :
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",
});
}
获取用户电话号码( 可选) :
<!-- 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");
}
}
获取 Douyin 迷你程序电话号码 :
async function onGetPhoneNumber(e) {
const result = await authClient.miniGetPhone({
...e,
platform: "douyin",
});
if (result.data) {
console.log("Phone number:", result.data.phone);
}
}
完整小型程序示例 :
// 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",
});
}
},
});
低等相容性 API( 已过期, 仅高级使用):
如果您的应用程序已经登录 code 电话 code,你还可以打电话:
loginWithMiniProgram(code | { code, platform })getMiniProgramPhoneNumber(code | { code, platform })
注销
await authClient.logout();
用户管理
获取当前用户
const result = await authClient.getMe();
if (result.data) {
console.log(result.data); // User object
}
更新配置
await authClient.updateMe({
displayName: "New Name",
avatarUrl: "https://example.com/avatar.jpg",
});
更改密码
await authClient.changePassword({
oldPassword: "OldPassword@123",
newPassword: "NewPassword@123",
});
权限检查
匿名访问支持
SDK 自动支持匿名用户 零配置数字 :
// 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({ ... });
本地权限检查( 快速)
本地缓存权限用于快速UI检查 :
// 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.
组织职位
const result = await client.auth.getPositions(); // unified application client
if (result.error) throw new Error(result.error.message);
// result.data: DepartmentPosition[] — { code, name, description? }
独立认证客户端曝光相同 authClient.getPositions() 方法。 它利用公众 /api/org/positions 带有当前访问符并需要的结束点 org.read。运行时间解决当前应用/环境和 authRef。配置的代码用于会员和BPM规则;名称用于显示。 空配置返回一个空阵列; 授权和服务失败仍然是错误 .
微信手机号授权直接登录
auth.miniPhoneLogin(e) 接收微信 getPhoneNumber 密码 { code, platform: 'wechat' }。无需预先调用 miniLogin()托肯 托肯 托肯 托肯。
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, 12421 存档副本 miniLogin() 3⁄4 ̄ ̧漯B miniGetPhone() 保持不变。需要先部署支持 POST /api/auth/miniprogram/phone-login cha 运行时间。
嵌入式浏览器会话
使用 createAuthClient({ baseURL: applicationOrigin, withCredentials: true, storage: createMemoryStorage(), autoHandleOAuthCallback: false, autoRedirectAfterLogin: false })。 。 。 . restoreSession() 使用现有的应用程序刷新 cookie,加载应用程序用户并开始正常更新. 明确的存储适配器或调用 restoreSession() 选择更新连接、延期重新安排和待答复保护。 现有客户端既未保留存储,刷新请求/计时器行为和配置响应处理; cookie credits 仍可选入.
getOAuthLoginURL(provider, applicationRelativeReturnPath) 解析与 API 源码相对并让一个主机打开弹出. 在应用程序正常的 OAuth 调用后设置其 HttpOnresh cookie, 一个静态的应用程序起源的补全页面可以调用 completeOAuthPopup()它消耗 requestId 财务报告和审定财务报表 openerOrigin 从它的查询中清除它的查询/断层,并只发出 { type: "amaster:oauth-complete", requestId }。呼叫者必须验证信件的来源、准确的弹出和待处理的请求,然后通过正常的API恢复应用程序会话。 信件没有发送任何令牌或刷新证书 .
现有 loginWithOAuth() 保持其当前来源的导航和提供者行为,包括何时 baseURL 在另一个来源点。 在需要应用程序原产地导航时明确使用新的URL方法.
公众 AuthClient 财务报告和审定财务报表 AuthModule 类型保留其原有的方法集,因此现有的执行和模拟不需要浏览器会话方法. 默认工厂签名和 ReturnType<typeof createAuthClient> 保留 AuthClient明确 storage 或者说 withCredentials 选项选择 BrowserAuthClient 超载, 曝光 restoreSession() 财务报告和审定财务报表 getOAuthLoginURL() 没有石膏。 使用 BrowserAuthClientOptions 用于明确输入的浏览器配置和 BrowserAuthClient (a) 长期结果 AuthClient。 。 。 .
跨原产地饼干请求需要应用程序的现有网关,以允许准确的主机来源和证书. HTTPS和浏览器 cookie 政策仍然适用. SDK从未创建应用程序权限或代用服务/开发者证书.