@elysiumjs/themis

中文文档
0.4.4发布于 2026/11/12Apache-2.0TypeScriptES Module近 30 天下载 3135

Elysium.js框架的认证:密码,令牌,会话,守卫,CSRF,TOTP,OAuth和密码.

此软件包已被标记为弃用

has moved into @elysiumjs/olympus: import from @elysiumjs/olympus/themis; passwords, JWT and signed URLs from @elysiumjs/olympus/harpocrates.

中文使用文档
文档目录11 节
  1. 安装
  2. 设置
  3. 认证
  4. 会议
  5. 中国证监会
  6. 密码
  7. 键
  8. 使用时读取的秘密
  9. 二要素代码
  10. OAuth 和 OpenID 连接
  11. 密码

@elysiumjs/themis (英语)

认证 极乐世界 应用:密码,指使,会话,卫士,CSRF,TOTP,OAuth/OpenID Connection和密码. 它运行于邦,节点.js和Cloudflare工人.

安装

bun add @elysiumjs/themis
# Sessions in Redis or Workers KV
bun add @elysiumjs/themis-redis   # or @elysiumjs/themis-workers

设置

import { Application, Middleware } from '@elysiumjs/core';
import { Redis } from '@elysiumjs/redis';
import {
    apiKeyGuard,
    CsrfMiddleware,
    jwtGuard,
    sessionGuard,
    SessionMiddleware,
    ThemisModule
} from '@elysiumjs/themis';
import { RedisSessionStore } from '@elysiumjs/themis-redis';

@Application.register({
    modules: [
        ThemisModule.with({
            secret: env.APP_SECRET,
            session: {
                store: new RedisSessionStore({ connection: () => Redis.connection() }),
                ttl: 60 * 60 * 24 * 7
            },
            guards: {
                session: sessionGuard({ user: (id) => users.find(id) }),
                jwt: jwtGuard({ secret: env.JWT_SECRET, user: (claims) => users.find(claims.sub) }),
                apiKey: apiKeyGuard({ find: (hash) => keys.ownerOf(hash) })
            }
        }),
        AppModule
    ]
})
@Middleware.register(SessionMiddleware, CsrfMiddleware)
class App extends Application {}

secret 签名会话 cookie, 加密 cookie 会话并绑定 CSRF 令牌 。 在环境上保密。 32 字节以下的秘密在启动时被拒绝(WeakSecretError),更短的JWT和已签名的URL秘密也一样.

认证

import type { AuthUser } from '@elysiumjs/themis';

import { Http } from '@elysiumjs/core';
import { Auth } from '@elysiumjs/themis';

declare module '@elysiumjs/themis' {
    interface AuthUser {
        id: string;
    }
}

@Http.controller('/me')
@Auth()
class MeController {
    @Http.get()
    public me(@Auth.user() user: AuthUser) {
        return { id: user.id };
    }

    @Http.get('/tokens')
    @Auth({ guards: ['jwt'] })
    public tokens() {
        /* ... */
    }
}
  • @Auth() 按顺序尝试配置的守护者 @Auth({ guards }) 只有这些名字 守护是执行 Guard,或者一个警卫班,这个班次通过服务集装箱建造一次,以便其建造者得到注射服务。 登记 AuthMiddleware这样 @Middleware.register(AuthMiddleware.guards(['jwt'])) 与 @Auth({ guards: ['jwt'] })。 。 。 .
  • 一个要求没有警卫接受一个401问题的答案. @Auth({ optional: true }) 让它通过,并 Auth.check() 告诉用户是否被签名 .
  • 用户可通过 @Auth.user() 在一个处理器参数上, Auth.currentUser() 财务报告和审定财务报表 ctx.currentUser这样cerberus就可以读它与 getSubject: (ctx) => ctx.currentUser。通过增加 AuthUser 接口。 后面,我们走 @Auth({ optional: true }),键入参数 AuthUser | undefined。 。 。 .
  • Auth.login(id) 在会话上签名一个用户(并重新生成) Auth.logout() 把他们签出来.

会议

import { Session } from '@elysiumjs/themis';

