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

@jxstjh/auth-client

v0.2.4

Published

匹配4a认证的客户端工具包

Readme

AuthClient

面向 4A 单点登录的前端客户端 SDK。

接入方式

npm 安装

npm install @jxstjh/auth-client
import {
  AuthClient,
  AuthMethod,
  LoginType,
} from '@jxstjh/auth-client'

浏览器 script 引入

可以使用 npm CDN,也可以将构建生成的 dist/index.js 部署到自己的静态资源服务器。生产环境建议固定版本号,避免自动升级引入不兼容变化:

<!-- CDN:请将版本号替换为实际发布版本 -->
<script src="https://unpkg.com/@jxstjh/[email protected]/dist/index.js"></script>

<!-- 或使用自行部署的文件 -->
<!-- <script src="/assets/auth-client/index.js"></script> -->

<script>
  window.ac = new window.AuthClient({
    SSOServer: 'https://sso.example.com',
    resourceCode: '由认证中心分配的资源code',
    method: window.AuthMethod.CUSTOM,
  })

  window.ac.on(window.ACEvents.ERROR, function (error) {
    console.error(error.msg)
  })

  window.ac.on(window.ACEvents.INITED, function (result) {
    if (result === false) {
      // /web/login 返回空响应,表示当前未登录。
      return
    }

    console.log(result.umsAdmin, result.tokenInfo, result.permissionList)
  })
</script>

script 文件会将 AuthClientAuthMethodACEventsLoginType 等公开成员挂载到 window。SDK 的认证功能运行在浏览器中;CommonJS 环境可以加载包入口用于构建和分析,但不能在没有 DOM 的服务端执行浏览器认证流程。

SSO 自动跳转模式

未登录时自动跳转到统一认证中心;已有登录缓存或携带有效 tmpTicket 时触发 inited 事件。

import {
  ACEvents,
  AuthClient,
  AuthMethod,
} from '@jxstjh/auth-client'
import type { AuthClientError } from '@jxstjh/auth-client'

const ac = new AuthClient({
  SSOServer: 'https://sso.example.com', // 替换为实际认证中心地址
  resourceCode: '由认证中心分配的资源code',
  method: AuthMethod.SSO,
})

ac.on(ACEvents.ERROR, (error: AuthClientError) => {
  console.error(error.msg, error.code, error.detail)
})

ac.on(ACEvents.INITED, ({ umsAdmin, tokenInfo, permissionList }) => {
  // 在这里初始化用户状态、权限和路由
  console.log(umsAdmin, tokenInfo, permissionList)
})

method 默认值就是 AuthMethod.SSO,可以省略。

自定义登录页

使用自定义登录页时,应先正常启动应用并展示登录页面,不要等待 inited 事件后才创建应用。inited(false) 表示 /web/login 返回空响应、当前未登录;对象结果表示恢复了已有登录状态或票据验证成功。

import {
  ACEvents,
  AuthClient,
  AuthMethod,
} from '@jxstjh/auth-client'
import type { AuthClientError } from '@jxstjh/auth-client'

const ac = new AuthClient({
  SSOServer: 'https://sso.example.com', // 替换为实际认证中心地址
  resourceCode: '由认证中心分配的资源code',
  method: AuthMethod.CUSTOM,
})

ac.on(ACEvents.INITED, (result) => {
  if (result === false) {
    // 当前未登录,继续展示自定义登录页。
    return
  }

  const { umsAdmin, tokenInfo, permissionList } = result
  // 恢复已有登录状态。
  console.log(umsAdmin, tokenInfo, permissionList)
})

ac.on(ACEvents.ERROR, (error: AuthClientError) => {
  console.error(error.msg)
})

// 此处直接启动 Vue、React 或其他前端应用,由路由决定展示登录页或首页。

账号密码登录

login() 返回 Promise<LoginResult>。接入方需要根据 result.step 展示下一步界面,并始终使用 try/catch 处理业务错误、HTTP 4xx/5xx 和网络异常。

