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

@dotbots-boutique/auth-sdk

v1.0.24

Published

Authentication SDK for DotBots marketplace apps

Downloads

602

Readme

@dotbots-boutique/auth-sdk

The official authentication and authorisation SDK for apps running on the DotBots Boutique platform.

What is DotBots Boutique?

DotBots Boutique is a marketplace platform where vibe coders build and publish apps. The platform handles user management, authentication, authorisation, payments, and infrastructure, and offers a growing set of additional services — so you can focus on building your app.


Installation

npm install @dotbots-boutique/auth-sdk

Note: This package is for browser use only. Do not install it in a Deno or Node backend. A separate @dotbots-boutique/server-sdk for backend use is coming soon.


Quick start

React

// main.tsx
import { DotBotsAuth } from '@dotbots-boutique/auth-sdk';
import { DotBotsAuthProvider } from '@dotbots-boutique/auth-sdk/react';

const auth = new DotBotsAuth({
  appId: import.meta.env.VITE_DOTBOTS_APP_ID,
  apiUrl: import.meta.env.VITE_DOTBOTS_API_URL
});

ReactDOM.createRoot(document.getElementById('root')!).render(
  <DotBotsAuthProvider auth={auth}>
    <App />
  </DotBotsAuthProvider>
);
// In any component
import { useDotBotsAuth } from '@dotbots-boutique/auth-sdk/react';

const MyComponent = () => {
  const { user, can, hasRole, fetch, logout } = useDotBotsAuth();

  return (
    <div>
      <p>Welcome, {user?.name}</p>
      {can('customers.read') && <CustomerList />}
      {hasRole('admin') && <AdminPanel />}
      <button onClick={logout}>Log out</button>
    </div>
  );
};

Custom loading and error screens:

<DotBotsAuthProvider
  auth={auth}
  loadingComponent={<MySpinner />}
  errorComponent={(error) => <MyError error={error} />}
>
  <App />
</DotBotsAuthProvider>

Without React

import { DotBotsAuth } from '@dotbots-boutique/auth-sdk';

const auth = new DotBotsAuth({
  appId: import.meta.env.VITE_DOTBOTS_APP_ID,
  apiUrl: import.meta.env.VITE_DOTBOTS_API_URL
});

await auth.initialize();

const user = await auth.getUser();
console.log(user.name, user.roles);

How authentication works

Your app can run in two modes — the SDK handles both automatically.

Inside an iframe (embedded in the DotBots Boutique marketplace):

  1. The SDK sends a DOTBOTS_READY message to the marketplace
  2. The marketplace responds with a short-lived auth code
  3. The SDK exchanges the code for an access token and refresh token

Standalone (opened directly on your app's domain):

  1. The SDK checks for a ?code= parameter in the URL
  2. If absent, it redirects the user to the DotBots login page
  3. After login, the user is redirected back with a code that the SDK exchanges for tokens

All tokens are stored in memory only — never in localStorage, sessionStorage, or cookies.


Proxy architecture

The SDK does not communicate directly with api.dotbots.ai for app-related calls. Each app server runs a shared proxy that handles caching, local database access, and request forwarding:

App (frontend) → Local proxy (per server) → api.dotbots.ai

During initialize(), the SDK fetches the proxy config from GET {apiUrl}/api/proxy/config. After that, all auth.fetch() calls are routed through the proxy automatically. If the proxy config cannot be fetched, the SDK falls back to direct communication with apiUrl.

Only GET /api/proxy/config always goes directly to apiUrl. All other calls — auth token exchange, refresh, revoke, user info, payments, and auth.fetch() — go to proxyUrl when available, falling back to apiUrl when proxy is unavailable.


X-Environment header

The SDK automatically detects the environment based on window.location.hostname:

  • Hostnames containing test-appstest
  • All other hostnames → prod

The X-Environment header is included on every request the SDK makes — auth endpoints, proxy config, and all auth.fetch() calls. No configuration is needed.


Making API calls

Always use auth.fetch() instead of the native fetch(). The SDK automatically:

  • Adds the Authorization, X-App-Id and X-Environment headers
  • Routes calls through the local proxy when available
  • Refreshes the access token when it is about to expire
  • Retries once on a 401 response
// GET
const response = await auth.fetch('/api/customers');
const customers = await response.json();

// POST
const response = await auth.fetch('/api/customers', {
  method: 'POST',
  body: JSON.stringify({ name: 'Acme' })
});

All SDK calls route via proxyUrl when available. Only GET /api/proxy/config always goes to apiUrl.


Payments

Use auth.charge() to charge a feature usage to the organisation or app budget. The app never handles token amounts, balances or budgets directly — the platform manages all of that.

const { transactionId } = await auth.charge('ai.generate', 'org');

With a custom quantity:

const { transactionId } = await auth.charge('ai.generate', 'org', 5);

Error handling

import { DotBotsAuthError } from '@dotbots-boutique/auth-sdk';

try {
  const { transactionId } = await auth.charge('ai.generate', 'org', 1);
} catch (error) {
  if (error instanceof DotBotsAuthError && error.code === 'PAYMENT_FAILED') {
    if (error.message === 'INSUFFICIENT_BALANCE') {
      // show top-up prompt
    } else if (error.message === 'BUDGET_EXCEEDED') {
      // show budget limit message
    }
  }
}

Possible PAYMENT_FAILED messages: INSUFFICIENT_BALANCE, BUDGET_EXCEEDED, FEATURE_NOT_FOUND, PAYMENT_REJECTED.


AI Gateway

The SDK provides access to the platform AI gateway. The developer never needs to know which provider or model is used — the platform handles routing, billing and rate limiting.

Non-streaming

import type { AiResponse } from '@dotbots-boutique/auth-sdk';

const response: AiResponse = await auth.ai('generate-text', {
  messages: [{ role: 'user', content: 'Summarise this document' }],
  maxTokens: 1000,
});

console.log(response.content);
console.log(response.usage.totalTokens);
console.log(response.cost.eur);

Streaming

let fullText = '';

await auth.aiStream('generate-text', {
  messages: [{ role: 'user', content: 'Write a poem' }],
}, (delta) => {
  fullText += delta;
  updateUI(fullText);
}, (response) => {
  console.log('Done — model:', response.model, 'tokens:', response.usage.totalTokens);
});

Tool calling

const response = await auth.ai('agent', {
  messages: [{ role: 'user', content: 'What is the weather in Brussels?' }],
  tools: [{
    name: 'get_weather',
    description: 'Get current weather for a city',
    parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
  }],
});

if (response.toolCalls?.length) {
  console.log(response.toolCalls[0].name, response.toolCalls[0].input);
}

Error handling

try {
  const response = await auth.ai('generate-text', { messages });
} catch (error) {
  if (error instanceof DotBotsAuthError) {
    switch (error.code) {
      case 'AI_INSUFFICIENT_BALANCE':
        // show top-up prompt
        break;
      case 'AI_FEATURE_NOT_FOUND':
        // feature not configured
        break;
      case 'AI_CALL_FAILED':
        // provider error
        break;
    }
  }
}

Checking permissions

auth.can('customers.read')                           // true/false
auth.canAll(['customers.read', 'customers.write'])   // true/false — all required
auth.canAny(['customers.read', 'invoices.read'])     // true/false — at least one
auth.hasRole('admin')                                // true/false

Events

auth.on('tokenRefreshed', () => { });
auth.on('sessionExpired', () => {
  // Show a message to the user
});
auth.on('loggedOut', () => { });
auth.on('userLoaded', () => { });
auth.on('charged', () => { });

| Event | Description | |-------|-------------| | tokenRefreshed | Access token was refreshed | | loggedOut | User logged out | | sessionExpired | Refresh token expired, user must re-authenticate | | userLoaded | User data was fetched | | charged | Successful charge — also sent as DOTBOTS_CHARGE postMessage to parent |


Error handling

import { DotBotsAuthError } from '@dotbots-boutique/auth-sdk';

try {
  await auth.initialize();
} catch (error) {
  if (error instanceof DotBotsAuthError) {
    switch (error.code) {
      case 'IFRAME_TIMEOUT':
        // Marketplace did not respond in time
        break;
      case 'UNAUTHORIZED':
        // User has no access to this app
        break;
      case 'NETWORK_ERROR':
        // API unreachable
        break;
      case 'CODE_EXPIRED':
        // Auth code was already expired or invalid
        break;
    }
  }
}

Error codes

| Code | Description | Fatal | |------|-------------|-------| | IFRAME_TIMEOUT | Marketplace did not respond within the timeout | Yes | | CODE_EXPIRED | Auth code was already expired or invalid | Yes | | UNAUTHORIZED | User has no access to this app | Yes | | REFRESH_FAILED | Token refresh failed | Yes | | NETWORK_ERROR | API unreachable | Yes | | NOT_INITIALIZED | initialize() has not been called yet | Yes | | PROXY_UNAVAILABLE | Proxy config could not be fetched | No — falls back to direct API | | PAYMENT_FAILED | Payment was rejected (402) — see message for reason | No | | AI_FEATURE_NOT_FOUND | AI feature code not configured in platform | No | | AI_PROVIDER_NOT_CONFIGURED | API key not set for AI provider | No | | AI_INSUFFICIENT_BALANCE | Organisation balance is 0 | No | | AI_CALL_FAILED | AI provider returned an error | No | | AI_STREAM_ERROR | Streaming connection failed | No |


Configuration

interface DotBotsConfig {
  appId: string;                     // Required — app ID assigned by the platform
  apiUrl: string;                    // Required — always 'https://api.dotbots.ai'
  marketplaceOrigin?: string;        // Default: 'https://dotbots.boutique'
  tokenRefreshBuffer?: number;       // Default: 60000 (ms before expiry to refresh)
  iframeTimeout?: number;            // Default: 5000 (ms to wait for auth code)
  onTokenRefreshFailed?: () => void; // Called when token refresh fails
}

DotBotsUser

interface DotBotsUser {
  id: string;
  name: string;
  email: string;
  orgId: string;
  orgName: string;
  teams: { teamId: string; teamName: string }[];
  roles: string[];
  permissions: string[];
  avatarUrl?: string;
}

API Reference

DotBotsAuth

constructor(config: DotBotsConfig)

Creates a new SDK instance.

initialize(): Promise<void>

Starts the authentication flow. Must be called before any other method.

  1. Fetches proxy config from apiUrl
  2. Detects the auth scenario (iframe or standalone)
  3. Authenticates the user

getUser(): Promise<DotBotsUser>

Returns the current user. Cached until the access token expires.

can(permission: string): boolean

Check if the user has a specific permission. Throws if getUser() hasn't been called.

canAll(permissions: string[]): boolean

Check if the user has all specified permissions.

canAny(permissions: string[]): boolean

Check if the user has at least one of the specified permissions.

hasRole(role: string): boolean

Check if the user has a specific role.

fetch(url: string, options?: RequestInit): Promise<Response>

Authenticated fetch wrapper. Routes through the proxy when available, falls back to apiUrl when not. Retries once on 401 after refreshing the token.

charge(featureCode: string, paidBy: 'org' | 'app', quantity?: number): Promise<{ transactionId: string }>

Charges a feature usage. Calls POST {proxyUrl}/payments/charge. On success, sends a DOTBOTS_CHARGE postMessage to the parent window (iframe only) and emits the charged event. Throws PAYMENT_FAILED on 402 with a message indicating the reason (INSUFFICIENT_BALANCE, BUDGET_EXCEEDED, FEATURE_NOT_FOUND, PAYMENT_REJECTED).

ai(feature: string, request: AiRequest): Promise<AiResponse>

Non-streaming AI call via the platform AI gateway. Calls POST {proxyUrl}/ai/call. Returns the full response with content, model, usage and cost.

aiStream(feature: string, request: AiRequest, onDelta: (delta: string) => void, onDone?: (response: AiResponse) => void): Promise<void>

Streaming AI call. onDelta fires for each text chunk, onDone fires with usage/cost metadata when the stream completes.

logout(): Promise<void>

Logs out the user. Revokes tokens on apiUrl, clears state, and redirects (standalone) or notifies the parent (iframe).

on(event: DotBotsAuthEvent, handler: Function): void

Register an event listener.

off(event: DotBotsAuthEvent, handler: Function): void

Remove an event listener.

useDotBotsAuth() (React)

const {
  user,       // DotBotsUser | null
  isLoading,  // boolean
  error,      // DotBotsAuthError | null
  can,        // (permission: string) => boolean
  canAll,     // (permissions: string[]) => boolean
  canAny,     // (permissions: string[]) => boolean
  hasRole,    // (role: string) => boolean
  fetch,      // auth.fetch proxied
  charge,     // auth.charge proxied
  ai,         // auth.ai proxied
  aiStream,   // auth.aiStream proxied
  logout,     // auth.logout proxied
} = useDotBotsAuth();

Backend integration

If your app has a backend, forward the three headers from the frontend request to your backend, and from your backend to the proxy.

Frontend → Proxy (direct)

Use this when your app has no backend or when the data is public within the organisation. The SDK handles everything automatically.

const response = await auth.fetch('/api/customers');

Frontend → Backend → Proxy

Use this when your backend performs extra logic before sending data to the frontend, such as validation, transformation, or aggregation of multiple proxy calls.

// Deno backend example
app.get('/api/customers', async (req) => {
  const proxyUrl = Deno.env.get('DOTBOTS_PROXY_URL');

  const response = await fetch(`${proxyUrl}/api/customers`, {
    headers: {
      'Authorization': req.headers.get('authorization') ?? '',
      'X-App-Id': req.headers.get('x-app-id') ?? '',
      'X-Environment': req.headers.get('x-environment') ?? '',
    }
  });

  return response;
});

Forward the headers exactly as received from the frontend. Do not add your own logic to the token.

Never store tokens in a database or log file, and never forward them to services outside the DotBots platform. The access token is scoped to your specific app — it cannot be used to access any other app's data.


dotbots.boutique.json

Your repository must contain a dotbots.boutique.json file. Use the env field to declare any environment variables your app needs beyond the platform defaults. The platform will ask the installer to fill these in before deployment.

{
  "dotbotsPromptVersion": "2.1.0",
  "database": true,
  "scopes": {
    "profile": { "description": "Read the user's name", "access": "required" },
    "email": { "description": "Read the user's email", "access": "optional" }
  },
  "roles": [
    { "name": "admin", "description": "Full access" }
  ],
  "permissions": [
    { "name": "customers.read", "description": "View customers" }
  ],
  "env": [
    {
      "name": "STRIPE_API_KEY",
      "description": "Stripe API key for payment processing",
      "required": true,
      "secret": true
    }
  ]
}

Platform variables (DATABASE_URL, PORT, DOTBOTS_APP_ID, DOTBOTS_PROXY_URL, DOTBOTS_API_URL) are injected automatically and do not need to be declared here.


Security

  • Tokens are stored in memory only — never persisted
  • postMessage origin is always validated — only messages from marketplaceOrigin are accepted
  • Auth codes are single-use and removed from the URL immediately after exchange
  • Tokens are never logged, not even in development mode
  • No runtime dependencies — eliminates supply chain risk entirely
  • Open source — view the source on GitHub

To report a security vulnerability, please email [email protected] instead of opening a public issue.


Exports

import { DotBotsAuth, DotBotsAuthError } from '@dotbots-boutique/auth-sdk';
import type { DotBotsUser, DotBotsConfig, DotBotsAuthEvent, DotBotsProxyConfig, AiRequest, AiResponse, AiMessage, AiTool, AiToolCall } from '@dotbots-boutique/auth-sdk';
import { DotBotsAuthProvider, useDotBotsAuth } from '@dotbots-boutique/auth-sdk/react';

License

MIT — © DotBots Boutique