npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@amaster.ai/auth-client

v1.1.1

Published

Authentication SDK for Amaster platform

Readme

@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

Zero Configuration (Recommended)

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({
  baseURL: "https://api.example.com",
  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 (recommended):

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

// Callback page (/auth/callback)
const { user } = await authClient.handleOAuthCallback();
const redirectUrl = sessionStorage.getItem("oauth_redirect") || "/";
window.location.href = redirectUrl;

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.

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 { accessToken } = await authClient.handleOAuthCallback();
  window.opener.postMessage({ type: "oauth-success", accessToken }, origin);
}

WeChat Mini Program Login

The SDK automatically detects WeChat Mini Program environment and uses wx.storage:

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

// Zero configuration - auto-detects WeChat environment
const authClient = createAuthClient({
  baseURL: "https://api.yourdomain.com",
});

// Login with WeChat code
wx.login({
  success: async (res) => {
    if (res.code) {
      const result = await authClient.loginWithMiniProgram(res.code);

      if (result.data) {
        console.log("Logged in:", result.data.user);
        // Token automatically saved to wx.storage
        wx.switchTab({ url: "/pages/index/index" });
      } else if (result.error) {
        wx.showToast({
          title: result.error.message || "Login failed",
          icon: "none",
        });
      }
    }
  },
  fail: (err) => {
    console.error("wx.login failed:", err);
  },
});

Get user phone number (optional):

<!-- WXML -->
<button
  open-type="getPhoneNumber"
  bindgetphonenumber="onGetPhoneNumber"
>
  Get Phone Number
</button>
// JS
async onGetPhoneNumber(e) {
  const { code } = e.detail;

  if (code) {
    const result = await authClient.getMiniProgramPhoneNumber(code);

    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) {
      wx.showToast({
        title: result.error.message || 'Failed to get phone number',
        icon: 'none'
      });
    }
  } else {
    // User denied authorization
    console.log("User cancelled phone number authorization");
  }
}

Complete Mini Program example:

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

const authClient = createAuthClient({
  baseURL: "https://api.yourdomain.com",
});

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

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

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

    wx.login({
      success: async (res) => {
        if (res.code) {
          const result = await authClient.loginWithMiniProgram(res.code);

          wx.hideLoading();

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

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

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

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

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

    const result = await authClient.getMiniProgramPhoneNumber(code);

    wx.hideLoading();

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

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

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.