Skip to content

Sign in with Vimine

View as Markdown

Members of an association sign in to your site with their Vimine account and authorise you, with an explicit consent, to use some of their data. It is OAuth 2.1 with PKCE: the password is only ever typed on the Vimine page, never on yours; your site receives tokens limited to the permissions granted.

  1. From a member’s click, your site sends the browser (the top window, never an iframe) to the authorization page:
    https://app.vimine.network/authorize
    ?client_id=vmc_…&redirect_uri=https://www.example.org/vimine/callback
    &response_type=code&state=…&code_challenge=…&code_challenge_method=S256
    &scope=profile:read%20membership:read
  2. The member signs in (if not already) and sees who is being granted what. They confirm or cancel.
  3. Vimine sends the browser back to your redirect_uri with code and state (or error=access_denied).
  4. Your site exchanges the code at POST https://api.vimine.network/v1/oauth/token (grant_type=authorization_code, code, code_verifier, client_id, redirect_uri) and receives an access token (10 minutes) and a refresh token (rotating; 30 days, 14 of inactivity).

The SDK does all of this for you.

The page stays Vimine’s (the address, the logotype, the words and the controls are its own), but it shows your association: on the left, or at the top on a phone, its logo and its name, your app’s name and the site the member goes back to, that is the host of your redirect_uri.

In Developers › your app › Look choose:

  • a colour: it tints the association’s half and fades into Vimine’s; on the sign-in card it colours only the primary button and the links, darkened or lightened until it reads;
  • a typeface, among those Vimine serves itself, for the association’s half.

Light or dark, and the palette, are the member’s: what they chose in Vimine on that device, otherwise Vimine’s palette in their browser’s light or dark. That is why the preview shows both versions and the authorization address carries neither.

If the association’s logo is made for one background only, say so in Association › Details › Logo made for: on the other scheme the page puts it on a plate.

any page
import { Vimine } from '@vimine/sdk';
const vimine = new Vimine({
clientId: 'vmc_…',
redirectUri: 'https://www.example.org/vimine/callback',
scopes: ['profile:read', 'membership:read']
});
// from a click: `returnTo` is your own state, you get it back after the callback
document.querySelector('#sign-in').addEventListener('click', () => vimine.signIn({ returnTo: location.pathname }));
callback page
const result = await vimine.handleRedirect(); // null when the URL carries no sign-in parameters
if (result) location.replace(result.returnTo || '/');

signIn() options:

Option Effect
scopes the permissions to ask for in this sign-in (default: the constructor’s, or profile:read membership:read)
prompt: 'login' forces the credential form even with a live Vimine session
prompt: 'consent' always shows the consent screen
returnTo your own state (for instance the page the member started from), kept locally and returned by handleRedirect()

handleRedirect() throws a VimineError with code access_denied when the member cancelled, state_mismatch or no_flow when the return does not match a sign-in started from this browser.

  • Tokens live in sessionStorage (per tab). storage: 'local' shares them across tabs and syncs sign-out; 'memory' never persists them. You can pass an adapter of your own.
  • vimine.getAccessToken() refreshes by itself 30 seconds before expiry; a call that receives 401 is retried once after the refresh. A reused refresh token (a copied one, say) revokes the whole session family.
  • vimine.onChange(session => …) notifies sign-in, refresh and sign-out.
  • vimine.getSession().sub is the member’s identifier for your association (vms_…): stable over time, different in every association, never an internal id.
  • vimine.getSession().aps is the association the member signed in to (slug, name, picture_url): show its name and logo beside the member without another call.
await vimine.me.get(); // profile:read
await vimine.me.update({ phone: '+39 …' }); // profile:write
await vimine.membership.get(); // membership:read — status, verdict, expires_at, statuto, card
await vimine.membership.events({ limit: 20 }); // status history
await vimine.membership.statuto(); // the in-force statuto and whether the member accepted it
await vimine.membership.acceptStatuto({ version: 3 }); // statuto:accept
await vimine.membership.request({ accept_statuto: { version: 3 }, privacy_notice: { version: 2 } }); // membership:request → pending card
await vimine.membership.card(); // the card token (QR) and its window
await vimine.legal.acceptances(); // legal:read → what they accepted and what is still pending
await vimine.legal.record({ kind: 'privacy_notice', version: 2 }); // legal:accept → acknowledge the association's notice
await vimine.connections.list(); await vimine.connections.revoke('vmc_…');

verdict is what matters at the door: valid, expired, statuto_required (re-acceptance overdue) or inactive. Someone who is not a member yet can still sign in and use membership.request: consent to the enrolment permissions is allowed to non-members too; the other permissions require a card in that association.

Every method is detailed in Vimine; the responses in Types.

await vimine.signOut(); // revokes the API session and forgets it
await vimine.signOut({ everywhere: true }); // also ends the member's Vimine session and comes back to your site

With everywhere the browser goes through https://app.vimine.network/oauth/logout?client_id=…&post_logout_redirect_uri=…; the return address must be on an origin registered for the client. When the member signs out of Vimine, every API session of their apps is revoked (single sign-out); the same happens when they revoke an app from Connected apps or reset their password.

If you cannot use the SDK (another language, a low-code platform) the flow is standard OAuth 2.1.

Authorization — GET https://app.vimine.network/authorize

Parameter
client_id your vmc_…
redirect_uri one of the registered ones, identical
response_type code
state random value, to compare on return
code_challenge, code_challenge_method PKCE, S256 only
scope space-separated permissions
prompt optional: login, consent
ui_locales optional: one of Vimine’s languages — it (default), en, zh-Hans, zh-Hant, fr, es, de, pt-BR

Exchange — POST https://api.vimine.network/v1/oauth/token, body application/x-www-form-urlencoded:

grant_type=authorization_code&code=…&code_verifier=…&client_id=vmc_…&redirect_uri=…
response
{ "access_token": "…", "token_type": "Bearer", "expires_in": 600, "refresh_token": "…",
"scope": "profile:read membership:read", "sub": "vms_…" }

Refresh — same endpoint with grant_type=refresh_token&refresh_token=…&client_id=…: the response carries a new refresh token (the old one stays valid for 15 seconds for concurrent requests, then reusing it revokes the whole family).

Revoke — POST /v1/oauth/revoke with token=…&token_type_hint=refresh_token (RFC 7009).

Discovery — GET https://api.vimine.network/.well-known/oauth-authorization-server (RFC 8414) lists endpoints, scopes and supported methods.

Errors of these endpoints use the OAuth format: { "error": "invalid_grant", "error_description": "…" }.