SessionKit docs
API reference
Everything @blaclabs/sessionkit exports, in both halves — browser and server.
Browser
connect(options)
Generates a session key pair, has your server sign the request, renders the
BLAC Code, and resolves once the user approves in BLAC Wallet. On the CDN
build it is BLACSession.connect.
| Option | Type | Meaning |
|---|---|---|
scopes | Scope[] |
What you're asking for. identity is added automatically,
and each sign:* scope adds its address scope. Required.
|
sign | (input) => Promise |
Posts { kemPubKey, scopes } to your backend and
returns its answer, { fields, sig, sigKid? }. The
browser checks that the fields match its own key, origin and scopes
before it shows a code. Required.
|
mount | HTMLElement | Where to draw the code. A canvas is created inside it. Omit to render the code yourself. |
origin | string | Defaults to the page's origin. Must match your well-known document and what your server signs. |
relayUrl | string | Defaults to the BLAC relay. Change it only for local testing. |
render | { color, background } | Code colours. Keep the strokes light on a dark card. |
signal | AbortSignal | Cancel the pending sign-in (user closed your modal). |
onApproved | ({ sas, origin }) => void |
Fires once the wallet approved. On a sign session, show
sas next to the code: the user compares the four digits
with the ones in the wallet.
|
onHandoff | () => void | The page runs inside BLAC Wallet's browser and the code was handed to it. Show "approve in BLAC Wallet" instead of a code. |
onHandoffDeclined | () => void | The wallet declined the hand-off, or refused the code. |
onDiagnostic | (code) => void | Fires for every frame the signing channel refused, with the reason only. Safe to log. |
A decline in the wallet sends nothing, so the promise keeps waiting until
the code expires or your signal fires.
The session object
{
origin: "https://your-domain.com",
grantedScopes: ["identity", "address:solana"],
grantedChains: [], // exact chains granted for signing; empty for a login
grantedMethods: [], // granted signing methods; empty for a login
user: {
id: "…", // pairwise, stable, unique to your origin
pubKey: "…", // Ed25519 public key
proof: "…", // signature binding this login to this session
username?: "…", // iff profile:username granted
avatar?: "…", // iff profile:avatar granted (base64)
avatarMimeType?: "…",
solanaAddress?: "…", // iff address:solana granted and its proof checked out
evmAddress?: "…", // iff address:evm granted and its proof checked out
favoriteMarkets?: […] // iff favorites:read granted
},
verified: { … }, // the verified view of the approval
raw: { … }, // forward this to your backend to verify
sas?: "1234", // sign sessions only; the wallet shows the same digits
isConnected: false, // true while the signing channel is open
request(input), // ask the wallet to sign
activity(), // tell the wallet the user is here (once a minute at most)
disconnect(), // end the session on both sides
onEnd(listener), // called once when the session ends; returns an unsubscribe
solanaWallet(options), // Wallet Standard wallet + Phantom-shaped provider
ethereumProvider(options) // EIP-1193 provider
}
A field for a scope that was not granted holds undefined.
Compare against undefined rather than testing whether the key
exists — the user may approve some scopes and not others.
A login without a sign:* scope is one exchange: nothing stays
open, isConnected is false, and request rejects
with 4900.
session.request(input)
await session.request({
chain: "solana", // or "eip155:1", "eip155:137", …
method: "solana_signMessage",
params: { message: base64Message }
});
Scope Methods sign:solana solana_signMessage, solana_signTransaction,
solana_signAllTransactions,
solana_signAndSendTransaction sign:evm personal_sign, eth_signTypedData_v4,
eth_sendTransaction
The wallet fills nonce, gas and fees itself, and the user approves every
request in the app. A request that fails rejects with a
SignRequestError whose code follows EIP-1193:
Code Meaning 4001The user rejected the request. 4100The chain, method or account was not granted. 4200Unsupported method. 4900The session has ended, or never had a signing channel. 4902Unsupported chain. -32602Invalid parameters. -32005Rate limited. Too many requests at once. -32603Internal error.
Adapters
session.solanaWallet({ register }) returns a Wallet
Standard wallet and a Phantom-shaped provider. Pass
register: true so wallet-adapter dapps list "BLAC Wallet".
session.ethereumProvider({ rpcUrls, defaultChainId, announce, injectGlobal })
returns an EIP-1193 provider. rpcUrls is required, keyed by
chain id: the wallet never proxies reads. announce: true
announces it via EIP-6963, and injectGlobal: true assigns
window.ethereum when it is unset.
Errors from connect
connect rejects with a SessionError carrying a
code you can branch on:
Code Meaning invalid_scope You requested a scope outside the third-party catalog. signing_failed
Your sign callback threw or returned no
fields and sig, or your server signed a
different key, topic, origin or audience, an expiry outside the
window, or wider scopes than asked. Check your backend config first.
relay_unavailable The relay could not be reached. Offer a retry. expired Nobody approved in time. Generate a new code. sas_mismatch
The four digits did not match on a sign session. Treat as hostile and
start over.
aborted Your AbortSignal fired.
An approval that fails verification never rejects the promise. Anyone who
saw the code can post junk to it, so SessionKit ignores anything that does
not verify and keeps waiting.
Rendering the code yourself
Skip mount and draw it where you like. The pairing topic is
in the fields your server returned, so read it in your
sign callback. Geometry is on a 440×440 logical canvas.
import { connect, renderBlacCode, blacCodeGeometry } from "@blaclabs/sessionkit";
const session = await connect({
scopes,
sign: async (input) => {
const signed = await signWithYourBackend(input);
renderBlacCode(canvas, signed.fields.pairingTopic, { color: "#fff" });
// or build your own SVG / WebGL / print output:
// const { data, anchors } = blacCodeGeometry(signed.fields.pairingTopic);
return signed;
}
});
Server
Imported from @blaclabs/sessionkit/server. Node only — never
import this from browser code.
createSessionSigner(config)
Config Type Meaning privateKeyPem string Your Ed25519 domain key, from npx @blaclabs/sessionkit init. Required. origin string
Your origin, exactly as served. Signed into every code; never read
from the request. Required.
allowedScopes Scope[]
The most a code may ask for. Requests are clipped to it, never
widened. Required.
kid string Key id, when your well-known document publishes more than one key. appId string Reverse-DNS client id. Defaults to the origin's hostname. expirySeconds number Code lifetime. Default 120, capped at 180.
Returns a signer. Call signer.sign(body) with the browser's
{ kemPubKey, scopes }; it returns
{ fields, sig, sigKid? } for your endpoint to send back.
Unknown or disallowed scopes are dropped silently, so a page cannot probe
your policy. Store fields.pairingTopic with the browser
session that asked.
signSessionRequest(config, input)
One-shot form of createSessionSigner(config).sign(input).
verifyApproval({ approval, origin, pairingTopic })
Verifies a completed login. Pass session.raw as
approval and the pairing topic you stored when you
signed, consumed once. Optional maxAgeMs (default five
minutes) and maxSkewMs (default two minutes) bound the
wallet's clock.
Returns { userId, origin, pairingTopic, issuedAt, grantedScopes, grantedChains, grantedMethods },
plus username, avatar, avatarMimeType,
solanaAddress, evmAddress and
favoriteMarkets when granted, and actionTopic and
sessionExpiresAt on a sign session. Throws
ApprovalError on the first check that fails. No network call
— the proof is self-contained.
Exports
import {
connect, SessionError, SignRequestError, ApprovalError,
renderBlacCode, blacCodeGeometry, encodeBlacCodePayload,
THIRD_PARTY_SCOPES, ERROR_CODES
} from "@blaclabs/sessionkit";
import type {
Scope, Chain, SignMethod, Session, ConnectOptions, ApprovedUser,
SignRequest, SignRequestInput, EthereumProviderOptions, SolanaWalletOptions
} from "@blaclabs/sessionkit";
import {
createSessionSigner, signSessionRequest, verifyApproval
} from "@blaclabs/sessionkit/server";