Skip to content

EggerMarc/better-auth-usage

Repository files navigation

@eggermarc/better-auth-usage

⚠️ Warning! This package is a work in progress! Expect breaking changes and functionality changes.

Feature and usage-based authorization plugin for BetterAuth. Provides a way to define features, track usage, apply per-plan limits, and integrate with external systems (Stripe, custom hooks, etc).

Roadmap

Below are the action items to fix known limitations of this plugin. Namely, customer management and consumption indepotency.

  • Customer table
  • Customer to feature table (for customer limits)
    • Feature
    • Follow slowly changing dimensions model
  • Consumption adapter as transaction
  • Non blocking syncUsage
  • Customer provider (Optional - considering leaving this to dev)
    • useCustomer(referenceId)

Features

  • Define features with maxLimit, minLimit, reset strategies, and metadata.
  • Apply customer-specific overrides (e.g. different limits per customer).
  • Hook into usage events (before and after).
  • Add custom authorization logic with authorizeReference.

Installation

npm add @eggermarc/better-auth-usage

Usage

Server

// server
export const auth = betterAuth({
    plugins: [usage({
        features: {
            "token-feature": {
                key: "token-feature",
                maxLimit: 1000,
                reset: "monthly",
                details: ["Number of tokens per month"],
            }
        },
        overrides: {
            "starter-plan": {
                "token-feature":{ 
                    maxLimit: 10_000,
                    hooks: {
                    after: async ({ usage, customer, feature }) => {
                        console.log(
                        `[AFTER HOOK] ${customer.referenceId} used ${usage.amount} of ${feature.key}`
                        );
                    },
                    stripeId: env.TOKEN_STARTER_ID // Can declare new fields
                },
            },
            "pro-plan": {
                "token-features": {
                    maxLimit: 1_000_000,
                    hooks: {
                        after: async ({ usage, customer, feature }) => {
                            console.log(
                            `[AFTER HOOK] ${customer.referenceId} used ${usage.amount} of ${feature.key}`
                            );
                        },
                    },
                }
            },
        }
    })]
})

Client

// client.ts
import { createAuthClient } from "better-auth/client";
import { usageClient } from "@eggermarc/better-auth-usage/client";

export const client = createAuthClient({
  plugins: [usageClient()],
});

// Example: consume usage, in your app
await client.usage.consume({
  featureKey: "token-feature",
  overrideKey: "starter-plan",
  referenceId: "123",
  amount: 1,
});

Goals

Why customer registration and not per user / organization query?

  • Generalizing customer management is not straight forward. Our goal was to not make many assumptions on the origin of the customer to let this plugin be usable for non typical use cases, like users and organizations. By giving customer registration to the dev, we allow multiple scenarios to arise, for instance per-session or per-ip limitations. We also open the door to team based usage.

Examples

Team based
const teamLimits = getTeamLimits(teamId, "token-feature") 

const customer: Customer = {
    referenceId: teamId, // Team ID,
    referenceType: "team",
    email: session.user.email, // User in team email
    name: `${session.user.name}@${teamName}`,
    // WIP featureLimits: new Record("token-feature", teamLimits)
}

await client.usage.registerCustomer(customer)

await client.usage.consume({
    featureKey: "token-feature",
    overrideKey: "team-plan",
    referenceId: teamId,
    amount: 1,
})
Session based / IP based
const customer: Customer = {
    referenceId: session.session.ipAddress ?? session.session.id,
    referenceType: "session",
}

await client.usage.registerCustomer(customer)


await client.usage.consume({
    featureKey: "token-feature",
    referenceId: session.session.ipAddress ?? session.session.id,
    amount: 1
})

In this current version, we apply to all customers the root limits, then overrides, and finally with customer specific overrides.

About

Track usage using Better Auth!

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •