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

@innerfire/auth-sdk

v0.3.0

Published

面向多租户 SaaS 应用的鉴权 SDK,提供登录、会话管理、权限校验、自动续期、跨项目恢复等能力。SDK 统一使用 Cookie 会话模式,内置 React 集成、Axios 拦截器与服务端 sid 恢复适配。

Readme

@innerfire/auth-sdk

面向多租户 SaaS 应用的鉴权 SDK,提供登录、会话管理、权限校验、自动续期、跨项目恢复等能力。SDK 统一使用 Cookie 会话模式,内置 React 集成、Axios 拦截器与服务端 sid 恢复适配。

安装

npm install @innerfire/auth-sdk
# peer dependencies
npm install react antd axios

快速开始

import {
  createAuthClient,
  AuthProvider,
  LoginCard,
  useAuth,
} from "@innerfire/auth-sdk/react";
import { setupAuthInterceptor } from "@innerfire/auth-sdk/interceptors";

const client = createAuthClient({
  baseURL: "https://auth.example.com",
  tenant: "my-org",
  projectId: "admin",
});

const api = axios.create({ baseURL: "https://api.example.com" });
setupAuthInterceptor(api, {
  client,
  onUnauthorized: () => {
    window.location.href = "/login";
  },
});

function App() {
  return (
    <AuthProvider client={client}>
      <Dashboard />
    </AuthProvider>
  );
}

function Dashboard() {
  const { session, tenantContext, logout } = useAuth();

  if (!session) {
    return <LoginCard client={client} />;
  }

  return (
    <div>
      <p>用户: {tenantContext?.userId}</p>
      <p>角色: {tenantContext?.roles.join(", ")}</p>
      <button onClick={logout}>退出</button>
    </div>
  );
}

鉴权流程

登录

用户提交凭证
    ↓
POST /auth/login  { username, password, provider, projectId }
    ↓
服务端验证 → 返回 { sessionId, projectToken }
    ↓
SDK 存储 sessionId(cookie)和 projectToken(sessionStorage)
    ↓
AuthProvider 更新上下文 → 触发 UI 重渲染

projectIdcreateAuthClient() 的固定配置提供,LoginCard 不再单独接收 projectId

会话校验

页面加载时,AuthProvider 自动调用 GET /auth/session 检查会话:

  • 会话有效 → 校准当前项目 token → 拉取租户上下文 (GET /me/tenant-context)
  • 会话无效/过期 → 清除存储,展示未登录状态

自动续期

Axios 拦截器在收到 401 响应时自动处理:

| 错误码 | 处理方式 | | ------------------------------------------------- | ------------------------------------------------------------------------------- | | SESSION_EXPIRED | 调用 POST /auth/refresh 续期后重试请求 | | PROJECT_TOKEN_EXPIRED / PROJECT_TOKEN_INVALID | 调用 changeToken() 重新换取当前项目 token,网络异常时按配置有限重试后重放请求 |

若最终仍无法恢复,SDK 会清除会话并触发 onUnauthorized 回调。

项目令牌切换

client.changeToken(projectId?)
    ↓
POST /projects/:id/access-tickets  → 获取临时票据
    ↓
POST /projects/:id/token-exchange  → 用票据换 projectToken
    ↓
存储新的 projectToken 和 projectId
  • 传入 projectId 时:为目标项目换取新 token
  • 不传时:回退到 createAuthClient() 中配置的固定 projectId

跨项目跳转

当用户从项目 A 跳转到项目 B 时,auth-sdk 支持通过 URL 传递 sid 来恢复会话,无需重新登录。

项目 A(来源项目):

const sid = client.getSessionId();
window.location.href = `${targetURL}?sid=${encodeURIComponent(sid ?? "")}`;

项目 B(目标项目,客户端恢复):

import { createAuthClient, AuthProvider } from '@innerfire/auth-sdk/react';

const client = createAuthClient({
  baseURL: 'https://auth.example.com',
  tenant: 'my-org',
  projectId: 'project-b',
});

function App() {
  return (
    <AuthProvider client={client}>
      {/* 你的应用 */}
    </AuthProvider>
  );
}

