npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

loginllama

v2.2.0

Published

Loginllama API wrapper

Downloads

92

Readme

LoginLlama API Client

Official Node.js/TypeScript SDK for LoginLlama - AI-powered login security and fraud detection.

Features

  • Automatic Context Detection: Auto-detects IP address and User-Agent from Express, Next.js, and other frameworks
  • Multi-Source IP Extraction: Supports X-Forwarded-For, CF-Connecting-IP, X-Real-IP, True-Client-IP with private IP filtering
  • Middleware Support: Drop-in middleware for Express and Next.js
  • TypeScript: Fully typed for excellent IDE support
  • Webhook Verification: Built-in HMAC signature verification

Installation

npm install [email protected]
# or
pnpm add [email protected]

Requires Node.js 22 or higher.

Quick Start

With Middleware (Recommended)

The simplest way to use LoginLlama is with the middleware pattern, which automatically captures request context:

import { LoginLlama } from 'loginllama';
import express from 'express';

const app = express();
const loginllama = new LoginLlama({
  apiKey: process.env.LOGINLLAMA_API_KEY
});

// Add middleware to auto-capture request context
app.use(loginllama.middleware());

app.post('/login', async (req, res) => {
  try {
    // IP and User-Agent are automatically detected!
    const result = await loginllama.check(req.body.email);

    if (result.status === 'error' || result.risk_score > 5) {
      console.log('Suspicious login blocked:', result.codes);
      return res.status(403).json({ error: 'Login blocked' });
    }

    // Continue with login...
    res.json({ success: true });
  } catch (error) {
    console.error('LoginLlama error:', error);
    // Fail open on errors
    res.json({ success: true });
  }
});

Without Middleware

If you prefer not to use middleware, you can pass the request explicitly:

const result = await loginllama.check(req.body.email, {
  request: req
});

Or provide IP and User-Agent manually:

const result = await loginllama.check('[email protected]', {
  ipAddress: '203.0.113.42',
  userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...'
});

Framework Examples

Express

import express from 'express';
import { LoginLlama } from 'loginllama';

const app = express();
const loginllama = new LoginLlama();

// Use middleware for automatic detection
app.use(loginllama.middleware());

app.post('/login', async (req, res) => {
  const result = await loginllama.check(req.body.email, {
    geoCountry: 'US',
    geoCity: 'San Francisco'
  });

  if (result.risk_score > 5) {
    return res.status(403).json({ error: 'Suspicious login' });
  }

  res.json({ success: true });
});

Next.js App Router

// app/api/login/route.ts
import { LoginLlama } from 'loginllama';
import { NextRequest, NextResponse } from 'next/server';

const loginllama = new LoginLlama();

export async function POST(request: NextRequest) {
  const body = await request.json();

  const result = await loginllama.check(body.email, {
    request: request // Pass Next.js request explicitly
  });

  if (result.risk_score > 5) {
    return NextResponse.json(
      { error: 'Login blocked' },
      { status: 403 }
    );
  }

  return NextResponse.json({ success: true });
}

Next.js Pages Router

// pages/api/login.ts
import { LoginLlama } from 'loginllama';
import type { NextApiRequest, NextApiResponse } from 'next';

const loginllama = new LoginLlama();

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const result = await loginllama.check(req.body.email, {
    request: req
  });

  if (result.risk_score > 5) {
    return res.status(403).json({ error: 'Login blocked' });
  }

  res.json({ success: true });
}

API Reference

new LoginLlama(options)

Create a new LoginLlama client.

Options:

  • apiKey (optional): Your API key. Defaults to LOGINLLAMA_API_KEY environment variable
  • baseUrl (optional): Custom API endpoint for testing
const loginllama = new LoginLlama({
  apiKey: 'your-api-key'
});

loginllama.check(identityKey, options)

Check a login attempt for suspicious activity.

Parameters:

  • identityKey (required): User identifier (email, username, user ID, etc.)
  • options (optional):
    • ipAddress: Override auto-detected IP address
    • userAgent: Override auto-detected User-Agent
    • request: Explicit request object (Express, Next.js)
    • emailAddress: User's email address for additional verification
    • geoCountry: ISO country code (e.g., 'US', 'GB')
    • geoCity: City name for additional context
    • userTimeOfDay: Time of login attempt

