@ factorialco/ auth (帮助)
校验阶乘代号, 并请求 OAuth 从它的标志端点获得赠款 .
库-不可知:它只取决于 jose 和全球 fetch, (中文(简体) ),
所以它继续运行 节点.js (>= 22.14) 继续 边缘/ 无服务器 运行时间.
- 通过 OIDC 发现 (作相克相克相克相克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克.
- 验证符(默认为ES256): 签名,
iss, (中文(简体) ),aud, (中文(简体) ),exp, (中文(简体) ),nbf。 。 。 . - 自动调试 JWKS 键旋转 (不详)
kid) (中文(简体) ). - 回返 严格、打字、不可改变 权利主张对象.
- 请求平台,授权,并通过已发现的终点刷新OAuth标志.
- 船舶演员身份值对象:
ActorRef, (中文(简体) ),ActorType, (中文(简体) ),IdentityChain, (中文(简体) ),ActType。 。 。 . - 提供框架不可知性
extractBearerToken帮手.
安装
pnpm add @factorialco/auth
# or: npm install / yarn add / bun add
快开始
import { FactorialAuth } from '@factorialco/auth'
const auth = new FactorialAuth({
oidcDiscoveryUrl: 'https://id.factorialhr.com/.well-known/openid-configuration',
audience: 'your-client-id',
})
// Decode and verify an access token
const accessTokenClaim = await auth.decodeAccessToken(token)
console.log(accessTokenClaim.sub, accessTokenClaim.cid)
// Decode and verify an id token
const idTokenClaim = await auth.decodeIdToken(token)
console.log(idTokenClaim.sub, idTokenClaim.email)
建筑费用低廉、同步 解码或代号客户呼叫。 发现和JWKS文件被懒散地取出 然后缓存( 见 缓存密钥旋转( K)) (中文(简体) ).
OAuth 令牌客户端
配置相同的客户端证书 FactorialAuth 举个例子 全权证书
仅当一个符号客户端操作被称为:
const auth = new FactorialAuth({
oidcDiscoveryUrl: process.env.FACTORIAL_OIDC_DISCOVERY_URL!,
audience: 'factorial',
clientId: process.env.FACTORIAL_OAUTH_CLIENT_ID,
clientSecret: process.env.FACTORIAL_OAUTH_CLIENT_SECRET,
})
const platform = await auth.tokenClient.platformToken({
audience: 'factorial-backend',
cell: 'eu1',
})
const delegated = await auth.tokenClient.delegatedToken({
subjectToken,
audience: 'factorial-backend',
})
const refreshed = await auth.tokenClient.refreshToken(refreshToken)
信使客户端解析 OAuth 响应, 但不会解码、 校验、 坚持, 自动刷新或撤销退回的赠款。 申请保留所有权 这些生命周期和授权决定.
当符号端点以 OAuth 错误回答时, 抛出
TokenRequestError 曝光 HTTP status 和奥地利 error 代码为
oauthError,所以呼叫者可以在不解析消息的情况下进行分支. 从
符号终点从未被遵循.
演员身份原始人
ActorRef 财务报告和审定财务报表 IdentityChain 为小值对象
携带经认证的演员身份跨越应用程序界限。 他们是
认证原始, 不授权背景和事件传输
对象.
演员参考
ActorRef 这是对一个具体行为者的不可改变的提法.
它回答:"这是哪个演员?"
import { ActorRef } from '@factorialco/auth'
ActorRef.employee('123')
ActorRef.company('42')
ActorRef.system('domain-events-audit-log-creator')
一名演员将序列化为类似URI的字符串:
f:act:employee:123
使用一个 ActorRef 当代码需要紧凑,稳定的演员标识符时,不需要
装入模型、携带许可或编码租户范围。 号码是
总是一个字符串,必须不透明: 永远不要放邮件,姓名, API 密钥, 符号,
IP,或其中的其他敏感值.
身份号码
IdentityChain 是一个递归链 演员参考.
它回答:"谁是有效的认证演员,谁在演 "通过谁?"
链根是当前行动的有效行为者. 如果动作
发生在一个变相/动作流, act 指向演员链
发起,并 actType 描述这两个节点之间的关系.
import { ActorRef, ActType, IdentityChain } from '@factorialco/auth'
const employee = ActorRef.employee('123')
const admin = ActorRef.employee('999')
const identityChain = IdentityChain.create({
actor: employee,
act: IdentityChain.create({ actor: admin }),
actType: ActType.AdminBecome,
})
identityChain.actor // employee
identityChain.act?.actor // admin
串行形状为串键并递归(线形使用:
蛇脚( c) act_type:
identityChain.toObject()
// {
// actor: "f:act:employee:123",
// act: { actor: "f:act:employee:999" },
// act_type: "admin_become",
// }
使用 IdentityChain.parse 仅用于信任的服务器侧元数据.
取消序列化默认在3个节点封顶,以保持解析保守; 2;
通过 maxDepth 改为:
IdentityChain.parse(serializedObject)
IdentityChain.parse(serializedObject, { maxDepth: 5 })
关系如何
ActorRef 是叶值。 它能识别一个演员 而不是别的.
IdentityChain 编剧将角色重置为认证的关系图.
保持了角色参考的稳定性和简洁性,同时保留了成/行
明确上下文。 不将冒用链条编码为演员参考字符串;
将演员推为主题并使用 IdentityChain.act 加号
IdentityChain.actType 为了关系.
例如,成为雇员的管理人员有:
employee actor ref -> act(admin actor ref), actType: admin_become
这样就可以让应用程序代码为自己的合同选择正确的投影.
绝不接受客户端提供的演员参考或身份链作为权威 写作。 从已认证的服务器侧上下文、 证书来构建这些对象 , 或已知的系统呼叫者。 演员代表是演员,不是房客 审计和领域活动流动必须分别包含公司或租户的范围.
处理错误
每一次校验/解码失败都投出一个子类: AuthError执行
AuthError 用来处理所有的, 或者一个特定的子类 更细的控制。 这个
tryDecode* 方法吞进 AuthError 返回时 null; 任何其他( 意外)
错误传播 .
Error
├─ AuthError // base for everything this library throws
│ ├─ ConfigurationError // invalid FactorialAuthConfig (thrown by the constructor)
│ ├─ OidcDiscoveryFetchError // discovery endpoint unreachable / non-2xx / timeout
│ ├─ OidcDiscoveryParseError // discovery document invalid JSON or missing issuer/jwks_uri
│ ├─ TokenRequestError // OAuth token endpoint request failed
│ ├─ TokenResponseParseError // OAuth token response malformed or unusable
│ ├─ JwksFetchError // JWKS endpoint unreachable / non-2xx / timeout
│ ├─ JwksParseError // JWKS invalid JSON or malformed key set
│ └─ TokenError // base for token-level failures
│ ├─ InvalidToken // bad signature, malformed token, unknown kid, or bad claim shape
│ ├─ ExpiredToken // exp check failed
│ ├─ InvalidIssuer // iss did not match the discovery document
│ ├─ InvalidAudience // aud did not match config.audience
│ └─ ImmatureToken // nbf check failed (token used before it is valid)
├─ ActorRefError // malformed ActorRef string/id (NOT an AuthError)
└─ IdentityChainError // malformed identity chain (NOT an AuthError)
说明:
ActorRefError财务报告和审定财务报表IdentityChainError扩展Error直接, 没有AuthError,并且不受tryDecode*。 。 。 .
import { FactorialAuth, ExpiredToken, TokenError, OidcDiscoveryFetchError } from '@factorialco/auth'
try {
const claims = await auth.decodeAccessToken(token)
} catch (error) {
if (error instanceof ExpiredToken) {
// prompt a refresh
} else if (error instanceof TokenError) {
// any other invalid-token case -> 401
} else if (error instanceof OidcDiscoveryFetchError) {
// upstream/infra problem -> 503
} else {
throw error
}
}
缓存密钥旋转( K)
- OIDC的发现文件和JWKS在内存中被缓存为双TTL: 新鲜10分钟,然后服务 暂停1小时以下 如果反切 失败(所以瞬间发现/JWKS出局不会破坏验证).
- 同时进行的首次核查 单人 获取和获取 单人 JWKS接取(飞行中的请求被去复制).
- 当一个符号带有一个
kid在缓存的 JWKS(键旋转)中 JWKS 已调试 一次 和仰视重试。 如果kid仍然是 未知,该符号以InvalidToken。 。 。 .
因为缓存住在实例上,创建 一个 FactorialAuth 并重新使用它
您的流程/工作寿命, 而不是构建一个请求.
一体化模式
服务器请求处理器( Express- style)
import { FactorialAuth, extractBearerToken } from '@factorialco/auth'
const auth = new FactorialAuth({
oidcDiscoveryUrl: process.env.FACTORIAL_OIDC_DISCOVERY_URL!,
audience: process.env.FACTORIAL_AUDIENCE!,
})
app.use(async (req, res, next) => {
const token = extractBearerToken(req.headers.authorization ?? null)
if (token === null) {
return res.status(401).json({ error: 'missing bearer token' })
}
const claims = await auth.tryDecodeAccessToken(token)
if (claims === null) {
return res.status(401).json({ error: 'invalid token' })
}
req.claims = claims
next()
})
边缘/ 网络 Request
const token = extractBearerToken(request.headers.get('authorization'))
const claims = token ? await auth.tryDecodeAccessToken(token) : null
A. 从访问控制索赔中确认的身份
Access-token 索赔使用
同一个员工,平台主,成为, 和授权规则与鲁比
factorial-auth 软件包 :
const claims = await auth.decodeAccessToken(token)
const actor = claims.actorRef
const chain = claims.identityChain()
identityChain() 掷出 IdentityChainError 当 act 链条更深
大于允许的最大值或携带未知的 bt 值——甚至一个符号
解码成功.
兼容性
- 运行时间 : 节点.js QQ 22.14, 以及任何带有全局的边缘/服务器运行时间
fetch和网络加密(Cloudflare Workers, Vercel Edge, Deno, Bun). - 模块 : 双重无害环境管理(
import)和CJS(require(b) 有捆绑的切入点.d.ts。 。 。 .
捐款
见 捐款额 用于本地设置和发展命令.