laravel-iam-node

Ask the IAM server “is this subject allowed to do this?” and verify its tokens — from any Node service, with the exact same wire contract and guarantees as the PHP client. No policy logic lives here: every decision is the server’s.

npm
tests
license

@padosoft/laravel-iam-node is the JavaScript/TypeScript SDK for Laravel IAM — an Identity & Authorization control plane (a Policy Decision Point, PDP) for multi-application ecosystems. Your non-PHP services still need to ask it for authorization decisions and verify the tokens it mints. This package is the thin, fail-closed client that does exactly that, and nothing more.

New here? Read this page top to bottom

In a few minutes you’ll know what this SDK is, the one rule that governs everything it does (fail-closed), the two questions it answers (may this subject act? and is this token genuine?), and where to click next. Every other page goes deeper — this one is the whole picture.


What it is — in one minute

A PDP is the component that owns authorization: your application asks “is subject S allowed to do P on resource R?” and the PDP answers allow or deny. Laravel IAM is that PDP. The decision logic — RBAC roles, ABAC conditions, ReBAC relationships, step-up requirements — lives entirely on the server.

This SDK is a thin client over two server surfaces:

  • POST {baseUrl}/decisions/check — the authorization question. You send a subject, a permission, an optional resource and context; you get back a normalised Decision.
  • JWKS token verification — the authentication question. You hand it an access/ID token; it verifies the ES256 signature and the iss / aud / exp / nbf claims against the server’s published keys.

There is no PDP logic in this package. It never interprets a grant, never evaluates a policy, never decides anything locally. It serialises your query to the exact wire format the PHP client uses, calls the server, and normalises the answer. That is the entire job — and the discipline that keeps it safe.

In one line: the shortest path from “my Node service needs to honour our IAM” to “my Node service fails closed against our IAM” — same contract as PHP, zero policy duplication.


The one rule: fail-closed

Everything in this SDK bends to a single invariant:

On any uncertainty — a network error, a timeout, a non-2xx response, a malformed body, a missing subject, an unverifiable token — the answer is deny. Never allow.

There is no fail-open switch. An unreachable PDP must never open the doors. This is not a configuration default you can flip; it is the shape of the code. Read Fail-closed by design for the full theory, threat model, and why the alternative is a latent outage-to-breach pipeline.

allowed === true is not yet permission

When a decision carries requiresStepUp: true, the action is only permitted at a higher assurance level — treat it as not yet allowed. Always reduce a decision through iam.can() / isGranted() for the fail-safe boolean. See Step-up & AAL.


The two questions it answers

May this subject act?

check() / can() ask the PDP decisions/check. You get a normalised Decision (allowed, requiresStepUp, requiredAal, policyVersion, matched, explanation). can() reduces it to the fail-safe boolean. Checking permissions →

Is this token genuine?

verifyToken() verifies an ES256 JWT against the server JWKS — signature, issuer, mandatory audience, expiry. Resolves to the claims or rejects (the fail-closed signal). Verifying tokens →


Why it’s different

Fail-closed by construction

Any network error, timeout, 5xx, 4xx, malformed body, or unverifiable token resolves to deny — never allow. No fail-open opt-out exists in the API surface.

Drop-in parity with the PHP client

Same endpoint, payload (current_aal snake-case, nulls included), Bearer auth, { data } envelope unwrap and deny-on-error semantics as the PHP HttpDecider — the server can’t tell the callers apart.

Mandatory audience on tokens

verifyToken refuses to run without an audience: jose silently skips the aud check when none is given, so a token minted for a sibling service would otherwise verify. Absent audience → reject, fail-closed.

A cache that can’t turn deny into allow

The opt-in decision cache stores the server’s verdict verbatim, expires on a short TTL, never caches transport errors, skips explain queries, and flushes wholesale on a newer policy_version. Correctness before latency.

Express & Fastify middleware

requirePermission(iam, 'stock.adjust', …) gates a route on a PDP permission. A missing subject, an unreachable PDP, or a pending step-up all respond 403 and never call next().

Zero heavy deps, ESM + CJS + types

Native fetch (Node 18+) and jose for JWKS verification — that’s the whole dependency tree. Ships ESM, CommonJS, and TypeScript declarations.


How it fits together

Your service asks; the SDK serialises and calls; the PDP decides; the SDK normalises and (for gates) reduces to a boolean. The verdict is always the server’s.

flowchart LR APP["Your Node service"] -->|"check(query)"| SDK["IamClient"] SDK -->|"POST /decisions/check<br/>Bearer token"| PDP["Laravel IAM<br/>(PDP)"] PDP -->|"{ data: Decision }"| SDK SDK -->|"normalised Decision"| APP APP -->|"verifyToken(jwt)"| SDK SDK -->|"GET /.well-known/jwks.json"| PDP SDK -->|"verified Claims / reject"| APP SDK -. "any error / timeout / non-2xx" .-> DENY["deny"] DENY --> APP

Start in 30 seconds

  1. Install

    npm install @padosoft/laravel-iam-node
    

    Requires Node 18+ (native fetch).

  2. Construct the client

    import { IamClient } from '@padosoft/laravel-iam-node';
    
    const iam = new IamClient({
      baseUrl: 'https://iam.example.com/api/iam/v1', // full API base, incl. route prefix
      token: process.env.IAM_SERVICE_TOKEN,          // OAuth2 Client Credentials service token
    });
    
  3. Ask the PDP

    if (!(await iam.can({
      subject: { type: 'user', id: 'usr_123' },
      application: 'warehouse',
      permission: 'stock.adjust',
      resource: { type: 'warehouse', id: 'wh_milan' },
    }))) {
      return res.status(403).end(); // fail-closed
    }
    

→ Quickstart · → Installation · → Core concepts


Ecosystem

This SDK is one client in the Laravel IAM family. The server is the PDP; every other package is a way to consume or extend it.

laravel-iam-server

The IAM server: identity, org, Application Registry + manifest, PDP (RBAC + ABAC + ReBAC), OAuth/OIDC, tamper-evident audit, IGA, Admin API + panel. Docs →

laravel-iam-contracts

Shared contracts/interfaces + DTOs (PDP, KeyProvider, Assurance, FeatureScope). Docs →

laravel-iam-client

The Laravel client for consumer apps: OIDC login, JWT/JWKS verify, introspection, iam.auth/iam.can middleware, Gate adapter, policy cache, webhook receiver. Docs →

laravel-iam-react-native

React Native client SDK (@padosoft/laravel-iam-react-native), thin + hooks. Docs →

laravel-iam-rust

Rust client SDK (crate laravel-iam), async + blocking, fail-closed. Docs →

More modules

laravel-iam-ai (advisory-only AI), laravel-iam-directory (LDAP/AD), laravel-iam-bridge-spatie-permission (migration bridge). Org →


Where to go next

Quickstart

A working Express route gated on a PDP permission, end to end. Open →

Fail-closed by design

The invariant, the threat model, and why every error path funnels to deny. Read →

IamClient API

Every constructor option and method, with exact types and behaviour. Reference →

Package facts

npm @padosoft/laravel-iam-node · Node >=18 · deps: jose only · ESM + CJS + types · MIT ·
GitHub · npm