客户端工作流程:

  1. 用户从项目 A 跳转到项目 B,URL 携带 ?sid=<sessionId>
  2. 项目 B 的 AuthProvider 初始化时自动检测 URL 中的 sid
  3. SDK 将 sid 写入 cookie 会话存储
  4. SDK 清理 URL 中的 sid 参数
  5. 会话校验成功后,SDK 自动调用 changeToken(projectId) 获取项目 B 的 project token
  6. 用户以已登录状态进入项目 B

SSR / Middleware 适配:

如果目标应用需要在服务端首屏前恢复会话,可以使用 @innerfire/auth-sdk/middleware 提供的 sid 恢复辅助函数:

import { createSidResumeResponse } from "@innerfire/auth-sdk/middleware";

export function middleware(request: NextRequest) {
  const result = createSidResumeResponse(request.url, {
    cookieName: "auth_session",
    secure: true,
    sameSite: "lax",
  });

  if (!result) {
    return NextResponse.next();
  }

  const response = NextResponse.redirect(result.location);
  response.headers.set("set-cookie", result.setCookieHeader);
  return response;
}

这类服务端适配的推荐流程是:

  1. 在服务端检测 URL 中的 sid
  2. 生成写入 session cookie 的 Set-Cookie
  3. 重定向到移除 sid 后的 URL
  4. 页面进入客户端后,AuthProvider 再自动校准当前项目 token

注意事项:

  • sid 通过 URL 传递,必须使用 HTTPS
  • SDK 会在消费后立即清理 URL,避免 sid 泄露
  • 如果 sid 无效或已过期,SDK 会清理会话并按未登录态处理
  • 目标项目必须配置固定 projectId 才能自动恢复并换取项目 token

退出登录

client.logout()
    ↓
POST /auth/logout
    ↓
清除 sessionId、projectToken、projectId
    ↓
AuthProvider 上下文重置为未登录状态

子路径导出

SDK 按功能拆分为独立子路径,支持 tree-shaking:

| 子路径 | 内容 | | ---------------------------------- | ----------------------------------------------------------------------- | | @innerfire/auth-sdk/core | AuthClient、类型定义、存储、sid 工具 | | @innerfire/auth-sdk/react | AuthProvideruseAuthLoginCardSessionProvideruseSession | | @innerfire/auth-sdk/server | createAuthServer,服务端鉴权(SSR / 纯后端) | | @innerfire/auth-sdk/interceptors | Axios 请求/响应拦截器 | | @innerfire/auth-sdk/middleware | 服务端路由守卫、sid 恢复辅助函数 | | @innerfire/auth-sdk | 全量导出 |


模块详解

core — 创建客户端

import { createAuthClient } from "@innerfire/auth-sdk/core";

const client = createAuthClient({
  baseURL: "https://auth.example.com",
  tenant: "my-org",
  projectId: "admin",
  cookieName: "auth_session",
  cookieOptions: {
    path: "/",
    secure: true,
    sameSite: "lax",
  },
});

配置说明:

| 字段 | 说明 | | --------------- | ------------------------------------- | | baseURL | 认证 API 地址,默认同源 | | tenant | 租户标识,必填 | | projectId | 当前应用固定项目 ID,必填 | | cookieName | 会话 cookie 名称,默认 auth_session | | cookieOptions | 会话 cookie 属性 |

AuthClient 方法:

| 方法 | 说明 | | ------------------------- | ------------------------------------------------------- | | login(request) | 登录,自动使用配置中的 projectId 建立会话和项目 token | | logout() | 退出,清除存储 | | getSession() | 获取当前会话,无会话返回 null | | refreshSession() | 刷新会话 | | getTenantContext() | 获取租户上下文 | | getProviders() | 获取可用登录方式 | | changeToken(projectId?) | 为目标项目或默认项目重新换取 project token | | getSessionId() | 同步读取 sessionId | | setSessionId(sessionId) | 同步写入 sessionId | | getProjectToken() | 同步读取 projectToken | | getCurrentProjectId() | 同步读取当前项目 ID | | clearSession() | 清除所有会话数据 | | getConfig() | 获取配置 |