const session = Session.current();
session.set('cart', items);
session.flash('notice', 'Saved');
session.regenerate();
  • 这些商店是 MemorySessionStore, (中文(简体) ), RedisSessionStore 联 合 国 @elysiumjs/themis-redis 财务报告和审定财务报表 KvSessionStore 联 合 国 @elysiumjs/themis-workers (a) 工人KV装订)。 没有商店,会话就活在过程的记忆中,他们就会在外部测试中警告.
  • store: 'cookie' 将整个会话保存在 AES- GCM 加密饼干中 。 它必须保持在4000个字符以下(SessionTooLargeError),而所复制的饼干在过期前仍然有效,所以在商店中保留可撤销的会话.
  • 会话饼干已经签了, HttpOnly, (中文(简体) ), SameSite=Lax 财务报告和审定财务报表 Secure 默认.
  • 处理器运行后会话被保存,当请求以客户端出错(4xx)而结束时也会被保存,例如签名后出现抛出的问题.

中国证监会

CsrfMiddleware 以会话中保存的同步令牌保护 cookie 会话 。 不安全的方法需要 x-csrf-token 页眉或 _csrf 要匹配的字段 Csrf.token()或者他们回答一个403个问题.

密码

import { Password } from '@elysiumjs/themis';

const hash = await Password.hash(input);
if ((await Password.verify(input, hash)) && Password.needsRehash(hash)) {
    await users.updateHash(id, await Password.hash(input));
}

散热器为rg2id(BunPasswordHasher) Bun和PBKDF2-SHA256号机上安装的Pbkdf2PasswordHasher在其他地方,有60万次重复(在Cloudflare Workers上为100,000次,在此处为PBKDF2上限). ScryptPasswordHasher从 @elysiumjs/themis/scrypt, 用途 node:crypto选择一个 Password.use(hasher)Hashes描述它们的格式,所以 verify 读取所有三个.

键

import { ApiKeys, Jwt, SignedUrl } from '@elysiumjs/themis';

const token = await Jwt.sign({ role: 'admin' }, { secret, subject: user.id, expiresIn: 3600 });
const claims = await Jwt.verify(token, { secret });

const { key, hash } = await ApiKeys.generate({ prefix: 'sk' }); // store the hash, show the key once
await ApiKeys.verify(key, hash);

const link = await SignedUrl.sign('https://app.example.com/download/1', { secret, expiresIn: 600 });
await SignedUrl.verify(link, { secret });

Jwt 带有密钥(HS256)或不对称密钥及其算法的符号。 无效或过期的符是401个问题,无效或过期的链接是403个问题.

Jwt.verify 未过期即拒签; 通过 requiredClaims (a) 更改必须携带的凭证([] 接受一个没有 exp) (中文(简体) ). currentDate 对照另一时间检查过期日期 issuedAt 从另一个时间开始.

使用时读取的秘密

每一个秘密 ThemisModule 秘密,JWT秘密和已签名的 URL 秘密)可以是函数,同步或async,每次需要该秘密时都会被调用. 从一个与请求相联的商店读取,比如Cloudflare Workers上绑有的秘密,一个旋转的秘密会被取出而不重启. 每个读数都检查32字节的最小值.

ThemisModule.with({ secret: () => secrets.get('app-secret') });

在旋转时,用当前键,然后用上一个键进行校验:

const claims = await Jwt.verify(token, {
    keys: [
        { secret: () => secrets.get('jwt-current') },
        { secret: () => secrets.get('jwt-previous') }
    ]
});

密钥按顺序进行,直到有人验证了签名;过期的令牌或错误的主张立即被拒绝.

二要素代码

import { BackupCodes, Totp } from '@elysiumjs/themis';

const secret = Totp.generateSecret();
const qr = Totp.uri({ secret, issuer: 'Acme', account: user.email });
const counter = await Totp.verify(code, secret, { window: 1 }); // null when wrong

const { codes, hashes } = await BackupCodes.generate(10);
const { valid, remaining } = await BackupCodes.consume(typed, hashes);

存储柜台 Totp.verify 返回并拒绝一个计数器没有更大的代码,这样一个代码就一次起作用.

普通的散列车让任何有表格测试副本的人猜测下线. 传递一个用 HMAC 来散开代码的密钥, 以及使用这些密钥 :

const { codes, hashes } = await BackupCodes.generate(8, {
    secret: () => secrets.get('backup-codes')
});
const { valid, used } = await BackupCodes.consume(typed, hashes, {
    secret: () => secrets.get('backup-codes')
});

used 是匹配的散列代码,以标记所使用的代码.

OAuth 和 OpenID 连接

import { OAuthClient } from '@elysiumjs/themis';

const google = new OAuthClient({
    issuer: 'https://accounts.google.com',
    clientId: env.GOOGLE_CLIENT_ID,
    clientSecret: env.GOOGLE_CLIENT_SECRET,
    redirectUri: 'https://api.example.com/auth/google/callback'
});

// GET /auth/google
const url = await google.authorize();

// GET /auth/google/callback
const { tokens, claims, userInfo } = await google.callback(request.url);

客户端使用PKCE的授权代码流. 状态,验证器和OpenID Connectnce保存在会话中. 一个没有发现的提供者需要 authorizationServer 和它的终点。 错误状态或交换失败的回调回答400个问题. 它的细节名称是 OAuth 错误代码, 提供者回答; 其它的失败都会被记录下来, 用固定的细节回答 .

密码

import { Passkeys } from '@elysiumjs/themis';

const passkeys = new Passkeys({
    rpName: 'Acme',
    rpID: 'example.com',
    origin: 'https://example.com',
    repository: new PasskeyTable()
});

const options = await passkeys.registrationOptions({ id: user.id, name: user.email });
const record = await passkeys.verifyRegistration(response);

const challenge = await passkeys.authenticationOptions();
const used = await passkeys.verifyAuthentication(assertion); // used.userId signs the user in

本届会议继续面临挑战。 应用程序通过执行存储证书 PasskeyRepository。无法验证400个问题的答案. updateCounter 获取上一个计数器: 答案 false 当存储的不再是它, 而输掉比赛的签入被拒绝.

一个在两个调用之间保持自身状态的应用程序,例如多步流,或者一个没有会话饼干的客户端,设置 challenges: 'caller' 并传递挑战回:

const passkeys = new Passkeys({ ...options, challenges: 'caller' });

const request = await passkeys.authenticationOptions({ id: user.id });
// keep request.challenge with the flow, then:
const used = await passkeys.verifyAuthentication(assertion, { challenge, userId: user.id });

residentKey: 'required' 使每一个新的密码都可以发现, 对于一个没有用户名的登录 timeout (以毫秒计)是客户端需要回答多久. 一个用户处理用户名而不是密码的断言被拒绝. 与一个 userId,另一个用户的密码像一个未知的一样被拒绝。 登记需要: userId 这是为.

版本与发布记录

@elysiumjs/themis 共发布 17 个版本,首次发布于 2026/11/07;过去 12 个月发布了 17 个版本。

版本发布日期距今
0.4.4latest2026/11/122 天前
0.4.32026/11/112 天前
0.4.22026/11/112 天前
0.4.12026/11/112 天前
0.4.02026/11/103 天前
0.3.62026/11/094 天前

关于 @elysiumjs/themis 的常见问题

@elysiumjs/themis 是什么?

Elysium.js框架的认证:密码,令牌,会话,守卫,CSRF,TOTP,OAuth和密码.注意:该包已被作者标记为弃用——has moved into @elysiumjs/olympus: import from @elysiumjs/olympus/themis; passwords, JWT and signed URLs from @elysiumjs/olympus/harpocrates.

如何安装 @elysiumjs/themis?

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

@elysiumjs/themis 支持 TypeScript 吗?

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

@elysiumjs/themis 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@elysiumjs/themis 可以商用吗?

@elysiumjs/themis 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@elysiumjs/themis 的下载量有多少?

根据 npm 官方下载统计,@elysiumjs/themis 在 2026/10/14 – 2026/11/12 这 30 天内被下载 3,135 次,日均约 105 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@elysiumjs/themis 还在维护吗?

最新版本 0.4.4 发布于 2026/11/12(2 天前);至今共发布 17 个版本,其中过去 12 个月发布了 17 个;首次发布于 2026/11/07。作者已将其标记为弃用,新项目建议寻找替代方案。