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

authrail

v1.1.0

Published

Framework-agnostic client-side policy engine for web applications

Readme

Authrail

Table of Contents


AuthRail enables composable middleware that evaluates authentication, authorization, and contextual rules before rendering UI, navigating routes, or exposing features.

It is deterministic, linear, framework-agnostic, and has zero runtime dependencies.

Why AuthRail?

Modern frontend applications require more than simple route guards. You need a predictable policy pipeline that handles:

  • Composable access policies: Stack rules (Auth -> Role -> Context).
  • Role-based UI gating: Hide/show components based on permissions.
  • Deterministic execution: No hidden control flow or complex state machines.
  • Explicit redirect signaling: Policies emit redirect decisions; your application handles navigation.
  • Context enrichment: Middleware can update context (e.g., fetch profile after auth).

Installation

npm install authrail

Peer Dependencies (for React)

If you are using the React adapter, ensure you have the following installed:

  • react ^18.0.0 || ^19.0.0
  • react-dom ^18.0.0 || ^19.0.0

Core Concepts

AuthRail executes middleware sequentially in a pipeline. Middleware executes strictly in the order they appear in the array.

  1. Freeze Context: The input context is treated as immutable.
  2. Run Middleware: Each middleware returns a decision or context updates.
  3. Short-circuiting: The engine stops at the first deny or redirect decision.
  4. Merge Enrichment: Successful middleware can contribute new data to the context.
  5. Default Allow: If no middleware rejects, the final result is allow.

React Integration

1. Define a Rail

import { createRail, requireAuth, requireRole } from "authrail";

type AppContext = {
  user: { id: string; role: "admin" | "user" } | null;
  subscription?: string;
};

export const adminRail = createRail<AppContext>("admin-area", [
  requireAuth("/login"),
  requireRole("admin"),
]);

2. Using RailBoundary

Wrap components to enforce policies declaratively.

import { RailBoundary } from "authrail";

function Dashboard() {
  const { user } = useAuth(); // Your auth hook

  return (
    <RailBoundary
      rail={adminRail}
      context={{ user }}
      onRedirect={(to) => navigate(to)}
      fallback={<Spinner />}
      denied={<AccessDenied />}
    >
      <AdminPanel />
    </RailBoundary>
  );
}

3. Using protect() HOC

For route-level protection or component composition.

import { protect } from "authrail";

const ProtectedAdmin = protect(
  adminRail,
  () => ({ user: getCurrentUser() }), // Context provider
  (to) => navigate(to), // Redirect handler
)(AdminPage);

4. Using useRail() Hook

For imperative decision making within a component.

import { useRail } from "authrail";

function DeleteButton({ user }) {
  const { decision, status } = useRail(adminRail, { user });

  if (status === "loading") return <span>Checking permissions...</span>;
  if (decision?.type !== "allow") return null;

  return <button>Delete Record</button>;
}

Using the Core Engine in Any Environment

The core engine is framework-neutral. It can run anywhere JavaScript runs—from the browser to the server (Node.js, Edge functions, etc.).

Example: Client-Side Navigation

const result = await adminRail.evaluate({ user });

if (result.decision.type === "redirect") {
  // Use your router's navigate function
  router.push(result.decision.to);
} else if (result.decision.type === "deny") {
  showAccessDenied();
}

Example: API Authorization

AuthRail can be used as a policy layer before performing sensitive operations in a backend environment.

const result = await adminRail.evaluate({
  user: req.user,
});

if (result.decision.type !== "allow") {
  return res.status(403).json({ error: "Forbidden" });
}

Versatile Policy Enforcement

This makes AuthRail suitable for:

  • Permission enforcement before database access.
  • Feature flag evaluation across different platforms.
  • Role-based API protection.
  • Consistent policy logic shared between frontend and backend.

AuthRail is a decision engine. It emits intents, and your application decides how to act on them.

Built-in Middleware

| Middleware | Description | | :------------------- | :----------------------------------------- | | requireAuth(to) | Redirects if ctx.user is null/undefined. | | requireRole(role) | Denies if user role doesn't match. | | allowIf(predicate) | Denies if predicate returns false. | | blockIf(predicate) | Denies if predicate returns true. |


Advanced Patterns

1. Dynamic Context Enrichment

Middleware can do more than just gate access; it can fetch data and inject it into the application pipeline.

const withUserPermissions = async (ctx) => {
  if (ctx.user) {
    const permissions = await api.getPermissions(ctx.user.id);
    return { context: { permissions } }; // This is merged into the rail result context
  }
};

2. Functional Middleware Factories

Create reusable middleware that accepts configuration.

const requireFeature = (featureName: string) => {
  return allowIf((ctx) => ctx.features?.[featureName] === true);
};

// Usage
createRail("billing", [requireAuth("/login"), requireFeature("billing-v2")]);

3. Handling Multiple Paths

You can use redirect to handle complex branching logic directly in your policy.

const onboardingRail = createRail("onboarding", [
  (ctx) => {
    if (!ctx.profile.hasCompletedBio)
      return { decision: { type: "redirect", to: "/onboarding/bio" } as const };
    if (!ctx.profile.hasSelectedPlan)
      return { decision: { type: "redirect", to: "/pricing" } as const };
  },
]);

API Reference

createRail<Ctx>(name, middleware, options?)

Initializes a new policy rail.

  • name: A string identifier for debugging.
  • middleware: An array of Middleware functions.
  • options:
    • debug: Boolean to enable execution logging.

<RailBoundary />

The primary React component for declarative protection.

  • rail: The Rail instance to evaluate.
  • context: The current application state.
  • fallback: Component to show while evaluating (async).
  • denied: Component to show if the decision is deny.
  • onRedirect: Callback fired if the decision is redirect.

protect(rail, getContext, onRedirect, ...)

High-Order Component for wrapping route components or library-level exports.


Custom Middleware

You can easily create your own middleware to handle complex logic or context enrichment.

const enrichmentMiddleware = async (ctx) => {
  if (ctx.user) {
    const details = await fetchUserDetails(ctx.user.id);
    return { context: { details } }; // Updates internal rail context
  }
};

Debugging

Enable deterministic logging to trace decision-making in the console.

createRail("admin", middleware, { debug: true });

Example Output:

[AuthRail:admin] → requireAuthMiddleware
[AuthRail:admin] → requireRoleMiddleware
[AuthRail:admin] decision → allow

FAQ

Q: Can I use AuthRail with any framework?
A: Yes! While AuthRail is designed for the client-side and v1 has only a React Adapter, the core engine works in any JS environment. You can use it in Server Components or Middleware as long as you provide the relevant context.

Q: How does AuthRail handle async middleware?
A: Every middleware is awaited. The pipeline execution is sequential, ensuring that context enrichment from one middleware is available to the next.

Q: Does it support nested rails?
A: Since rails are just evaluation engines, you can easily call one rail inside another middleware, or combine them in your component logic.

Q: What happens if no middleware returns a decision?
A: AuthRail defaults to { type: "allow" }. We believe policies should be explicit about what they block, rather than blocking by default.


Demo

A complete demo application with authentication, role gating, and policy toggling is available here to help you understand and test the features:

👉 https://github.com/Rahmannugar/authrail-demo


Live Website

You can visit the live website at: https://authrail.vercel.app/


License

MIT © Rahmannugar