react — React 集成

AuthProvider

import { AuthProvider } from "@innerfire/auth-sdk/react";

<AuthProvider client={client}>{children}</AuthProvider>;

管理会话状态和租户上下文,挂载时自动校验会话,并在需要时自动校准当前项目 token。

useAuth()

const {
  session,
  tenantContext,
  isLoading,
  error,
  client,
  login,
  logout,
  refreshSession,
} = useAuth();

LoginCard

支持两种模式:

SPA 模式(直接调用 AuthClient):

import { LoginCard } from "@innerfire/auth-sdk/react";

<LoginCard
  client={client}
  onSuccess={(sessionId, projectToken) => router.push("/dashboard")}
/>;

SSR 模式(POST 到自己的 API route):

<LoginCard
  action="/api/auth/login"
  onSuccess={() => router.push("/dashboard")}
/>

action 模式下,LoginCard 会向指定 URL 发送 POST 请求,并从同级路径 /api/auth/providers 自动获取登录方式。也可通过 providers prop 直接传入,跳过自动获取。

基于 Ant Design 实现,自动支持多 provider tab 切换(如账号密码 + SSO)。

SessionProvider + useSession(SSR 模式)

SSR 框架中,由服务端解析 session 后通过 SessionProvider 传入,客户端组件通过 useSession 消费,无需在前端持有 Auth Center 配置。

// app/layout.tsx(Server Component)
import { cookies } from "next/headers";
import { auth } from "@/lib/auth";
import { SessionProvider } from "@innerfire/auth-sdk/react";

export default async function RootLayout({ children }) {
  const sid = cookies().get("auth_session")?.value;
  const data = sid ? await auth.getSession(sid) : null;

  return (
    <SessionProvider
      session={data?.session ?? null}
      tenantContext={data?.tenantContext ?? null}
    >
      {children}
    </SessionProvider>
  );
}
// app/dashboard/page.tsx(Client Component)
"use client";
import { useSession } from "@innerfire/auth-sdk/react";

export default function Dashboard() {
  const { session, tenantContext, status } = useSession();
  if (status === "unauthenticated") redirect("/login");
  return <div>欢迎,{tenantContext?.userId}</div>;
}

server — 服务端鉴权

createAuthServer() 用于 SSR 框架(Next.js、Nuxt 等)和纯后端 API 服务,直接与 Auth Center 通信,无跨域限制。

// lib/auth.ts
import { createAuthServer } from "@innerfire/auth-sdk/server";

export const auth = createAuthServer({
  baseURL: process.env.AUTH_CENTER_URL!,
  tenant: process.env.TENANT_ID!,
  projectId: process.env.PROJECT_ID!,
  secret: process.env.AUTH_SECRET, // 可选:服务间通信密钥
});

AuthServer 方法:

| 方法 | 说明 | | --------------------- | -------------------------------------------------------------------- | | login(credentials) | 登录,返回 sessionIdprojectTokensetCookieHeader | | verifySession(sid) | 验证 session 是否有效,返回 Sessionnull | | getSession(sid) | 获取 session + tenantContext,用于 SSR layout | | logout(sid) | 登出 | | refreshSession(sid) | 刷新 session | | getProviders() | 获取可用登录方式 | | resumeFromUrl(url) | 跨项目 sid resume:提取 sid → 换 token → 返回 cookie 指令 + 干净 URL |

Next.js API Route 示例:

// app/api/auth/login/route.ts
import { auth } from "@/lib/auth";

export async function POST(req: Request) {
  const body = await req.json();
  const result = await auth.login(body);

  return new Response(
    JSON.stringify({
      sessionId: result.sessionId,
      projectToken: result.projectToken,
    }),
    {
      headers: {
        "Content-Type": "application/json",
        "Set-Cookie": result.setCookieHeader,
      },
    },
  );
}

Next.js Middleware 路由守卫 + 跨项目 sid resume:

// middleware.ts
import { auth } from "@/lib/auth";

