@ effect/ oidc
OpenID Connect for Effect v4:对照其发行人所发现的密钥验证一个ID令牌,读取标准ID-token的诉求,并通过任何运行时间都可以服务的一个小合同请求一个工作量身份令牌. 它运行任何地方WebCrypto做, 包括Cloudflare工人,没有 node: 导入.
预
1.0.0。 。 。 . 此软件包是@effected/*工具箱,在 稳定效果 v4并仍在0.x发展。 为了保持你的effect财务报告和审定财务报表@effect/*该工具包在线上构建和测试、安装@effected/pnpm-plugin-effected。 。 。 .稳定:不稳定. 此软件包的 API 表面尚未考虑 完成并可能改变
0.x释放 准确的版本。 满 政策: 释放策略。 。 。 .
为什么 @ e受影响/ idc
校验一个OIDC ID标志是一种JWT的校验,有三种规则容易出错:密钥出自发行商的发现文件,你信任的发行商是你配置的而不是象征性的声称,观众必须总是被校验,因为发行商是每个依赖方的一键设置标志标志. Oidc.verify 持有全部三: 它只为配置的发行者查找密钥, 它在提出任何请求之前拒绝一个空的听众 .
核心的签名和索赔核对 effect/jose,通过 @effected/jwt发现支持 JwksResolver如果您持有发行人的密钥, 或者它的 JWKS URL 固定在您的配置中, 核心 effect/jose (单位:千美元)Jwt.verify 与 Jwks.layerRemote)在没有此包的情况下进行工作。 到达 Oidc.verify 在发现后找到密钥时 @effected/jwt更安全的默认值( 60 秒时钟 skew, exp ),或当您想要时 IdTokenClaims 编辑 IdentityToken 合同.
对于 GitHub 动作 OIDC 令牌, 使用 ActionsOidc.verify 从 @effected/github,这是 Oidc.verify 被绑在GitHub的发行人身上,有打字的权利要求.
安装
npm install @effected/oidc effect
pnpm add @effected/oidc effect
在节点上,它需要Node.js QQ24.11.0;它还运行在任何其他WebCrypto运行时间上,Cloudflare Workers也包括在内. effect v4是唯一的对等者,和 @effected/jwt 带着它 导入 JwksResolver 财务报告和审定财务报表 JwksStore 从 @effected/jwt全部数据 @effected/* 软件包仅限无害环境管理.
提供什么
| 运行 | 要求数 |
|---|---|
Oidc.verify | JwksResolver (从) @effected/jwt和 Signature.Signature (从) effect/crypto(中文(简体) ) |
Oidc.decodeUnverified | 无 |
使用 IdentityToken | a 一个 IdentityToken 层 (下方) |
| 服务 | 任何 WebCrypto 运行时间( 包括工人) | 节点 |
|---|---|---|
Signature.Signature | Signature.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto)) | NodeCrypto.layerSignature 从 @effect/platform-node |
JwksResolver | JwksResolver.layer,超过一个 HttpClient (单位:千美元)FetchHttpClient.layer)和a 数据 JwksStore (单位:千美元)JwksStore.layerMemory,或KV后置商店) | 相同,或 NodeHttpClient.layerUndici 为客户服务 |
IdentityToken | IdentityToken.layerStatic(token); 在 GitHub 动作中, ActionsIdentityToken.layer 从 @effected/github-actions | 同样的 |
import { JwksResolver, JwksStore } from "@effected/jwt";
import { Layer } from "effect";
import { SecureRandom, Signature } from "effect/crypto";
import { FetchHttpClient } from "effect/http";
const OidcLive = Layer.merge(
JwksResolver.layer.pipe(Layer.provide(Layer.merge(FetchHttpClient.layer, JwksStore.layerMemory))),
Signature.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto)),
);
验证标识符
import { IdTokenClaims, Oidc } from "@effected/oidc";
const verifyIdToken = (token: string) =>
Oidc.verify(token, {
issuer: "https://accounts.example.com",
audience: "my-client-id",
claims: IdTokenClaims,
});
// Effect<IdTokenClaims, JwtError, JwksResolver | Signature>
键是从 <issuer>/.well-known/openid-configuration,而发现文件必须准确命名该发行人。 签名就来了, exp (必须), iss (必须等于 issuer, (中文(简体) ), aud (必须包含其中之一) audience,可能是一个清单, nbf、 claims 计划,和 iat 未来没有,60秒的时钟允许(clockTolerance 改变它.
身份证上的说法
IdTokenClaims 已登记的JWT索赔(iss, (中文(简体) ), sub, (中文(简体) ), aud, (中文(简体) ), exp, (中文(简体) ), nbf, (中文(简体) ), iat, (中文(简体) ), jti)加 OpenID 连接 ID - token 索赔 azp, (中文(简体) ), nonce, (中文(简体) ), auth_time, (中文(简体) ), acr 财务报告和审定财务报表 amr。每个权利要求都是可选的,因此它解码了任何提供者的代号,而计划是开放的,因此权利要求它没有名字在解码中存活下来。 方案中的可选性并不意味着可选性: Oidc.verify 仍然需要 exp 检查 iss 财务报告和审定财务报表 aud。 。 。 .
供货商自己的主张属于你自己的计划,建立在 IdTokenClaims字段 :
import { IdTokenClaims, Oidc } from "@effected/oidc";
import { Schema } from "effect";
const GoogleClaims = Schema.StructWithRest(
Schema.Struct({
...IdTokenClaims.schema.fields,
email: Schema.String,
email_verified: Schema.Boolean,
}),
[Schema.Record(Schema.String, Schema.Unknown)],
);
const verifyGoogle = (token: string) =>
Oidc.verify(token, { issuer: "https://accounts.google.com", audience: "my-client-id", claims: GoogleClaims });
Oidc.decodeUnverified(token, claims) 用计程器解码一个符号的有效载荷 无需验证任何东西用于读取一个令牌, 您的进程是用它已经信任的运行时间传递的, 而不是授权请求 .
这个 IdentityToken 合同
IdentityToken 是另一个方向: 一个需要 当前 工作量身份(用来交换签名证书,例如)要求给一个观众一个符号,运行时间决定如何获得.
import { IdentityToken } from "@effected/oidc";
import { Effect, Redacted } from "effect";
const presentIdentity = Effect.gen(function* () {
const identity = yield* IdentityToken;
const token = yield* identity.token("https://my-service.example");
return Redacted.value(token);
});
// A token you already hold. The audience is ignored: mint it for the audience it will be used with.
const program = presentIdentity.pipe(Effect.provide(IdentityToken.layerStatic(process.env.OIDC_TOKEN ?? "")));
在 GitHub 动作中,提供 ActionsIdentityToken.layer 从 @effected/github-actions,它问 跑者的信使服务. IdentityToken 本身只进口 effect,因此,一个只需要合同链接到JWT代码的软件包.
错误
Oidc.verify 财务报告和审定财务报表 Oidc.decodeUnverified 失败 @effected/jwt是因为 JwtError 并且不要增加他们自己的错误。 运行路线 reason数字 :
reason | 含义 |
|---|---|
wrongAudience | aud 包含没有被接受的受众,或者缺少;以及空的 audience 在任何请求之前被拒绝. |
wrongIssuer | iss 缺少或不是已配置的发行者. |
expired 编号 : notYetValid | exp 已经过去,或 nbf 或者说 iat 未来 超越时钟的容忍度. |
claims | exp 缺少, 注册的索偿类型有错误, 或者您 claims schema拒绝了有效载荷. |
badSignature | 签名不能与发行人的密钥核对. |
unknownKid | 发行商的密钥集没有用于令牌算法和 kid即使经过一次反弹. |
jwksFetch | 无法获取发现文件或密钥集, 不是 https:,或指定另一个发行人. |
malformed | 并非带有JSON物体有效载荷的紧凑JWS,或 alg: "none"。 。 。 . |
unsupportedAlgorithm 编号 : algorithmMismatch 编号 : key | HMAC算法,一个 alg 这不是密钥,也不是不导入的已公布密钥. |
IdentityToken.token 失败 IdentityTokenError,带有 audience 请求的标志和基础 cause。 。 。 .
测试
IdentityToken.layerTest() 用固定的测试符回答每个请求; 通过 { token } 以不同的回答来回答。 测试 Oidc.verify,用 TestIssuer 从 @effected/jwt/testing ,并提供其 resolverLayer真正的解决者 在一个内幕发行者:
import { TestIssuer } from "@effected/jwt/testing";
import { IdTokenClaims, Oidc } from "@effected/oidc";
import { Effect } from "effect";
const test = Effect.gen(function* () {
const issuer = yield* TestIssuer.make({ issuer: "https://accounts.example.com" });
const token = yield* issuer.sign({ aud: "my-client-id", sub: "user-1" });
return yield* Oidc.verify(token, {
issuer: "https://accounts.example.com",
audience: "my-client-id",
claims: IdTokenClaims,
}).pipe(Effect.provide(issuer.resolverLayer));
});
// provide Signature (see "What to provide") around the test