跳转到内容

Vimine(浏览器)

查看 Markdown
import { Vimine } from '@vimine/sdk';
const vimine = new Vimine(options);

Vimine 负责使用 Vimine 登录(OAuth 2.1 + PKCE)、会员的会话以及对会员 API 的调用。每个客户端一个实例;创建一次,反复使用。

选项 类型 默认值
clientId string — 您的 vmc_…(必填)
redirectUri string — 调用 handleRedirect() 的页面,已为该客户端登记(必填)
scopes Scope[] ['profile:read', 'membership:read'] signIn() 申请的权限
storage 'session' | 'local' | 'memory' | TokenStorage 'session' 会话和进行中的登录保存在哪里
storageKey string vimine:<clientId>: 存储中的键前缀
locale Locale 'it' 登录和授权同意页面的语言
apiUrl string https://api.vimine.network API 的来源
appUrl string https://app.vimine.network 登录页面的来源
fetch typeof fetch globalThis.fetch 用于测试和 polyfill
navigate (url) => void top.location.assign 如何离开当前页面

缺少 clientId 或 redirectUri 时,构造函数抛出 VimineError(invalid_options)。

把顶层窗口送到登录和授权同意页面。请在用户操作(一次点击)中调用。在跨域 iframe 内部,导航会被拒绝(framed):登录页面从不显示在框架中。

选项
scopes 本次登录的权限
prompt 'login'(始终要求输入登录信息)或 'consent'(始终要求同意)
returnTo 您自己的状态,保存在本地,由 handleRedirect() 返回——不会被发送

createSignInUrl(options?)

章节“createSignInUrl(options?)”

构建授权 URL 并记住进行中的登录(PKCE verifier 和 state),但不进行跳转。适合您自己渲染链接的场景。

当 URL(默认:当前页面)带有返回参数(code+state 或 error)时为 true。

handleRedirect({ url?, cleanUrl? })

章节“handleRedirect({ url?, cleanUrl? })”

在回调页面上:检查 state,用授权码交换令牌并保存会话。返回 { session, returnTo };URL 中没有登录参数时返回 null。启用 cleanUrl(默认 true)时,会从地址栏中移除 code、state 和 error。

错误(VimineError.code):会员未授权时为 access_denied 及其他 OAuth 错误;本浏览器没有发起过登录时(存储为空或已过期)为 no_flow;返回不匹配时为 state_mismatch;授权码已过期或已被使用时为 invalid_grant。

getSession() · isSignedIn()

章节“getSession() · isSignedIn()”

已保存的会话,或 null。

interface Session {
accessToken: string;
refreshToken: string | null;
expiresAt: number; // 毫秒,绝对时间
scope: Scope[];
sub: string | null; // 会员在您的协会中的标识(vms_…)
mode?: 'live' | 'test'; // 测试客户端的会话为 'test'
}

一个有效的访问令牌;若在 30 秒内到期则先刷新。没有会话时抛出 not_signed_in。

用刷新令牌轮换会话(即使被并发调用,也只发出一次请求)。如果刷新令牌已无法使用,会话会被清除,错误会被重新抛出。

signOut({ everywhere?, postLogoutRedirectUri?, state? })

章节“signOut({ everywhere?, postLogoutRedirectUri?, state? })”

撤销刷新令牌(RFC 7009,尽力而为)并清除会话。使用 everywhere: true 时,随后会把浏览器送到 Vimine 应用的 /oauth/logout,结束会员的会话,再返回 postLogoutRedirectUri(默认为 redirectUri;必须位于已登记的来源上)。

每次登录、刷新和退出登录时调用 listener(session | null);使用 storage: 'local' 时,其他标签页中发生的这些事件也会触发。返回取消订阅的函数。

一次经过认证的调用:Authorization: Bearer,输入输出均为 JSON,错误以 VimineError 抛出,响应为 401 时在刷新后重试一次。init.idempotencyKey 会成为 Idempotency-Key 请求头。