import type { LoginOption, LoginResult } from '@jxstjh/auth-client'

// ac 是上文通过 new AuthClient(...) 创建的实例。
let credentials: LoginOption = {
  username: '',
  password: '',
}

// 以下变量仅表示接入方自己的页面状态,不是 SDK 属性。
let currentStep: LoginResult['step'] | 'idle' = 'idle'
let promptMessage = ''
let captchaImage = ''
let maskedPhone = ''
let dynamicCodeHelpUrl = ''

async function login(extra: Partial<LoginOption> = {}) {
  try {
    // SDK 方法:提交账号密码,或继续图片验证码/动态口令步骤。
    const result = await ac.login({
      ...credentials,
      ...extra,
    })
    await handleLoginStep(result)
  } catch (error: any) {
    // 后端业务提示优先读取 msg;HTTP 错误还可能包含 status。
    console.error(error?.msg || error?.message || String(error))
  }
}

async function handleLoginStep(result: LoginResult) {
  currentStep = result.step

  switch (result.step) {
    case 'success': {
      const { umsAdmin, tokenInfo, permissionList } = result.data
      // 保存用户状态并进入系统
      console.log(umsAdmin, tokenInfo, permissionList)
      break
    }

    case 'captcha':
      // 接入方操作:将图片绑定到页面的 <img>,并显示验证码输入框。
      captchaImage = result.data.img || ''
      promptMessage = result.data.message || ''
      // 用户填写后再次调用 SDK:
      // await ac.login({ ...credentials, loginCode: '用户填写的图片验证码' })
      break

    case 'dynamic_code':
      // 接入方操作:显示动态口令输入框和帮助链接。
      promptMessage = result.data.message || ''
      dynamicCodeHelpUrl = result.data.helpUrl || ''
      // 用户填写后再次调用 SDK:
      // await ac.login({ ...credentials, dynamicCode: '用户填写的动态口令' })
      break

    case 'dual_factor':
      // 临时 Token 由 SDK 内部管理,接入方不要保存或回传 Token。
      // 接入方操作:展示脱敏手机号,并显示短信验证码输入框。
      maskedPhone = result.data.phone
      promptMessage = result.data.message || ''
      // 此时 SDK 已保存双因子临时 Token,可以展示“获取验证码”按钮。
      // 用户点击获取验证码时调用 ac.sendDualFactorSmsCode();
      // 用户填写后调用 ac.loginByDualFactorSms({ smsCode })。
      break

    case 'weak_password':
      // 检测到当前密码安全强度较低,需要修改后继续登录。
      window.location.href = result.data.redirectUrl
      break
  }
}

各步骤对应的 SDK 公开 API:

| result.step | 接入方操作 | 下一次调用的 SDK API | | --- | --- | --- | | success | 保存登录结果并进入系统 | 无,登录已完成 | | captcha | 展示图片验证码输入框 | ac.login({ username, password, loginCode }) | | dynamic_code | 展示动态口令输入框 | ac.login({ username, password, dynamicCode }) | | dual_factor | 展示脱敏手机号和短信验证码输入框 | 用户点击获取验证码时调用 ac.sendDualFactorSmsCode(),然后调用 ac.loginByDualFactorSms({ smsCode }) | | weak_password | 检测到弱密码,提示用户修改后继续登录 | 跳转至 result.data.redirectUrl |

注意:

  • 密码由 SDK 内部加密,接入方传入用户输入的原始密码即可。
  • 图片验证码 ID 由 SDK 内部维护,接入方只需要再次传入 loginCode
  • 继续图片验证码或动态口令步骤时,仍需传入原来的 usernamepassword

双因子短信验证

sendDualFactorSmsCode() 依赖 SDK 内部保存的双因子临时 Token,因此只能在账号密码登录返回 dual_factor 后调用,不能在建立双因子登录状态前直接调用。

完整流程是:

