类型脚本的 Finueva Auth SDK
使用 @finueva/auth 将 Finueva 签名、组织访问、邀请和 Drive 证书添加到浏览器、服务器、 CLI 或本地应用程序.
目前公布的版本是 0.11.1. 一揽子措施仅限无害环境管理。 节点.js消费者需要节点.js 22或后. 其他服务器运行时间必须提供相容的Fetch,Web Crypto,URL,以及Web Streams API. 它的界面是实验直到版本 1.0.0。 。 。 .
下面的平台状态,组织授权,以及邀请管理方法描述了这个寄存器的下一个软件包候选人. 已出版版本 0.11.1 不包括他们.
安装
pnpm add @finueva/auth
对于浏览器或服务器应用程序,请询问您的 Auth 操作员:
- 环境的认证源;
- 您的注册申请来源.
对于CLI或本地申请,要求:
- 环境的认证源;
- 注册设备客户端ID.
不从另一个应用程序或环境进行再利用配置.
选择客户端
| 运行时间 | 导入 | 会议授权 | 创建它 |
|---|---|---|---|
| 浏览器 | @finueva/auth | Auth HTTP 纯饼干 | 申请一次 |
| 服务器或 SSR 处理器 | @finueva/auth/server | 收到 Auth 饼干或手持者 | 每个请求一次 |
| CLI 或本地应用 | @finueva/auth/device | 设备无记名会话 | 每个进程一次 |
个人身份解决候选人
对于私有服务器到服务器的身份解析, 下一个软件包候选导出 createIdentityResolverAuth 从 @finueva/auth/server。此助手未发布 0.15.0。 。 。 .
import { createIdentityResolverAuth } from "@finueva/auth/server";
const identities = createIdentityResolverAuth({ authOrigin: "https://auth.example.test" });
async function resolvePrivateIdentity(identifier: string, clientAssertion: string) {
return identities.resolveIdentity({ identifier }, clientAssertion);
}
经批准的服务器用其注册的密钥和明确字签出新的断言 identities:resolve 能力。 只返回方法 { userId } 或对于一个准确的用户名或电子邮件无效。 把结果保密 它不授予产品授权。 见 服务器指南 合同和限制.
浏览器快速启动
以精确的 Auth 和应用源创建客户端 :
import { AuthError, createAuthClient } from "@finueva/auth";
const auth = createAuthClient({
authOrigin: "https://auth.finueva.com",
appOrigin: window.location.origin,
});
阅读当前会话 :
const session = await auth.getSession();
if (session) {
console.log(`Signed in as ${session.user.email}`);
}
用电子邮件和密码签名 :
try {
const session = await auth.signIn({
email: "[email protected]",
password: "correct-horse-battery-staple",
});
console.log(`Welcome ${session.user.name}`);
} catch (error) {
if (error instanceof AuthError && error.code === "invalid_credentials") {
showInvalidCredentials();
} else {
throw error;
}
}
浏览器拥有会话饼干. SDK没有暴露或坚持.
共同任务
| 任务 | 浏览器 | 服务器 | 设备 |
|---|---|---|---|
| 创建账户 | signUp() | 不详 | signUpWithEmail() |
| 读取当前会话 | getSession() | getSession() | getSession() |
| 观察会话状态 | onAuthStateChange() | 不详 | 不详 |
| 更新配置文件 | updateProfile() | 不详 | updateProfile() |
| 更改密码 | changePassword() | 不详 | changePassword() |
| 完成第二个因素的登录 | verifyTwoFactor() | 不详 | 设备授权 |
| 管理两步核查 | startTwoFactorEnrollment(), (中文(简体) ), confirmTwoFactorEnrollment(), (中文(简体) ), disableTwoFactor(), (中文(简体) ), regenerateTwoFactorBackupCodes() | 不详 | 不详 |
| 读取双步验证状态 | getTwoFactorStatus() | 不详 | getTwoFactorStatus() |
| 重置密码 | requestPasswordReset(), (中文(简体) ), resetPassword() | 不详 | 不详 |
| 校验邮件 | sendVerificationEmail() | 不详 | 不详 |
| 名单组织 | listOrganizations() | listOrganizations() | listOrganizations() |
| 阅读成员权限 | getOrganizationMemberAuthority() | getOrganizationMemberAuthority() | getOrganizationMemberAuthority() |
| 创建普通组织 | createOrganization() | createOrganization() | createOrganization() |
| 创建普通团队 | createOrganizationTeam() | createOrganizationTeam() | createOrganizationTeam() |
| 邀请普通成员 | createOrganizationMemberInvitation() | createOrganizationMemberInvitation() | createOrganizationMemberInvitation() |
| 重新发送成员邀请 | resendOrganizationInvitation() | resendOrganizationInvitation() (饼干) | 不详 |
| 取消成员邀请 | cancelOrganizationInvitation() | cancelOrganizationInvitation() (饼干) | 不详 |
| 删除普通成员 | removeOrganizationMember() | removeOrganizationMember() (饼干) | 不详 |
| 离开一个组织 | leaveOrganization() | leaveOrganization() (饼干) | 不详 |
| 在团队中添加成员 | addOrganizationTeamMember() | addOrganizationTeamMember() (饼干) | 不详 |
| 从团队中删除一个成员 | removeOrganizationTeamMember() | removeOrganizationTeamMember() (饼干) | 不详 |
| 删除普通团队 | deleteOrganizationTeam() | deleteOrganizationTeam() (饼干) | 不详 |
| 接受邀请 | acceptInvitation() | acceptInvitation() | 不详 |
| 拒绝邀请 | declineInvitation() | declineInvitation() | 不详 |
| 输入密码 | addPasskey() | 不详 | 不详 |
| 执行最近的认证 | 平台和组织行动方法 | 不详 | 不详 |
| 改变手动角色或所有者 | 组织授权方法 | 不详 | 不详 |
| 读取当前平台角色 | getPlatformStatus() | 不详 | 不详 |
| 请求驱动器访问 | getDriveCredential() | getDriveCredential() | getDriveCredential() |
| 签名会议列表 | listSessions() | 不详 | listSessions() |
| 其他会议结束 | revokeSession(), (中文(简体) ), revokeOtherSessions() | 不详 | revokeSession(), (中文(简体) ), revokeOtherSessions() |
| 启动奥特-奥里金仪式 | createCeremonyHandoff() | 不详 | createCeremonyHandoff() |
| 列出并重命名密码 | listPasskeys(), (中文(简体) ), renamePasskey() | 不详 | listPasskeys(), (中文(简体) ), renamePasskey() |
| 结束本届会议 | signOut() | signOut() | revokeCurrentDevice() |
创建组织邀请
对您要创建的每份邀请使用一款新的自定义键 :
const invitation = await auth.createOrganizationMemberInvitation("org_01J8R7M6N5P4Q3S2T1", {
email: "[email protected]",
teamIds: ["team_01J8R7M6N5P4Q3S2T1"],
idempotencyKey: crypto.randomUUID(),
});
console.log(invitation.id, invitation.expiresAt);
如果回复丢失,请用相同的密钥重复同样的请求. SDK 不自动重试 。 结果证实了邀请的创建。 它不能确认电子邮件发送.
手动所有者和手动管理员可以通过浏览器-cookie或服务器-cookie权限重新发送或取消准确的待决邀请 :
const resent = await auth.resendOrganizationInvitation(organizationId, invitation.id, crypto.randomUUID());
const canceled = await auth.cancelOrganizationInvitation(organizationId, invitation.id, crypto.randomUUID());
重新发送成功确认承诺的过期 。 它不能确认电子邮件发送。 仅在回复失败后再用相同的密钥重复一次动作 。 一个无记名支持的服务器客户端在网络一/O之前拒绝两种方法.
删除组织成员
手动拥有者和手动管理员除去另一个成员. 每个现任成员都可以离开. 两种操作都使用浏览器-cookie或服务器-cookie权限:
const removed = await auth.removeOrganizationMember(organizationId, memberId, crypto.randomUUID());
const left = await auth.leaveOrganization(organizationId, crypto.randomUUID());
房主的目标被拒绝了 先转让所有权. 只有在回复失败后,才用同样的密钥重复删除;已经属于该密钥之下的成员重播同样的结果. 一个无记名支持的服务器客户端在网络一/O之前拒绝两种方法.
管理小组成员
手动所有者和手动管理员将一个目前的组织成员添加到普通团队中,将一个成员从团队中除名,或者删除一个普通团队. 三种操作使用浏览器-cookie或服务器-cookie权限:
await auth.addOrganizationTeamMember(organizationId, teamId, memberId, crypto.randomUUID());
await auth.removeOrganizationTeamMember(organizationId, teamId, memberId, crypto.randomUUID());
await auth.deleteOrganizationTeam(organizationId, teamId, crypto.randomUUID());
Auth保留了最后一个活跃的球队,呼叫者选中的球队,以及一个等待邀请的球队目标. 重复使用同一密钥的操作,仅在失去响应后进行. 一个无记名支持的服务器客户端在网络一/O之前拒绝三种方法. 受邀请者拒绝邀请 declineInvitation({ invitationId })。 。 。 .
执行最近的认证
实验性的密码方法只在浏览器页面使用精确的Auth Origin时运行. 要录入密码, 请先创建关于此源的密码认证会议 :
await auth.signIn({ email, password });
await auth.addPasskey();
入学许可在五分钟后到期。 这是单用。 注册、社会注册、会话刷新、无记名使用和通用会话创建不授权注册。 没有新密码的用户不能使用此路径 .
在使用密码认证之前
await auth.startPlatformRoleChangeRecentAuthentication({
targetUserId: "user_01J8R7M6N5P4Q3S2T1",
requestedRoles: ["administrator", "investigator"],
expectedTargetRevision: 0,
expectedGlobalRevision: 0,
reasonCategory: "initial_bootstrap",
changeReference: "change_01J8R7M6N5P4Q3S2T1",
idempotencyKey: crypto.randomUUID(),
});
await auth.verifyRecentAuthenticationWithPasskey();
const continuation = await auth.getRecentAuthenticationStatus();
if (continuation.status === "ready") {
showReviewedActionIsReady();
}
维护的密码客户端在兼容认证器出现时为待决动作进行WebAuthn仪式. Auth对这项行动提出了确切的挑战。 SDK 仅返回 pending 或者说 ready。它不会暴露挑战、WebAuthn响应、会话、饼干、证据或散列. ready 不赋予平台角色或可重复使用的管理模式。 Bootstrap批准在解剖学上消耗其状态过渡的证明.
组织所有者变更使用同一仪式. 将相同的输入传递到起始和提交方法 :
const change = {
requestedManualRole: "owner" as const,
expectedActorAuthorityRevision: 3,
expectedTargetAuthorityRevision: 1,
idempotencyKey: crypto.randomUUID(),
};
await auth.startOrganizationOwnerRoleChangeRecentAuthentication(organizationId, memberId, change);
await auth.verifyRecentAuthenticationWithPasskey();
await auth.changeOrganizationMemberManualRole(organizationId, memberId, change);
原子转移类似对 startOrganizationOwnershipTransferRecentAuthentication() 与 transferOrganizationOwnership()。输入名称为不同的离任所有人和继任成员,由此产生的离任所有人 member 或者说 admin 角色,所有精确的权威修改,以及一个一能键. SDK验证并发出一个正常化请求,永远不暴露所消耗的证据.
如果找不到启动结果, 请打电话 startPlatformRoleChangeRecentAuthentication() 以相同的常态输入。 客户端重用当前请求的非ce 。 不同的开始或观察到的账户过渡会去除此本地重试状态 。 只有一个启动请求可以激活 。 重叠调用返回 state_conflict 在网络I/O之前。 SDK 不自动重试 .
读取用户当前在 Auth 源代码上的有效平台角色 :
const platform = await auth.getPlatformStatus();
结果包含 eligible, (中文(简体) ), roles,以及 globalRevision。仅用于显示和突变准备。 每个平台运行再次检查当前权限. 浏览器 SDK 发送来自 Auth 的 cookie 证书和精确的浏览器生成 Origin它发送不 Authorization 头曰. 服务器和设备客户端不披露这种方法.
请求 A 驱动程序证书
请求一个工作空间的短寿命证书:
const credential = await auth.getDriveCredential({
workspace: { type: "personal" },
});
await fetch("https://drive.finueva.com/api/files", {
headers: {
authorization: `${credential.tokenType} ${credential.token}`,
},
});
对于一个组织的工作空间:
const credential = await auth.getDriveCredential({
workspace: {
type: "organization",
organizationId: "org_01J8R7M6N5P4Q3S2T1",
},
});
浏览器和设备客户端缓存 1 完成内存的证书 。 服务器客户端不缓存证书 。 SDK从未重放失败的 Drive 操作.
服务器快速启动
在每个请求处理器内创建一个客户端 :
import { createServerAuth } from "@finueva/auth/server";
export async function handleRequest(request: Request): Promise<Response> {
const auth = createServerAuth({
authOrigin: "https://auth.finueva.com",
appOrigin: "https://app.heylomeet.com",
request,
});
const session = await auth.getSession();
return Response.json(
{ authenticated: session !== null, user: session?.user ?? null },
{ headers: { "cache-control": "no-store" } },
);
}
如果收到的请求 Authorization,服务器客户端使用该无记名并转发没有饼干。 否则,它只转发Auth饼干.
一些服务器方法返回 cookie 更改 。 在发送回复前应用 :
const result = await auth.signOut();
const headers = new Headers({ location: "/signed-out" });
result.applyCookies(headers);
return new Response(null, { status: 303, headers });
打电话 applyCookies() 在服务器之后 signOut(), (中文(简体) ), acceptInvitation(),以及 signInWithSocialIdToken()。 。 。 .
恢复谷歌账户冲突
使用请求范围服务器客户端. 如果 Google 登录报告账户冲突, 请打电话 startGoogleAccountRecovery({ idToken: { token, nonce } }). Auth验证了Google的身份,并证实现有的已核实密码账户需要链接. 仅凭一般性的登录错误并不能授权回收.
结果是不透明的 recoveryToken 和一个ISO expiresAt. 将结果和Google标识符保存在短寿命加密服务器管理状态. 不要将它们放入URL,日志或浏览器存储中.
请用户用密码登录到已有的账户 。 完成配置的第二个因子 。 然后用该会话创建一个新的请求范围客户端并呼叫 completeGoogleAccountRecovery({ recoveryToken, idToken: { token, nonce } }). Auth在恢复请求后需要更新密码认证. 旧会话或不同的账户无法完成回收。 结果是 { linked: true };本届会议仍在使用.
如果补全返回 google_recovery_in_progress,保留当前届会和恢复状态。 重试可以确认一个承诺的链接. Auth 不重复不确定的链接突变. 如果原始尝试无法完成, 请等待其10分钟后重启 .
恢复是 cookie 唯一的, 并绑定在注册的应用程序源头 。 它不能仅通过电子邮件匹配来隐含连接或链接用户. 如果恢复已过期, 请重新启动 Google 登录 。 如果需要电子邮件验证,则首先完成现有账户的验证.
解析驱动收件人
使用单独的服务器客户端 。 查询前,Drive必须检查拨打者当前管理项目股份的许可. 收件人解析不批准文件访问 .
import { createDriveRecipientAuth } from "@finueva/auth/server";
const recipients = createDriveRecipientAuth({
authOrigin: "https://auth.finueva.com",
timeoutMs: 10_000,
});
const recipient = await recipients.resolveRecipient(
{ email: "[email protected]" },
clientAssertion, // Generate a new service assertion for this request.
);
结果只包含 { userId, name, email }。 SDK 修剪和小写 。 它不接受额外的输入字段. 电子邮件有320字节的UTF-8限制. JSON请求有1024字节限制. 反应有4096字节限制. 犬形ID包含1到128个URL安全字符. 非空名有1024字节的UTF-8限制. 回复邮件必须正常化, 并匹配请求的邮件 .
打电话者提出一个Ed25519断言 typ: "finueva-drive-recipient-assertion+jwt" 和注册的JWK-thumb打印 kid设置 aud 到精确的 Auth 端点,例如 https://auth.finueva.com/api/v1/auth/drive-share-recipients/resolve设置 iss 财务报告和审定财务报表 sub 使用同一注册服务身份证。 包含已注册 environment, (中文(简体) ), ver: 1, (中文(简体) ), principal_type: "ecosystem-service",一个新的界限 jti,以及 exp = iat + 60 在几秒钟。 可选包括 actorUserId,服务器解决接收者的用户名称。 从服务器已核实的会话中取出,而不是从请求值取出. 它包含1到128个URL安全字符. Auth 将每个角色限制在每60秒30个分辨率, 并拒绝无效的 actorUserId 状态401 无 actorUserId,服务的所有请求每60秒共享10个分辨率。 Auth允许30秒的钟容度. 供应主张不能取而代之。 SDK不接受密钥. Auth验证断言;SDK只检查其紧凑的语法和16个KiB无记名头的限制.
这个客户端没有发送饼干或起源。 它没有跟踪方向,没有保留接收者缓存,也没有自动重试。 它允许每个客户请求一个主动请求,而不排队. 默认最后期限为10秒. timeoutMs 接受从 1 到 60 000 的整数。 如果自定义运输忽略了中止,客户端会拒绝新通话,直到运输结算. 习惯 fetch 必须保留请求的可信度和重定向限制.
在回复失败或失败后,使用新的断言进行明确的重试. 即使请求失败, Auth 也可以使用一个断言 。 失踪或无资格领取者返回 AuthError 与 recipient_unavailable 和状态 404。 精确无效请求的响应映射到 invalid_request 地位为400或413。 无效的断言映射到 authentication_required 状态401 利率限制映射到 auth_request_rate_limited 具有第429和 retryAfterSeconds 从1起至60起。 无效的响应、 运输故障和服务故障映射到 identity_authority_unavailable 地位为503. 重叠调用返回 state_conflict 地位为409. 错误消息中没有请求值 .
本地语法错误 invalid_request 和状态400。 准确 405 METHOD_NOT_ALLOWED 用信件回复 METHOD_NOT_ALLOWED 地图到 invalid_request 状态405 其他非合同错误没有以503号状态结清。 SDK 不披露原始 Auth 错误消息 .
工厂和接收者类型只存在于 @finueva/auth/server。饼干和会话客户端来自 createServerAuth() 不暴露接收方的分辨率.
设备快速启动
将设备流用于可以打开系统浏览器的 CLI 或本地应用程序:
import { createDeviceAuth } from "@finueva/auth/device";
const auth = createDeviceAuth({
authOrigin: "https://auth.finueva.com",
clientId: "finueva-cli",
});
const authorization = await auth.requestAuthorization();
console.log(`Open ${authorization.verificationUriComplete}`);
console.log(`Code: ${authorization.userCode}`);
await auth.pollForToken(authorization);
clientId 是公开的. deviceCode 没有。 永远不要登录或显示设备代码 .
设备符号默认为只存储. 对于持久性,供应a DeviceTokenPersistence 由操作系统认证金库支持的适配器. 不要使用本地存储, WebView cookie 罐, 或纯文本文件 .
处理出错
构造器和选中的账户输入出错可以丢出 TypeError。验证 SDK 请求失败 AuthError。设备授权可以丢出 DeviceAuthorizationError数字 :
import { AuthError } from "@finueva/auth";
try {
await auth.signIn({
email: "[email protected]",
password: "correct-horse-battery-staple",
});
} catch (error) {
if (!(error instanceof AuthError)) throw error;
if (error.code === "invalid_credentials") {
showInvalidCredentials();
} else if (error.code === "identity_authority_unavailable") {
showAuthUnavailable();
} else {
showAuthError(error.code);
}
}
使用 error.code 应用行为。 不分析 error.message除RFC 8628设备投票外,SDK不自动重复请求.
安全检查清单
- 使用 HTTPS. Plain HTTP只在本地主机接受.
- 登记确切的应用程序来源.
- 每个请求创建服务器客户端 .
- 在发送回复前应用返回的服务器 cookie .
- 只保留浏览器会话快照显示 .
- 将设备会话保存在操作系统认证金库中.
- 将 Drive 证书保存在内 .
- 保护服务器侧突变对抗CSRF.
- 不要日志 cookie,无记名符号,ID符号,重置符号,设备代码,或完成Auth回复.
当前限制
- 包是实验到
1.0.0。 。 。 . - 默认10秒后请求暂停 .
timeoutMs接受1至60,000毫秒.- 大多数答复限于64 KiB。 组织读取的答复最多可使用256 KiB.
- 浏览器快照和无记名授权值仅限于16 KiB.
- 组织收藏使用从1到100的页面大小.
- Organization Drive上下文最多支持50个现有团队.
- 邀请创建仅支持
member在普通组织中发挥作用. - 邀请重新发送和取消需要浏览器-cookie或服务器-cookie权限和一个手动所有者或手动管理员.
- Cross-root SSO,自定义刷新符号,账户删除未执行.
- 生产支持门不全. 在通过前询问 Auth 运算符当前环境状况 .
软件包导出
import { AuthError, createAuthClient } from "@finueva/auth";
import { createServerAuth } from "@finueva/auth/server";
import { DeviceAuthorizationError, createDeviceAuth } from "@finueva/auth/device";
TypeScript申报船与包裹. 公开声明没有披露更好的Auth类型.
文档
长式任务指南由Auth源维护:
这些链接跟踪了当前源并可以描述一个更新的SDK版本. 在指南开头检查版本 。 在这些指南得到公开的、版本化的文件部署之前,必须进入仓库.
联合国存在核准候选人
请求范围服务器方法 authorizeOrganizationPresence(organizationId, { applicationId, userIds }, serviceAssertion, { applicationCredential }) 转发新角色、应用程序和注册服务证明。 精确命令的选择包含1到50个Auth用户ID. 提供的应用程序证书仍然不可信。 授权检查它的签名和当前演员的确切会话 。 其结果对行为者、组织、申请和选定的身份证具有约束力;其观察时间不是授权租赁。 您的申请必须执行自己的公开和知名度政策。 浏览器和设备客户端没有直接的服务外观. 这一来源候选人在使用前需要获得释放和登记资格.
组织招生候选人
导入 createOrganizationAdmissionAuth, (中文(简体) ), createOrganizationAdmissionBillingAuth,以及 createOrganizationAdmissionDirectoryAuth 从 @finueva/auth/server。这些单独的客户披露了演员的设置操作、私人开具账单的证明以及待定的设置目录。 演员业务需要当前收到的请求和单独的服务断言. 使用 organizationAdmissionBodyDigest(operation, input) 和客户的 audience(operation) 当签署每个新的断言。 有待设立的机构不授予任何组织成员资格或驱动权. getCurrentAdmission 返回时 null 或承认 state 实值 pending 或者说 activeAuth没有招生政策 集体或封印 这个来源候选人在使用前需要释放并托管资格.