const events = await vimine.request('/v1/me/membership/events?limit=5');
Scope
me.get() profile:read 个人资料(Me),带有 codicefiscale_check:当事人的身份识别方式(null = 尚未识别)
me.update(patch) profile:write 电话、性别,以及身份识别信息:出生日期、出生地和出生国(birth_country)、税号——可选,可以之后再补充。不一致的税号返回 422,带有一条 codicefiscale_mismatch 记录;当它为 confirmable 时,当事人可以用 codicefiscale_confirmed: true 确认(指南)
Scope
membership.get() membership:read 会员卡:status、verdict、expires_at、statuto、card
membership.request(body, { idempotencyKey? }) membership:request 入会申请;未提供幂等键时会自动生成。个人资料尚未识别身份时返回 422 identification_required:err.errors 列出缺少的内容,用 me.update() 补全
membership.events({ limit?, cursor? }) membership:read 状态历史,分页
membership.statuto() membership:read 现行章程及会员的接受情况
membership.acceptStatuto({ version, sha256? }) statuto:accept 记录接受
membership.card() membership:read 会员卡令牌(二维码)及其时间窗口;会员资格未生效时返回 409 not_active
membership.renew({ version, sha256? }) POST /v1/me/membership/renew 自助续期:免费且无需审批时立即完成,否则向秘书处提交申请
membership.setNetworkOptIn(bool) PATCH /v1/me/membership/network-opt-in 加入网络的同意(network:manage)
membership.googleWalletUrl() GET /v1/me/membership/wallet/google “添加到 Google Wallet”链接;会员资格未生效时返回 409 not_active
membership.cardSvg() GET /v1/me/membership/card.svg SVG 格式的会员卡(带二维码);会员资格未生效时返回 409 not_active
membership.emailCard() POST /v1/me/membership/card/email 通过电子邮件把会员卡发给会员

协会已发布的活动,包括仅限会员的活动(events:read)。公开活动无需登录:vimine.public.events。

events.list({ filter?, from?, to?, limit? }) filter:upcoming(默认)、past、cancelled、all
events.get(slug) 文案、图片、各报名类型及剩余名额
events.eligibility(slug) 会员能否报名?按类型给出,并附原因(membership_required、sold_out 等)

会员的报名(registrations:read / registrations:write)。参见活动与报名指南。

registrations.create(slug, { registration_type, note?, quantity? }, opts?) 免费类型 → confirmed,带二维码 token;需付参与费 → reserved,带 payment_instructions;422 not_eligible 附原因;自动生成 Idempotency-Key
registrations.list({ filter? }) · registrations.get(id) 会员的报名(filter: 'upcoming')
registrations.cancel(id) 在活动的取消期限内取消(超过期限返回 409 window_closed)
registrations.tokenPayload(id) 已确认报名的二维码内容,或 null
connections.list() 会员在本协会中授权过的应用
connections.revoke(clientId) 撤回对某个应用的同意(其会话随即结束)

无需凭据的公开端点:参见 publicApi。

storage 位置 适用场景
'session'(默认) sessionStorage:每个标签页独立,关闭即清除 对公开客户端而言最安全的默认值
'local' localStorage:多个标签页共享,重启后仍保留;退出登录会同步到其他标签页 会员期望保持登录状态的应用
'memory' 仅在内存中 SSR、测试、自助终端
TokenStorage 您自己的对象 { get, set, remove } Node、加密存储、原生应用

在无法使用浏览器存储的情况下(隐私模式、about:blank),SDK 会退回到内存存储。MemoryStorage 已导出。

  • createPkce() → { verifier, challenge, method: 'S256' }、createVerifier()、createChallenge(verifier)、createState()、base64url(bytes)——基于 WebCrypto 的 PKCE 和 state,供您手动构建流程时使用。
  • DEFAULT_API_URL、DEFAULT_APP_URL。
  • publicApi(options?)——参见公开 API。
  • VimineError——参见错误。