Vimine(浏览器)
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)。
登录
章节“登录”signIn(options?)
章节“signIn(options?)”把顶层窗口送到登录和授权同意页面。请在用户操作(一次点击)中调用。在跨域 iframe 内部,导航会被拒绝(framed):登录页面从不显示在框架中。
| 选项 | |
|---|---|
scopes |
本次登录的权限 |
prompt |
'login'(始终要求输入登录信息)或 'consent'(始终要求同意) |
returnTo |
您自己的状态,保存在本地,由 handleRedirect() 返回——不会被发送 |
createSignInUrl(options?)
章节“createSignInUrl(options?)”构建授权 URL 并记住进行中的登录(PKCE verifier 和 state),但不进行跳转。适合您自己渲染链接的场景。
hasRedirectParams(url?)
章节“hasRedirectParams(url?)”当 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'}getAccessToken()
章节“getAccessToken()”一个有效的访问令牌;若在 30 秒内到期则先刷新。没有会话时抛出 not_signed_in。
refresh()
章节“refresh()”用刷新令牌轮换会话(即使被并发调用,也只发出一次请求)。如果刷新令牌已无法使用,会话会被清除,错误会被重新抛出。
signOut({ everywhere?, postLogoutRedirectUri?, state? })
章节“signOut({ everywhere?, postLogoutRedirectUri?, state? })”撤销刷新令牌(RFC 7009,尽力而为)并清除会话。使用 everywhere: true 时,随后会把浏览器送到 Vimine 应用的 /oauth/logout,结束会员的会话,再返回 postLogoutRedirectUri(默认为 redirectUri;必须位于已登记的来源上)。
onChange(listener)
章节“onChange(listener)”每次登录、刷新和退出登录时调用 listener(session | null);使用 storage: 'local' 时,其他标签页中发生的这些事件也会触发。返回取消订阅的函数。
会员 API
章节“会员 API”request(path, init?)
章节“request(path, init?)”一次经过认证的调用:Authorization: Bearer,输入输出均为 JSON,错误以 VimineError 抛出,响应为 401 时在刷新后重试一次。init.idempotencyKey 会成为 Idempotency-Key 请求头。
const events = await vimine.request('/v1/me/membership/events?limit=5');me
章节“me”| Scope | ||
|---|---|---|
me.get() |
profile:read |
个人资料(Me),带有 codicefiscale_check:当事人的身份识别方式(null = 尚未识别) |
me.update(patch) |
profile:write |
电话、性别,以及身份识别信息:出生日期、出生地和出生国(birth_country)、税号——可选,可以之后再补充。不一致的税号返回 422,带有一条 codicefiscale_mismatch 记录;当它为 confirmable 时,当事人可以用 codicefiscale_confirmed: true 确认(指南) |
membership
章节“membership”| 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
章节“events”协会已发布的活动,包括仅限会员的活动(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
章节“registrations”会员的报名(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
章节“connections”connections.list() |
会员在本协会中授权过的应用 |
connections.revoke(clientId) |
撤回对某个应用的同意(其会话随即结束) |
public
章节“public”无需凭据的公开端点:参见 publicApi。
存储
章节“存储”storage |
位置 | 适用场景 |
|---|---|---|
'session'(默认) |
sessionStorage:每个标签页独立,关闭即清除 |
对公开客户端而言最安全的默认值 |
'local' |
localStorage:多个标签页共享,重启后仍保留;退出登录会同步到其他标签页 |
会员期望保持登录状态的应用 |
'memory' |
仅在内存中 | SSR、测试、自助终端 |
TokenStorage |
您自己的对象 { get, set, remove } |
Node、加密存储、原生应用 |
在无法使用浏览器存储的情况下(隐私模式、about:blank),SDK 会退回到内存存储。MemoryStorage 已导出。