
Security News
Risky Biz Podcast: Making Reachability Analysis Work in Real-World Codebases
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
@polar-sh/better-auth
Advanced tools
A Better Auth plugin for integrating Polar payments and subscriptions into your authentication flow.
pnpm add better-auth @polar-sh/better-auth @polar-sh/sdk
Go to your Polar Organization Settings, and create an Organization Access Token. Add it to your environment.
# .env
POLAR_ACCESS_TOKEN=...
The Polar plugin comes with a handful additional plugins which adds functionality to your stack.
import { betterAuth } from "better-auth";
import { polar, checkout, portal, usage, webhooks } from "@polar-sh/better-auth";
import { Polar } from "@polar-sh/sdk";
const polarClient = new Polar({
accessToken: process.env.POLAR_ACCESS_TOKEN,
// Use 'sandbox' if you're using the Polar Sandbox environment
// Remember that access tokens, products, etc. are completely separated between environments.
// Access tokens obtained in Production are for instance not usable in the Sandbox environment.
server: 'sandbox'
});
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
client: polarClient,
createCustomerOnSignUp: true,
use: [
checkout({
products: [
{
productId: "123-456-789", // ID of Product from Polar Dashboard
slug: "pro" // Custom slug for easy reference in Checkout URL, e.g. /checkout/pro
}
],
successUrl: "/success?checkout_id={CHECKOUT_ID}",
authenticatedUsersOnly: true
}),
portal(),
usage(),
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET,
onCustomerStateChanged: (payload) => // Triggered when anything regarding a customer changes
onOrderPaid: (payload) => // Triggered when an order was paid (purchase, subscription renewal, etc.)
... // Over 25 granular webhook handlers
onPayload: (payload) => // Catch-all for all events
})
],
})
]
});
You will be using the BetterAuth Client to interact with the Polar functionalities.
import { createAuthClient } from "better-auth/react";
import { polarClient } from "@polar-sh/better-auth";
import { organizationClient } from "better-auth/client/plugins";
// This is all that is needed
// All Polar plugins, etc. should be attached to the server-side BetterAuth config
export const authClient = createAuthClient({
plugins: [polarClient()],
});
import { betterAuth } from "better-auth";
import {
polar,
checkout,
portal,
usage,
webhooks,
} from "@polar-sh/better-auth";
import { Polar } from "@polar-sh/sdk";
const polarClient = new Polar({
accessToken: process.env.POLAR_ACCESS_TOKEN,
// Use 'sandbox' if you're using the Polar Sandbox environment
// Remember that access tokens, products, etc. are completely separated between environments.
// Access tokens obtained in Production are for instance not usable in the Sandbox environment.
server: "sandbox",
});
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
client: polarClient,
createCustomerOnSignUp: true,
getCustomerCreateParams: ({ user }, request) => ({
metadata: {
myCustomProperty: 123,
},
}),
use: [
// This is where you add Polar plugins
],
}),
],
});
client
: Polar SDK client instancecreateCustomerOnSignUp
: Automatically create a Polar customer when a user signs upgetCustomerCreateParams
: Custom function to provide additional customer creation metadataWhen createCustomerOnSignUp
is enabled, a new Polar Customer is automatically created when a new User is added in the Better-Auth Database.
All new customers are created with an associated externalId
, which is the ID of your User in the Database. This allows us to skip any Polar <-> User mapping in your Database.
To support checkouts in your app, simply pass the Checkout plugin to the use-property.
import { polar, checkout } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout({
// Optional field - will make it possible to pass a slug to checkout instead of Product ID
products: [ { productId: "123-456-789", slug: "pro" } ],
// Relative URL to return to when checkout is successfully completed
successUrl: "/success?checkout_id={CHECKOUT_ID}",
// Wheather you want to allow unauthenticated checkout sessions or not
authenticatedUsersOnly: true,
// Enforces the theme - System-preferred theme will be set if left omitted
theme: "dark"
})
],
})
]
});
When checkouts are enabled, you're able to initialize Checkout Sessions using the checkout-method on the BetterAuth Client. This will redirect the user to the Product Checkout.
await authClient.checkout({
// Any Polar Product ID can be passed here
products: ["e651f46d-ac20-4f26-b769-ad088b123df2"],
// Or, if you setup "products" in the Checkout Config, you can pass the slug
slug: "pro",
});
Checkouts will automatically carry the authenticated User as the customer to the checkout. Email-address will be "locked-in".
If authenticatedUsersOnly
is false
- then it will be possible to trigger checkout sessions without any associated customer.
This plugin supports the Organization plugin. If you pass the organization ID to the Checkout referenceId, you will be able to keep track of purchases made from organization members.
const organizationId = (await authClient.organization.list())?.data?.[0]?.id,
await authClient.checkout({
// Any Polar Product ID can be passed here
products: ["e651f46d-ac20-4f26-b769-ad088b123df2"],
// Or, if you setup "products" in the Checkout Config, you can pass the slug
slug: 'pro',
// Reference ID will be saved as `referenceId` in the metadata of the checkout, order & subscription object
referenceId: organizationId
});
A plugin which enables customer management of their purchases, orders and subscriptions.
import { polar, checkout, portal } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout(...),
portal()
],
})
]
});
The portal-plugin gives the BetterAuth Client a set of customer management methods, scoped under authClient.customer
.
The following method will redirect the user to the Polar Customer Portal, where they can see orders, purchases, subscriptions, benefits, etc.
await authClient.customer.portal();
The portal plugin also adds a convenient state-method for retrieving the general Customer State.
const { data: customerState } = await authClient.customer.state();
The customer state object contains:
Thus, with that single object, you have all the required information to check if you should provision access to your service or not.
You can learn more about the Polar Customer State in the Polar Docs.
The portal plugin adds 3 convenient methods for listing benefits, orders & subscriptions relevant to the authenticated user/customer.
All of these methods use the Polar CustomerPortal APIs
This method only lists granted benefits for the authenticated user/customer.
const { data: benefits } = await authClient.customer.benefits.list({
query: {
page: 1,
limit: 10,
},
});
This method lists orders like purchases and subscription renewals for the authenticated user/customer.
const { data: orders } = await authClient.customer.orders.list({
query: {
page: 1,
limit: 10,
productBillingType: "one_time", // or 'recurring'
},
});
This method lists the subscriptions associated with authenticated user/customer.
const { data: subscriptions } = await authClient.customer.subscriptions.list({
query: {
page: 1,
limit: 10,
active: true,
},
});
Important - Organization Support
This will not return subscriptions made by a parent organization to the authenticated user.
However, you can pass a referenceId
to this method. This will return all subscriptions associated with that referenceId instead of subscriptions associated with the user.
So in order to figure out if a user should have access, pass the user's organization ID to see if there is an active subscription for that organization.
const organizationId = (await authClient.organization.list())?.data?.[0]?.id,
const { data: subscriptions } = await authClient.customer.subscriptions.list({
query: {
page: 1,
limit: 10,
active: true,
referenceId: organizationId
},
});
const userShouldHaveAccess = subscriptions.some(
sub => // Your logic to check subscription product or whatever.
)
A simple plugin for Usage Based Billing.
import { polar, checkout, portal, usage } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout(...),
portal(),
usage()
],
})
]
});
Polar's Usage Based Billing builds entirely on event ingestion. Ingest events from your application, create Meters to represent that usage, and add metered prices to Products to charge for it.
Learn more about Usage Based Billing in the Polar Docs.
const { data: ingested } = await authClient.usage.ingest({
event: "file-uploads",
metadata: {
uploadedFiles: 12,
},
});
The authenticated user is automatically associated with the ingested event.
A simple method for listing the authenticated user's Usage Meters, or as we call them, Customer Meters.
Customer Meter's contains all information about their consumtion on your defined meters.
const { data: customerMeters } = await authClient.usage.meters.list({
query: {
page: 1,
limit: 10,
},
});
The Webhooks plugin can be used to capture incoming events from your Polar organization.
import { polar, webhooks } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET,
onCustomerStateChanged: (payload) => // Triggered when anything regarding a customer changes
onOrderPaid: (payload) => // Triggered when an order was paid (purchase, subscription renewal, etc.)
... // Over 25 granular webhook handlers
onPayload: (payload) => // Catch-all for all events
})
],
})
]
});
Configure a Webhook endpoint in your Polar Organization Settings page. Webhook endpoint is configured at /polar/webhooks.
Add the secret to your environment.
# .env
POLAR_WEBHOOK_SECRET=...
The plugin supports handlers for all Polar webhook events:
onPayload
- Catch-all handler for any incoming Webhook eventonCheckoutCreated
- Triggered when a checkout is createdonCheckoutUpdated
- Triggered when a checkout is updatedonOrderCreated
- Triggered when an order is createdonOrderPaid
- Triggered when an order is paidonOrderRefunded
- Triggered when an order is refundedonRefundCreated
- Triggered when a refund is createdonRefundUpdated
- Triggered when a refund is updatedonSubscriptionCreated
- Triggered when a subscription is createdonSubscriptionUpdated
- Triggered when a subscription is updatedonSubscriptionActive
- Triggered when a subscription becomes activeonSubscriptionCanceled
- Triggered when a subscription is canceledonSubscriptionRevoked
- Triggered when a subscription is revokedonSubscriptionUncanceled
- Triggered when a subscription cancellation is reversedonProductCreated
- Triggered when a product is createdonProductUpdated
- Triggered when a product is updatedonOrganizationUpdated
- Triggered when an organization is updatedonBenefitCreated
- Triggered when a benefit is createdonBenefitUpdated
- Triggered when a benefit is updatedonBenefitGrantCreated
- Triggered when a benefit grant is createdonBenefitGrantUpdated
- Triggered when a benefit grant is updatedonBenefitGrantRevoked
- Triggered when a benefit grant is revokedonCustomerCreated
- Triggered when a customer is createdonCustomerUpdated
- Triggered when a customer is updatedonCustomerDeleted
- Triggered when a customer is deletedonCustomerStateChanged
- Triggered when a customer is createdFAQs
Polar integration for better-auth
The npm package @polar-sh/better-auth receives a total of 3,933 weekly downloads. As such, @polar-sh/better-auth popularity was classified as popular.
We found that @polar-sh/better-auth demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
Security News
/Research
Malicious Nx npm versions stole secrets and wallet info using AI CLI tools; Socket’s AI scanner detected the supply chain attack and flagged the malware.
Security News
CISA’s 2025 draft SBOM guidance adds new fields like hashes, licenses, and tool metadata to make software inventories more actionable.