export async function middleware(req) {
  // 跨项目跳转:URL 带 ?sid=xxx 时自动换 token 并重定向
  const resumeResult = await auth.resumeFromUrl(req.url);
  if (resumeResult) {
    return new NextResponse(null, {
      status: 302,
      headers: {
        Location: resumeResult.location,
        "Set-Cookie": resumeResult.setCookieHeader,
      },
    });
  }

  // 正常鉴权
  const sid = req.cookies.get("auth_session")?.value;
  if (!sid) return NextResponse.redirect(new URL("/login", req.url));

  const session = await auth.verifySession(sid);
  if (!session) return NextResponse.redirect(new URL("/login", req.url));
}

Express 中间件示例:

import { auth } from "./lib/auth";

app.use(async (req, res, next) => {
  const token = req.headers["x-project-token"];
  if (!token) return res.status(401).json({ error: "Unauthorized" });

  // 也可用 verifySession 校验 cookie session
  next();
});

interceptors — Axios 拦截器

import axios from "axios";
import { setupAuthInterceptor } from "@innerfire/auth-sdk/interceptors";

const api = axios.create({ baseURL: "https://api.example.com" });

const cleanup = setupAuthInterceptor(api, {
  client,
  autoRefresh: true,
  changeTokenRetryCount: 1,
  onUnauthorized: () => {
    window.location.href = "/login";
  },
});

// 卸载时调用 cleanup() 移除拦截器

自动注入的请求头:

| Header | 值 | | ----------------- | ------------------------- | | X-Tenant-ID | 配置中的 tenant | | X-Project-Token | 当前 projectToken(如有) |


middleware — 服务端路由守卫

import {
  createAuthGuard,
  createSidResumeResponse,
} from "@innerfire/auth-sdk/middleware";

const guard = createAuthGuard({
  client,
  projectAccess: {
    projectId: "proj-123",
    resource: "articles",
    action: "read",
  },
  onUnauthorized: () => {
    redirect("/login");
  },
  onForbidden: () => {
    redirect("/403");
  },
});

export async function middleware(request: NextRequest) {
  const sidResume = createSidResumeResponse(request.url, {
    secure: true,
    sameSite: "lax",
  });

  if (sidResume) {
    const response = NextResponse.redirect(sidResume.location);
    response.headers.set("set-cookie", sidResume.setCookieHeader);
    return response;
  }

  return guard();
}

守卫流程:校验会话 → 获取租户上下文 → 可选校验项目权限 (POST /authz/check) → 可选获取数据范围 (POST /authz/query-scopes)


错误处理

SDK 统一抛出 AuthError,携带结构化错误码:

import { AuthError, AuthErrorCode } from "@innerfire/auth-sdk";

try {
  await client.login({
    username: "admin",
    password: "123",
    provider: "password",
  });
} catch (error) {
  if (error instanceof AuthError) {
    switch (error.code) {
      case AuthErrorCode.INVALID_CREDENTIALS:
        break;
      case AuthErrorCode.SESSION_EXPIRED:
        break;
      case AuthErrorCode.PROJECT_TOKEN_EXPIRED:
        break;
    }

    if (error.relogin) {
      // 需要重新登录
    }
  }
}

错误码列表:

| 错误码 | 说明 | | ------------------------ | ---------------- | | VALIDATION_ERROR | 请求参数校验失败 | | INVALID_CREDENTIALS | 用户名或密码错误 | | SESSION_EXPIRED | 会话已过期 | | SESSION_REVOKED | 会话已被撤销 | | PROJECT_NOT_ENABLED | 项目未启用 | | NO_PROJECT_ACCESS | 无项目访问权限 | | PROJECT_TOKEN_EXPIRED | 项目令牌过期 | | PROJECT_TOKEN_INVALID | 项目令牌无效 | | TENANT_CONTEXT_INVALID | 租户上下文无效 |


类型定义

所有类型通过 Zod schema 定义,同时导出 schema 和 TS 类型:

import type {
  AuthClientConfig,
  LoginRequest,
  LoginResponse,
  Session,
  TenantContext,
  LoginProvider,
  CookieOptions,
  AuthErrorCode,
} from "@innerfire/auth-sdk";