Skip to content

$user permissions

The defineUser() function allows you to specify access rules, roles, and permissions for your application. It defines a centralized configuration that governs:

  • Which users can access specific routes
  • What roles are available
  • What actions roles can perform on certain resources

✅ Basic Usage

ts
const { $user } = defineUser({
    roles: {
        public: {},
        admin: {},
    },
    access: {
        public: ["/login"],
        admin: ["**"],
    },
    options: {
        collection: "users",
    },
})

🧩 Configuration Fields

roles

A map of role names (e.g. admin, user, public). Each key represents a role, and its value can be any options you define (often left empty).

The public role is given to everyone, if they are logged in or not.

ts
roles: {
  admin: {},
  user: {},
  guest: {},
}

resources

Defines the actions that roles may take on specific domain resources (e.g., invoice, project).

ts
resources: {
  invoice: ["create", "read", "update", "delete"] as const,
  project: ["read"] as const,
}

access

Route access rules per role.

  • Glob-style path matching (via minimatch)
  • "**" means full access
ts
access: {
  public: ["/login", "/about"],
  admin: ["**"],
  user: ["/dashboard/**"],
}

permissions

Fine-grained control over what roles can do with resources. Specify for each role the resource and allowed actions.

ts
permissions: {
  admin: {
    invoice: {
      create: true,
      update: true,
      delete: true,
    },
  },
  user: {
    invoice: {
      read: true,
    },
  },
}

options

Used to configure the behavior of the $user toolkit.

ts
options: {
    collection: "users" // where the Firestore user profile documents live
}

🧪 Example Config

ts
const { $user } = defineUser({
    roles: {
        admin: {},
        user: {},
    },
    resources: {
        invoice: ["create", "read", "update", "another"] as const,
    },
    access: {
        admin: ["**"],
        user: ["~/invoices/**"],
    },
    permissions: {
        admin: {
            invoice: {
                create: true,
                another: true,
            },
        },
        user: {
            invoice: {
                read: true,
            },
        },
    },
})

Defaults

By default everyone has full access. The public role is given to everyone.

ts
const { $user } = defineUser({
    roles: { public: {} },
    access: { public: ["**"] },
    options: { collection: "users" },
})

Then you can take away route permissions like this.

ts
const { $user } = defineUser({
    roles: { public: {} },
    access: { public: ["**", "!/dashboard/**"] },
    options: { collection: "users" },
})

If revoking permissions for most routes, be careful to always leave at least 1 page that is public and redirect public traffic there, it's easy to end up with a redirect loop.

🧠 Notes

  • Matching uses minimatch
  • access is path-based, used for route guards
  • permissions is resource-based, used in app logic ($user.hasPermission())
  • Roles can fallback to public by default

If you'd like we can generate a helper UI or admin panel schema from this structure too.