Skip to content

[Feature]: Scoped API tokens — issue a token with a subset of its owner's permissions #19618

Description

@shanehughes-ui

Snipe-IT Version

v8.6.3 - build 23252-gfffcfd2a3a

How can we help?

Let an API token be issued with a subset of its owner's permissions, rather than always inheriting all of them.

A feature request rather than a vulnerability report. I have opened two smaller, related issues — #19616 (per-token usage visibility) and #19617 (optional source-IP restriction); this is the largest of the three and the one most likely to need a design discussion before any code, which is why I am raising it separately.

Today

Personal access tokens are issued through Laravel Passport with no scopes, so a token carries everything its owning user can do. The only way to narrow a token is to create a dedicated user with a restricted role and issue the token as that user.

That works, and it is what careful deployments do — but it has real friction:

  • every integration needs its own user account, which consumes a user row, shows up in user lists and reports, and needs maintaining;
  • role permissions are per-model CRUD, so the narrowest useful read token is still "read every asset", not "read assets";
  • rotating or narrowing one integration means editing a user, which is a heavier and more error-prone operation than reissuing a token;
  • the relationship between "this token" and "what it may do" is indirect, so reviewing it means chasing through a user to a role.

What would help

Scope (or "ability") selection at token creation, enforced on the API guard:

Create API token
  Name:       [ Asset sync (read only)          ]
  Abilities:  [x] hardware:read
              [ ] hardware:write
              [x] users:read
              [ ] users:write
              ...

Passport supports scopes natively, so the machinery largely exists; the work is defining a sensible ability vocabulary, surfacing it in the token UI, and enforcing it in the API middleware as an intersection with the user's existing permissions — a token must never be able to grant more than its owner has.

Design questions I would want maintainers to settle first

These are why I am opening an issue rather than a PR:

  1. Granularity. Per-model read/write (hardware:read) is probably the right first cut — it maps onto the existing permission names and is understandable. Finer than that (per-field, per-status) gets hard to present and hard to reason about.
  2. Backwards compatibility. Existing tokens have no scopes and must keep working with full permissions, so "no abilities recorded" has to mean "all", not "none". That default is the opposite of what you would choose on a clean slate, and worth being explicit about.
  3. Where enforcement lives — middleware on the API routes, a Passport CheckScopes-style guard, or in the existing authorisation layer.
  4. Interaction with full multiple company support, which already scopes what a user can see.

What it would and would not solve

Worth being honest about the limits, since it is easy to over-sell: a scoped read token is still read-only across every record of that type. It shrinks the blast radius of a leaked credential from "everything this user can do" to "reading assets", which is a genuine and worthwhile reduction — but it is not per-record authorisation, and deployments that need that will still need to keep the credential out of untrusted places entirely.

Willing to help

I am happy to do the implementation work if the direction and the four questions above are settled. I would not send an unsolicited PR against the auth layer of a project this size.

Code of Conduct

  • I agree to follow this project's Code of Conduct

Related, filed together as a small set on API token hardening — each stands alone:
#19616 (record token usage) · #19617 (source-IP restriction) · #19618 (scoped tokens).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions