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().

import { MeroJs, parseAuthCallback } from '@calimero-network/mero-js';
const auth = parseAuthCallback(window.location.hash);
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.

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.

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)