Search docs

Jump between documentation pages.

Browse docs

Protect a DaloyJS API with LoginRadius

LoginRadius is a customer identity platform for hosted login, social login, registration, user profiles, and account-management flows. Its official loginradius-sdk wraps the LoginRadius V2 APIs for Node.js. In a DaloyJS API, use it as a server-side verifier for LoginRadius access tokens and as a profile API client.

LoginRadius access-token validation
Client appLoginRadiusDaloyJS API
  1. 01asyncClient appLoginRadiusHosted login, social login, or registration flowLoginRadius issues an access token
  2. 02requestClient appDaloyJS APIAuthorization: Bearer <access token>
  3. 03asyncDaloyJS APILoginRadiusauthValidateAccessToken(accessToken)server-side SDK call
  4. 04asyncDaloyJS APILoginRadiusgetProfileByAccessToken(accessToken)optional profile lookup
  5. 05responseDaloyJS APIClient app401 on invalid token, protected data on success
LoginRadius access tokens are validated through the provider API via the Node SDK. This is different from the JWT/JWKS provider pages, where verification is local after the signing keys are cached.

1. Configure LoginRadius

  1. In the LoginRadius Admin Console, copy your API Key, API Secret, and Site Name.
  2. Configure your hosted login, social login, registration, and callback URLs in LoginRadius. The frontend owns the login flow and sends the resulting access token to your DaloyJS API.
  3. Keep the API secret on the server only. Never expose it in browser, mobile, or generated client code.

2. Install

ts
pnpm add loginradius-sdk

3. Environment variables

ts
# .env
LOGINRADIUS_API_DOMAIN=api.loginradius.com
LOGINRADIUS_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
LOGINRADIUS_API_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
LOGINRADIUS_SITE_NAME=acme
LOGINRADIUS_API_REQUEST_SIGNING=false

4. Plugin

The SDK is CommonJS and does not ship first-class TypeScript types, so wrap only the methods your app needs behind a small typed boundary.

ts
// src/plugins/loginradius.ts
import { createRequire } from "node:module";
import type { App } from "@daloyjs/core";

const require = createRequire(import.meta.url);

type LoginRadiusProfile = {
  Uid?: string;
  ID?: string;
  Email?: Array<{ Value?: string; Type?: string }>;
  Roles?: string[];
  [claim: string]: unknown;
};

type LoginRadiusAccessTokenInfo = {
  access_token?: string;
  expires_in?: number;
  [claim: string]: unknown;
};

type LoginRadiusClient = {
  authenticationApi: {
    authValidateAccessToken(token: string): Promise<LoginRadiusAccessTokenInfo>;
    getProfileByAccessToken(
      token: string,
      emailTemplate?: string | null,
      fields?: string | null,
      verificationUrl?: string | null,
      welcomeEmailTemplate?: string | null,
    ): Promise<LoginRadiusProfile>;
  };
};

const createLoginRadiusClient = require("loginradius-sdk") as (
  config: Record<string, unknown>,
) => LoginRadiusClient;

const loginRadius = createLoginRadiusClient({
  apiDomain: process.env.LOGINRADIUS_API_DOMAIN ?? "api.loginradius.com",
  apiKey: process.env.LOGINRADIUS_API_KEY!,
  apiSecret: process.env.LOGINRADIUS_API_SECRET!,
  siteName: process.env.LOGINRADIUS_SITE_NAME!,
  apiRequestSigning: process.env.LOGINRADIUS_API_REQUEST_SIGNING === "true",
});

export interface Principal {
  sub: string;
  email?: string;
  roles: string[];
  tokenInfo: LoginRadiusAccessTokenInfo;
  profile: LoginRadiusProfile;
}

export const loginRadiusPlugin = {
  name: "loginradius",
  register(app: App) {
    app.decorate("verifier", {
      async verify(token: string): Promise<Principal> {
        const tokenInfo =
          await loginRadius.authenticationApi.authValidateAccessToken(token);
        const profile =
          await loginRadius.authenticationApi.getProfileByAccessToken(token);
        const primaryEmail = profile.Email?.find((email) => email.Value)?.Value;
        return {
          sub: String(profile.Uid ?? profile.ID ?? tokenInfo.access_token),
          email: primaryEmail,
          roles: Array.isArray(profile.Roles) ? profile.Roles : [],
          tokenInfo,
          profile,
        };
      },
    });
  },
};

declare module "@daloyjs/core" {
  interface AppState {
    verifier: { verify(token: string): Promise<Principal> };
    principal?: Principal;
  }
}

5. Guard a route

ts
import { z } from "zod";
import { App, secureHeaders, rateLimit } from "@daloyjs/core";
import { loginRadiusPlugin } from "./plugins/loginradius";
import { requireAuth } from "./plugins/auth"; // from the Overview page

const app = new App();
app.use(secureHeaders());
app.use(rateLimit({ windowMs: 60_000, max: 100 }));
app.register(loginRadiusPlugin);

app.route({
  method: "GET",
  path: "/me",
  operationId: "getMe",
  middleware: [requireAuth()],
  responses: {
    200: {
      description: "OK",
      body: z.object({
        userId: z.string(),
        email: z.string().optional(),
      }),
    },
  },
  handler: ({ state }) => ({
    status: 200,
    body: {
      userId: state.principal!.sub,
      email: state.principal!.email,
    },
  }),
});

Role checks

LoginRadius profile shape depends on your account configuration and selected fields. If your site stores roles or authorization flags in the profile, normalize them in the plugin and enforce them with a narrow middleware:

ts
import type { Middleware } from "@daloyjs/core";

export function requireLoginRadiusRole(role: string): Middleware {
  return async (ctx, next) => {
    if (!ctx.state.principal?.roles.includes(role)) {
      return ctx.problem(403, "forbidden", `Requires ${role}`);
    }
    return next();
  };
}

Registration and account APIs

The SDK also wraps registration, password reset, email verification, access-token invalidation, account lookup, and custom-object APIs. Keep those operations in server-side routes, validate every input with your schema library, and return DaloyJS problem+json errors instead of raw SDK error objects.

Runtimes

loginradius-sdk is a Node-style CommonJS SDK. Use it on the Node adapter, Bun when your deployment supports CommonJS packages, Vercel Node functions, and AWS Lambda. It is not a fit for Cloudflare Workers or Vercel Edge. For edge APIs, put LoginRadius validation behind a small Node service or use direct HTTP calls from a runtime that can safely keep server secrets.

Security notes

  • Treat the LoginRadius API secret like a signing key. Store it in your platform secret manager and never send it to clients.
  • Validate the access token on every protected API request, or cache positive validation results only for a short period bounded by token expiry.
  • Use rateLimit() on login, registration, password reset, and token-validation routes. Identity endpoints are high-value abuse targets.
  • Do not trust user profile fields as authorization policy until your backend has normalized them into explicit roles, scopes, or tenant memberships.

See also the auth integrations overview, Auth0, and Clerk.