loginradius-sdk@12 is a fresh package surface, not an in-place upgrade. v12
is generated from the LoginRadius OpenAPI spec — every operation is typed
end-to-end, errors are class-based, and the client is one LoginRadius
object with ~56 typed service fields instead of a factory function returning
20 hand-written namespaces.
Existing 11.x consumers keep working exactly as before — npm install loginradius-sdk@11 still installs the legacy line. Migrate at your own pace.
| Reason to migrate now | Reason to defer |
|---|---|
| You want IDE autocomplete + compile-time safety. | Your integration is stable and on 11.x long-term. |
| You're calling endpoints not covered by v11 (392 vs 210). | You can't take a major-version bump right now. |
You want typed error branching (isAuth, isForbidden). |
You can't take a major-version bump right now. |
| You're starting a new integration. |
You can pin to loginradius-sdk@^11.7 indefinitely. v11 stays in maintenance
for security fixes only.
# v11
npm install loginradius-sdk@11
# v12
npm install loginradius-sdk@12Both publish under the same loginradius-sdk npm name. Lockfiles pin the
major, so an npm install against an existing project won't accidentally
jump to v12.
Node 18+ is required for v12. v11 supported older Node.
The shape changes from a factory function to a class constructor.
// v11 — factory returns an object with namespaces
const lrv2 = require('loginradius-sdk')({
apiKey: process.env.LR_API_KEY,
apiSecret: process.env.LR_API_SECRET,
apiDomain: 'api.loginradius.com',
siteName: 'YourSiteName',
apiRequestSigning: false,
proxy: { host: '', port: '', user: '', password: '' },
});// v12 — class constructor, typed options
import { LoginRadius } from 'loginradius-sdk';
const client = new LoginRadius({
apiKey: process.env.LR_API_KEY!,
apiSecret: process.env.LR_API_SECRET!,
});| v11 | v12 | Notes |
|---|---|---|
apiKey |
apiKey |
Same. Sent as X-LoginRadius-ApiKey header + apikey query fallback. |
apiSecret |
apiSecret |
Same. Sent as X-LoginRadius-ApiSecret header + apisecret query fallback. |
apiDomain |
baseURL |
Only set if you target a non-production URL. Default https://api.loginradius.com. |
siteName |
(removed) | Was used to stamp some emails. Templates now configure this server-side. |
serverRegion |
domain / customDomain / baseURL |
domain is the tenant name on the LoginRadius hub — e.g. domain: 'acme' → https://acme.hub.loginradius.com. Use customDomain: 'auth.acme.com' for your own DNS, or baseURL to target a region-specific API URL directly. |
apiRequestSigning |
apiRequestSigning |
Supported. Opt-in, off by default; applies to /manage/ paths excluding /account/access_token. Do not assume parity with v11's implementation — v11 signed the literal string "undefined", so any v11 signature you have recorded is not a valid reference. Validate against your own tenant before relying on it. |
originIp |
originIp |
X-Origin-IP header on every request. Note this header is not in the OpenAPI spec — the facade sends it to match v11. |
fieldsParam / fieldsValue |
fields |
fields= query parameter on every request. v11 made you supply the separator (?fields= vs &fields=); pass only the value now. |
preventWebhook |
preventWebhook |
X-PreventWebhook: true on every request. v12 also has a per-operation parameter. |
| (implicit) | defaultHeaders |
Headers merged into every request at the lowest precedence — the SDK's own credential and User-Agent headers always win. |
| (implicit) | debug |
Request/response logging with credentials redacted. Pass true or your own sink. |
| (implicit) | serverIndex |
Escape hatch for the 42 operations the spec pins to their own host. An explicit baseURL already overrides those pins. |
proxy |
httpClient (custom axios) |
Build your own axios.create({ proxy, httpsAgent, ... }) and pass it via httpClient. See examples/custom-http/. |
| (implicit) | bearerToken / m2mBearerToken |
New first-class support for Bearer schemes. v11 had no built-in support. |
| (implicit) | accessToken |
First-class — was previously passed per-call. |
| (implicit) | userAgent |
Override the default loginradius-node/12.x.y. |
// v11 — positional args, returns Promise<any>
lrv2.authenticationApi
.loginByEmail(emailAuthenticationModel, emailTemplate, fields, loginUrl, verificationUrl)
.then((res) => console.log(res))
.catch((err) => console.log(err));// v12 — single typed request object, returns Promise<AxiosResponse<TypedModel>>
const res = await client.login.emailByLoginUserNamePhone({
emailByLoginUserNamePhoneRequest: { email, password },
emailTemplate,
fields,
loginUrl,
verificationUrl,
});
console.log(res.data);Three universal changes:
- Single object parameter. Every v12 method takes one object whose property names match the OpenAPI parameter names (camelCase). No more guessing positional order.
.datafor the response body. v12 returns the full axios response; the typed body is on.data. Useconst { data } = await ...for terser call sites.- Promises throw, not reject with bare objects. Use
try/catchand branch oninstanceof LoginRadiusError— no more.catch((err) => …)with an untyped argument.
v11 grouped methods by hand-curated namespace. v12 groups by OpenAPI tag — some v11 namespaces split across multiple v12 services, and a few names diverge. Common mappings:
| v11 namespace | v12 service field(s) |
|---|---|
lrv2.authenticationApi |
client.login, client.user, client.password, client.session, client.security |
lrv2.passwordLessLoginApi |
client.login (passwordless methods) |
lrv2.phoneAuthenticationApi |
client.login, client.user |
lrv2.oneTouchLoginApi |
client.login (oneTouchLogin* methods) |
lrv2.smartLoginApi |
client.login (*SmartLogin methods) |
lrv2.slidingTokenApi |
client.session |
lrv2.pinAuthenticationApi |
client.login, client.password |
lrv2.riskBasedAuthenticationApi |
client.security |
lrv2.accountApi |
client.accounts, client.accountSession, client.accountSecurity |
lrv2.roleApi |
client.roles, client.rolesManagement, client.permissions |
lrv2.sottApi |
client.sott |
lrv2.socialApi / lrv2.nativeSocialApi |
client.accountSession, client.socialProviders, client.login |
lrv2.configurationApi |
client.captchaConfiguration, client.passwordPolicy, client.secondFactorConfiguration, … |
lrv2.webHookApi |
client.webhooks |
lrv2.multiFactorAuthenticationApi |
client.secondFactorConfiguration, client.user, client.login (passkey/MFA methods) |
lrv2.consentManagementApi |
client.consent |
lrv2.customObjectApi |
client.customObject, client.customObjects, client.accountCustomObject |
lrv2.reAuthenticationApi |
client.security |
When in doubt, the IDE is authoritative — client. shows you the full list,
and each service has full JSDoc for the underlying endpoint.
// v11
const emailAuthenticationModel = { email, password };
lrv2.authenticationApi
.loginByEmail(emailAuthenticationModel, '', '', '', '')
.then((res) => console.log(res));// v12
const { data } = await client.login.emailByLoginUserNamePhone({
emailByLoginUserNamePhoneRequest: { email, password },
});
console.log(data);// v11
lrv2.passwordLessLoginApi.passwordlessLoginByEmail(email, '', '').then(console.log);// v12
const { data } = await client.login.passwordlessLoginByEmail({ email });
console.log(data);// v11
lrv2.authenticationApi.getProfileByAccessToken(accessToken, '', '').then(console.log);// v12 — bearer-token style; pass the user's LR access_token as bearerToken
const userClient = new LoginRadius({ apiKey, bearerToken: accessToken });
const { data } = await userClient.user.getAccountDetails({});
console.log(data);The v11 namespaced lookups (getAccountProfileByEmail, getAccountProfileByUserName,
getAccountProfileByPhone) collapse into a single identity query in v12.
// v11
lrv2.accountApi.getAccountProfileByEmail(email, '').then(console.log);// v12 — query identities, then read by UID
const { data: identities } = await client.accounts.getIdentities({ email });
if (identities.Data?.length) {
const { data: profile } = await client.accounts.getAccountIdentityByUID({
uid: identities.Data[0].Uid,
});
console.log(profile);
}// v11
lrv2.authenticationApi.forgotPassword(email, resetPasswordUrl, '').then(console.log);// v12
const { data } = await client.password.forgotPassword({
forgotPasswordRequest: { email },
resetPasswordUrl,
});
console.log(data);// v11
lrv2.authenticationApi.changePassword(accessToken, newPassword, oldPassword).then(console.log);// v12
const userClient = new LoginRadius({ apiKey, bearerToken: accessToken });
const { data } = await userClient.password.changePassword({
changePassword: { newPassword, oldPassword },
});v11's webhook method names were renamed in v12 to match the spec — the shape is identical, only the operation name changed.
// v11
lrv2.webHookApi.listAllWebhooks().then(console.log);
lrv2.webHookApi.createWebhookSubscription(payload).then(console.log);// v12
const { data } = await client.webhooks.getAllWebhooksConfigurations();
const { data: created } = await client.webhooks.createWebhookConfiguration({
webhookConfigurationRequest: payload,
});Mapping cheat sheet:
| v11 method | v12 method |
|---|---|
listAllWebhooks |
getAllWebhooksConfigurations |
createWebhookSubscription |
createWebhookConfiguration |
updateWebhookSubscription |
updateWebhookConfigurationById |
deleteWebhookSubscription |
deleteWebhookConfigurationById |
getWebhookSubscriptionDetail |
getWebhookConfigurationById |
getWebhookEvents |
getAllEvents |
v11's customObjectApi split into three v12 services in the OpenAPI spec:
client.customObject— token-context operations (*ByToken)client.accountCustomObject— server-side UID-context operations (*ByUid)client.customObjects— query-style bulk operations
// v11
lrv2.customObjectApi.getCustomObjectByToken(accessToken, objectName).then(console.log);// v12 — pass the user's access_token via ClientOptions
const userClient = new LoginRadius({ apiKey, accessToken });
const { data } = await userClient.customObject.getCustomObjectByToken({
objectName,
});// v11 — bare object/string in .catch
lrv2.authenticationApi.loginByEmail(model)
.then((res) => console.log(res))
.catch((err) => console.log(err)); // err shape depends on the failure// v12 — typed LoginRadiusError, branch on HTTP semantics
import { LoginRadius, LoginRadiusError } from 'loginradius-sdk';
try {
const { data } = await client.login.emailByLoginUserNamePhone({ ... });
console.log(data);
} catch (err) {
if (err instanceof LoginRadiusError) {
if (err.isAuth()) // 401 — re-authenticate
else if (err.isForbidden()) // 403 — IP/domain restriction or plan limit
else if (err.isRateLimit()) // 429 — back off
else if (err.isServer()) // 5xx — retry later
else console.error(err.code, err.description);
return;
}
throw err;
}LoginRadiusError populates code, description, message, and
rawBody regardless of the response envelope shape — both
/identity/v2/ (capitalised ErrorCode) and /api/v2/access_token/*
(lowercase errorCode) are parsed transparently.
v11 had no first-class header overrides — you'd reach into the request
helper. v12 exposes them as ClientOptions:
// Header-only credential override (uncommon — useful behind a gateway)
const client = new LoginRadius({
apiKey: 'query-side-key',
xLoginRadiusApiKey: 'header-side-key', // overrides X-LoginRadius-ApiKey only
xLoginRadiusApiSecret: 'header-side-secret',
});Per-call axios overrides go through the second argument every generated method accepts:
const { data } = await client.login.checkUserNameAvailability(
{ username: 'alice' },
{ timeout: 5000, headers: { 'X-Trace-Id': traceId } },
);These are settled decisions, not gaps waiting on a release.
- Built-in proxy config — set
proxyon a custom axios instance and pass viahttpClientinstead. Seeexamples/custom-http/. Note v11'sproxyoption was a no-op: it assignedoptions.proxytohttps.request, which has no such option.httpClientis the first working proxy support in the Node line. siteName-driven email template selection — configure server-side or pass the relevant template names per call. v11 acceptedsiteNamebut never read it.
Earlier drafts of this guide listed these as missing. They ship today:
| v11 concept | v12 |
|---|---|
apiRequestSigning |
apiRequestSigning: true — opt-in, off by default. Do not treat v11 as a reference implementation: it signed options.uri/options.body, neither of which was ever set, so every v11 signature was computed over the literal string "undefined". |
serverRegion |
serverRegion — sets the region query parameter. (domain/customDomain select a tenant on the hub, not a region.) |
| (none — new) | validateJWT(token, { algorithm, key }) verifies a LoginRadius-issued JWT locally: signature, exp, nbf, optional issuer and audience. Neither v11 nor early v12 had it. The algorithm is supplied by you and never read from the token header — see the README for why that matters. |
A pragmatic order if you're moving a large integration:
- Inventory.
grepyour codebase forloginradius-sdkand list every namespace + method you call. - Install v12 in parallel. Don't remove v11 yet — keep both in
package.jsonunder separate names if you want. (You cannpm install loginradius-sdk-v11@npm:loginradius-sdk@11to alias.) - Port one flow at a time. Pick the smallest one — usually
checkUserNameAvailabilityor a public availability check. - Update error handling from
.catch(err => ...)toinstanceof LoginRadiusError. Lift error-branching into a small helper. - Switch to
try/catch+async/await. v12 is async-first; you'll see the typed responses better. - Remove
loginradius-sdk@11once every flow is on v12.
- Per-auth-scheme runnable demos live under
v12/examples/. - File an issue: https://github.com/LoginRadius/node-js-sdk/issues — include both v11 and v12 snippets so we can confirm the mapping.
- Security-sensitive concerns:
security@loginradius.com— please email rather than opening a public issue.