Skip to content

Authentication

Every request the SDK makes carries a JWT access token. This page covers the three ways to obtain that token, and how the SDK keeps it fresh without any work from you.

The simplest path: exchange a username and password for a token pair. MeroJs posts to the node’s /auth/token endpoint, stores the returned tokens in memory (and in your tokenStore, if provided), and attaches the access token to every subsequent request.

import { MeroJs } from '@calimero-network/mero-js';
const sdk = new MeroJs({ baseUrl: 'http://localhost:2428' });
await sdk.authenticate({ username: 'admin', password: 'your-password' });
// sdk.isAuthenticated() === true

You can also set credentials once in the constructor; they’re used the first time a token is needed:

const sdk = new MeroJs({
baseUrl: 'http://localhost:2428',
credentials: { username: 'admin', password: 'your-password' },
});
await sdk.authenticate(); // uses config.credentials

The token lifecycle — refresh is automatic

Section titled “The token lifecycle — refresh is automatic”

The SDK is reactive: it never proactively refreshes (the node rejects a refresh while the access token is still valid). Instead, when a request comes back 401 with x-auth-error: token_expired, the transport refreshes the token and retries the original request — transparently. Your code only ever sees the successful result.

Refresh tokens are single-use, so concurrent 401s are deduplicated: the refresh runs once (guarded by an in-process promise and a Web Lock named mero-js:token-refresh), and every waiting request retries with the one rotated token.

When your app is opened from Calimero Desktop, the desktop process appends the tokens to the URL hash instead of asking the user to log in again. Read them with parseAuthCallback() and apply them with setTokenData().

The desktop hands over tokens no login in this browser asked for, so the app has to opt in with allowUnsolicited. Only do that where nothing but the host can open the page (inside the desktop shell): anywhere else it lets whoever sends a link choose the node and session.

import { MeroJs, parseAuthCallback } from '@calimero-network/mero-js';
const inDesktop = '__TAURI_INTERNALS__' in window;
const auth = parseAuthCallback(window.location.href, { allowUnsolicited: inDesktop });
if (auth) {
// auth.nodeUrl, auth.accessToken, auth.refreshToken,
// auth.applicationId, auth.contextId, auth.contextIdentity
const sdk = new MeroJs({ baseUrl: auth.nodeUrl });
sdk.setTokenData({
access_token: auth.accessToken,
refresh_token: auth.refreshToken,
expires_at: 0, // 0 → the SDK reads the exp claim from the JWT itself
});
// Strip the tokens from the URL bar (security best practice)
history.replaceState(null, '', location.pathname);
}

The hash fragment format is:

#access_token=…&refresh_token=…&node_url=…&application_id=…&context_id=…&context_identity=…

parseAuthCallback is also available as a static method, MeroJs.parseAuthCallback(url).

To start an interactive login (rather than receive a callback), build the node’s login URL and redirect the browser to it. After the user authenticates, the node redirects back to your callbackUrl with the token hash above.

buildAuthLoginUrl adds a single-use auth_state to callbackUrl and remembers it in localStorage for 10 minutes. On return, parseAuthCallback(window.location.href) only adopts tokens that carry a state this browser began, so a crafted link cannot sign the person in to someone else’s node or account. A login URL built any other way gets no tokens back.

import { MeroJs, buildAuthLoginUrl } from '@calimero-network/mero-js';
const url = buildAuthLoginUrl('http://localhost:2428', {
callbackUrl: window.location.origin + '/callback',
mode: 'login',
permissions: ['admin'],
packageName: 'my-app',
});
window.location.href = url;

Pass a tokenStore and the SDK persists tokens on login/refresh and restores them on construction — so a reload stays signed in. Stored tokens are tied to the node’s origin and path prefix: a store shared with another node’s MeroJs is not restored.

import { MeroJs, LocalStorageTokenStore, MemoryTokenStore } from '@calimero-network/mero-js';
// Browser — survives reloads
const sdk = new MeroJs({
baseUrl: 'http://localhost:2428',
tokenStore: new LocalStorageTokenStore('my-app-tokens'), // default key: 'mero-tokens'
});
// Server / ephemeral — no side effects (this is also the default)
const server = new MeroJs({
baseUrl: 'http://localhost:2428',
tokenStore: new MemoryTokenStore(),
});

Implement the TokenStore interface (getTokens / setTokens / clear) to back tokens with anything else — see the reference.

sdk.isAuthenticated(); // boolean — are tokens present?
sdk.getTokenData(); // TokenData | null (useful for debugging)
sdk.clearToken(); // drop tokens from memory and the store (sign out)