Skip to content

Vimine (browser)

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

Vimine handles Sign in with Vimine (OAuth 2.1 + PKCE), the member’s session and the calls to the member API. One instance per client; create it once and reuse it.

Option Type Default
clientId string — your vmc_… (required)
redirectUri string — the page that calls handleRedirect(), registered for the client (required)
scopes Scope[] ['profile:read', 'membership:read'] permissions asked by signIn()
storage 'session' | 'local' | 'memory' | TokenStorage 'session' where the session and the pending sign-in live
storageKey string vimine:<clientId>: key prefix in the storage
locale Locale 'it' language of the sign-in and consent pages
apiUrl string https://api.vimine.network API origin
appUrl string https://app.vimine.network origin of the sign-in pages
fetch typeof fetch globalThis.fetch for tests and polyfills
navigate (url) => void top.location.assign how to leave the page

The constructor throws a VimineError (invalid_options) without clientId or redirectUri.

Sends the top window to the sign-in and consent page. Call it from a user gesture (a click). From inside a cross-origin iframe the navigation is refused (framed): the sign-in page is never shown in a frame.

Option
scopes permissions of this sign-in
prompt 'login' (credentials always) or 'consent' (consent always)
returnTo your own state, kept locally and returned by handleRedirect() — it is not sent

Builds the authorization URL and remembers the pending sign-in (PKCE verifier and state) without navigating. Useful when you render the link yourself.

true when the URL (default: the current page) carries the parameters of a return (code+state or error).

On the callback page: checks the state, exchanges the code for tokens and stores the session. Returns { session, returnTo }, or null when the URL carries no sign-in parameters. With cleanUrl (default true) it removes code, state and error from the address bar.

Errors (VimineError.code): access_denied and the other OAuth errors when the member did not authorise; no_flow when no sign-in was started from this browser (storage empty or expired); state_mismatch when the return does not match; invalid_grant when the code expired or was already used.

The stored session, or null.

interface Session {
accessToken: string;
refreshToken: string | null;
expiresAt: number; // ms, absolute
scope: Scope[];
sub: string | null; // the member's identifier for your association (vms_…)
mode?: 'live' | 'test'; // 'test' for sessions of a test client
aps?: AssociationRef | null; // the association: slug, name, picture_url
}

A valid access token, refreshed when it expires within 30 seconds. Throws not_signed_in without a session.

Rotates the session with the refresh token (a single request even when called concurrently). When the refresh token is no longer usable the session is cleared and the error rethrown.

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

Section titled “signOut({ everywhere?, postLogoutRedirectUri?, state? })”

Revokes the refresh token (RFC 7009, best effort) and forgets the session. With everywhere: true it then sends the browser to the Vimine app’s /oauth/logout, which ends the member’s session and comes back to postLogoutRedirectUri (default redirectUri; it must be on a registered origin).

listener(session | null) on every sign-in, refresh and sign-out; with storage: 'local' also for those that happened in other tabs. Returns the unsubscribe function.

An authenticated call: Authorization: Bearer, JSON in and out, errors as VimineError, one retry after a refresh when the response is 401. init.idempotencyKey becomes the Idempotency-Key header.

const events = await vimine.request('/v1/me/membership/events?limit=5');
Scope
me.get() profile:read the profile (Me), with codicefiscale_check: how the person is identified (null = not yet)
me.update(patch) profile:write phone, gender, and the identification: date, place and country of birth (birth_country), fiscal code — optional, may be added later. A code that disagrees answers 422 with a codicefiscale_mismatch entry; when it is confirmable, the person confirms it with codicefiscale_confirmed: true (guide)
Scope
membership.get() membership:read the card: status, verdict, expires_at, statuto, card
membership.request(body, { idempotencyKey? }) membership:request membership request; an idempotency key is generated when absent. 422 identification_required when the profile is not identified: err.errors names what is missing, completed with me.update()
membership.events({ limit?, cursor? }) membership:read status history, paginated
membership.statuto() membership:read the in-force statuto and the member’s acceptance
membership.acceptStatuto({ version, sha256? }) statuto:accept records the acceptance
membership.card() membership:read the card token (QR) and its window; 409 not_active unless the membership is active
membership.renew({ version, sha256? }) POST /v1/me/membership/renew self-service renewal: at once when free with no approval gate, otherwise a request to the secretariat
membership.setNetworkOptIn(bool) PATCH /v1/me/membership/network-opt-in network consent (network:manage)
membership.googleWalletUrl() GET /v1/me/membership/wallet/google a “Save to Google Wallet” link; 409 not_active unless the membership is active
membership.cardSvg() GET /v1/me/membership/card.svg the card as SVG (with the QR); 409 not_active unless the membership is active
membership.emailCard() POST /v1/me/membership/card/email e-mails the member their card

The association’s published events, members-only ones included (events:read). The public ones need no sign-in: vimine.public.events.

events.list({ filter?, from?, to?, limit? }) filter: upcoming (default), past, cancelled, all
events.get(slug) copy, images, registration types with places left
events.eligibility(slug) may the member register? per type, with reasons (membership_required, sold_out, …)

The member’s registrations (registrations:read / registrations:write). See the Events and registrations guide.

registrations.create(slug, { registration_type, note?, quantity? }, opts?) free type → confirmed with the QR token; with a contribution → reserved with payment_instructions; 422 not_eligible with the reasons; Idempotency-Key generated
registrations.list({ filter? }) · registrations.get(id) the member’s registrations (filter: 'upcoming')
registrations.cancel(id) cancel inside the event’s window (409 window_closed after)
registrations.tokenPayload(id) the QR content of a confirmed registration, or null
connections.list() the apps the member authorised in this association
connections.revoke(clientId) withdraws consent from an app (its sessions end)

The public, credential-less endpoints: see publicApi.

storage Where When
'session' (default) sessionStorage: per tab, gone when it closes the safest default for a public client
'local' localStorage: shared across tabs, survives restarts; sign-out propagates to other tabs apps where the member expects to stay signed in
'memory' memory only SSR, tests, kiosks
TokenStorage an object of yours { get, set, remove } Node, encrypted storages, native apps

Where browser storage is unavailable (private mode, about:blank) the SDK falls back to memory. MemoryStorage is exported.

  • createPkce() → { verifier, challenge, method: 'S256' }, createVerifier(), createChallenge(verifier), createState(), base64url(bytes) — PKCE and state on WebCrypto, if you build the flow by hand.
  • DEFAULT_API_URL, DEFAULT_APP_URL.
  • publicApi(options?) — see Public API.
  • VimineError — see Errors.