Returns: Promise<LoginCheckResponse>

interface LoginCheckResponse {
  status: 'success' | 'error';
  message: string;
  codes: LoginCheckStatus[];
  risk_score: number; // 0-10 scale
  environment: string;
  meta?: Record<string, any>;
}

Detection Priority:

  1. Explicit ipAddress and userAgent in options
  2. Extract from request object if provided
  3. Use async context from middleware (if used)

Examples:

// Auto-detect from middleware context
const result = await loginllama.check('[email protected]');

// Pass request explicitly
const result = await loginllama.check('[email protected]', {
  request: req
});

// Manual override
const result = await loginllama.check('[email protected]', {
  ipAddress: '203.0.113.42',
  userAgent: 'Mozilla/5.0...'
});

// With additional context
const result = await loginllama.check('[email protected]', {
  emailAddress: '[email protected]',
  geoCountry: 'US',
  geoCity: 'San Francisco'
});

loginllama.middleware()

Returns Express/Next.js middleware that automatically captures request context using AsyncLocalStorage.

app.use(loginllama.middleware());

verifyWebhookSignature(payload, signature, secret)

Verify webhook signature using constant-time HMAC comparison.

Parameters:

  • payload: Raw webhook body (string or Buffer)
  • signature: Value from X-LoginLlama-Signature header
  • secret: Webhook secret from LoginLlama dashboard

Returns: boolean

import { verifyWebhookSignature } from 'loginllama';
import express from 'express';

app.post(
  '/webhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const valid = verifyWebhookSignature(
      req.body,
      req.headers['x-loginllama-signature'] as string,
      process.env.WEBHOOK_SECRET!
    );

    if (!valid) {
      return res.status(401).send('Invalid signature');
    }

    const event = JSON.parse(req.body.toString());
    // Handle event...
    res.sendStatus(200);
  }
);

Login Status Codes

The SDK exports a LoginCheckStatus enum with all possible status codes:

import { LoginCheckStatus } from 'loginllama';

// Example status codes:
LoginCheckStatus.VALID
LoginCheckStatus.IP_ADDRESS_SUSPICIOUS
LoginCheckStatus.KNOWN_BOT
LoginCheckStatus.GEO_IMPOSSIBLE_TRAVEL
LoginCheckStatus.USER_AGENT_SUSPICIOUS
// ... and more

Error Handling

The SDK will throw errors if required parameters are missing:

try {
  const result = await loginllama.check('[email protected]');
} catch (error) {
  if (error.message.includes('IP address could not be detected')) {
    // No IP available - pass { ipAddress } or { request } explicitly
    // or use middleware()
  }
  // Consider failing open on errors to avoid blocking legitimate users
}

Best Practice: Fail open on errors to avoid blocking legitimate users during API outages:

try {
  const result = await loginllama.check(email);
  if (result.risk_score > 5) {
    // Block suspicious login
    return res.status(403).json({ error: 'Login blocked' });
  }
} catch (error) {
  console.error('LoginLlama error:', error);
  // Fail open - allow login to proceed
}

IP Detection

The SDK automatically detects IP addresses from multiple sources with priority fallback:

  1. X-Forwarded-For - Parses chain, takes first public IP (filters private IPs)
  2. CF-Connecting-IP - Cloudflare real client IP
  3. X-Real-IP - nginx proxy header
  4. True-Client-IP - Akamai/Cloudflare header
  5. Direct connection - socket.remoteAddress, req.connection.remoteAddress

Private IP Filtering: Automatically filters 10.x.x.x, 172.16-31.x.x, 192.168.x.x, 127.x.x.x, ::1, fc00::/7, fe80::/10

TypeScript Support

The SDK is written in TypeScript and includes full type definitions:

import {
  LoginLlama,
  LoginCheckResponse,
  LoginCheckStatus,
  CheckOptions,
  verifyWebhookSignature
} from 'loginllama';

Contributing

Contributions are welcome! Please open an issue or submit a pull request on GitHub.

License

GNU GPL V3 License

Support