VIZ.cx
← Learn

Sign in with VIZ

10 minintermediate

“Sign in with VIZ” is passwordless auth backed by the blockchain: a user proves they control an account by signing a challenge with their key, and your app gets a session token in return. No passwords to store, no email flow — the same key that signs transactions signs the login.

The flow is a signature challenge: fetch a one-time nonce, sign it, exchange the proof for a bearer token. viz.cx's own watchlist and notifications run on exactly this.

Step 1 — Build a signer

npm install @viz-cx/api @viz-cx/core

A Signer is an account name plus a function that signs bytes with its regular key.

import { createApiClient } from '@viz-cx/api'
import { keys, wif } from '@viz-cx/core'

// A Signer proves control of an account by signing challenges with its
// regular key. In a real dApp this comes from the user's wallet — here we
// load the WIF from the environment for a server-side or CLI login.
const signer = {
  account: 'alice',
  sign: (bytes: Uint8Array) => keys.sign(bytes, wif(process.env.VIZ_REGULAR_KEY!)),
}

const api = createApiClient({ auth: signer })

Step 2 — Exchange a signature for a token

authedFetch handles the nonce round-trip and header signing, so hitting POST /session returns a 30-day bearer token.

// authedFetch runs the whole challenge for you:
//   1. POST /auth/nonce            -> a single-use nonce (valid 5 min)
//   2. sign the nonce with the key
//   3. resend with X-Auth-Account / X-Auth-Nonce / X-Auth-Signature
// Exchange that proof for a 30-day bearer token.
const res = await api.authedFetch('/session', { method: 'POST' })
if (!res.ok) throw new Error('login failed')

const { token } = await res.json()
console.log('Session token:', token)   // store it (cookie / secure storage)

Step 3 — Call session-gated endpoints

Send the token as Authorization: Bearer <token>. No signing per request — the token is the session.

// Use the bearer token on session-gated endpoints — no more signing.
const base = 'https://api.viz.cx'
const auth = { Authorization: `Bearer ${token}` }

// e.g. add an account to the signed-in user's watchlist
await fetch(`${base}/watchlist`, {
  method: 'POST',
  headers: { ...auth, 'Content-Type': 'application/json' },
  body: JSON.stringify({ account: 'bob' }),
})

const count = await fetch(`${base}/notifications/count`, { headers: auth }).then(r => r.json())
console.log('unread:', count)

Step 4 — Log out

DELETE /sessionrevokes the token immediately. It's idempotent, so double-logout is fine.

// Log out — revoke the token server-side. Idempotent.
await fetch('https://api.viz.cx/session', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${token}` },
})

Nonces are single-use and expire in 5 minutes; tokens last 30 days or until revoked. In the browser, get the signature from the user's wallet rather than handling their raw key.