login({ username, password })
→ 返回 dual_factor
→ 接入方使用 result.data.phone 展示脱敏手机号和“获取验证码”按钮
→ 用户点击按钮,调用 sendDualFactorSmsCode()
→ 接入方展示短信验证码输入框
→ 用户填写验证码
→ loginByDualFactorSms({ smsCode })
→ 返回 success

收到 dual_factor 后,接入方展示脱敏手机号和“获取验证码”按钮。用户点击按钮时调用 sendDualFactorSmsCode();用户填写验证码后调用 loginByDualFactorSms() 继续完成登录:

// 只能在 login() 返回 dual_factor 后调用:
// SDK 使用此时已保存的双因子临时 Token 发送验证码。
await ac.sendDualFactorSmsCode()

// SDK API:用户填写验证码后提交,继续完成登录。
const result = await ac.loginByDualFactorSms({
  smsCode: '用户填写的短信验证码',
})
await handleLoginStep(result)

手机号短信登录

普通手机号登录与账号密码登录的双因子步骤是两个不同流程。普通手机号登录使用 sendPhoneSmsCode(phone),不依赖 dual_factor 状态:

// SDK API:向指定手机号发送验证码。
await ac.sendPhoneSmsCode('13800000000')

// SDK API:提交手机号和短信验证码。
const result = await ac.loginByPhone({
  smsMob: '13800000000',
  smsCode: '用户填写的短信验证码',
})
await handleLoginStep(result)

if (result.step === 'dynamic_code') {
  // 接入方展示动态口令输入框;用户填写后再次调用同一个 SDK API。
  const finalResult = await ac.loginByPhone({
    smsMob: '13800000000',
    smsCode: '原短信验证码',
    dynamicCode: '用户填写的动态口令',
  })
  await handleLoginStep(finalResult)
}

完整流程:

sendPhoneSmsCode(phone)
→ 用户填写短信验证码
→ loginByPhone({ smsMob, smsCode })
   ├─ success      → 登录完成
   └─ dynamic_code
      → 接入方展示动态口令输入框
      → loginByPhone({ smsMob, smsCode, dynamicCode })
      → success

sendSmsCode(phone?)loginBySms() 为兼容已有接入保留,新代码请使用语义明确的 sendDualFactorSmsCode()loginByDualFactorSms()sendPhoneSmsCode(phone)

登录辅助信息

// SDK 方法:获取认证中心为当前资源配置的登录方式 ID。
const loginMethods = await ac.getAvailableLoginMethods()

// SDK 方法:获取认证中心配置的密码规则说明。
const passwordRule = await ac.getPasswordRule()

登录方式 ID:

  • LoginType.PASSWORD:账号密码
  • LoginType.SMS:手机号短信
  • LoginType.DINGTALK:钉钉
  • LoginType.GZT:赣政通

错误处理

请求失败时,Promise 会被拒绝。后端返回 JSON 错误体时会尽量保留原始字段,并补充 HTTP 状态:

try {
  await ac.login(credentials)
} catch (error: any) {
  console.error(error.code)
  console.error(error.msg)
  console.error(error.status)
  console.error(error.statusText)
}

全局 error 事件用于接收 SDK 初始化、配置、票据校验等错误,不能替代每个异步登录方法自身的 try/catch

退出登录

// SDK 方法:通知认证中心退出当前登录。
await ac.serverLogout()

// SDK 方法:清理当前浏览器中的用户、Token 和权限缓存。
ac.clientLogout()

站点统计工具

import { SiteAnalysis } from '@jxstjh/auth-client'

const siteAnalysis = new SiteAnalysis({
  siteAnalysisServer: 'https://analytics.example.com', // 替换为实际统计服务地址
  appId: '申请的 appId',
  interval: 30,
  production: process.env.NODE_ENV === 'production',
})

router.afterEach(() => {
  siteAnalysis.recordPage()
})

siteAnalysis.setLoginName(loginName)