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

@padosoft/laravel-iam-react-native

v1.1.0

Published

Thin, fail-closed React Native client and hooks for the Laravel IAM control plane.

Readme

@padosoft/laravel-iam-react-native

Thin, fail-closed React Native client and hooks for the Laravel IAM control plane.

tests npm license docs

Ask the IAM server "can this user do this?" from your React Native (or React) app — with the exact same wire contract and guarantees as the PHP and Node clients, plus React hooks that stay fail-closed while loading.

📚 Full documentation: doc.laravel-iam-react-native.padosoft.com — quickstart, hook lifecycle, fail-closed theory, the Hermes/Web Crypto caveat, ADRs, and the full API reference.

Why

  • Fail-closed by construction. Network error, timeout, 5xx, 4xx, malformed body, or unverifiable token resolves to deny. Always. Loading resolves to deny. No fail-open switch.
  • No PDP logic client-side. Every verdict comes from the server's decisions/check. The client never interprets grants.
  • React Native safe. No node:crypto, no Node built-ins. Uses the RN fetch polyfill and jose (Web Crypto / globalThis.crypto.subtle).
  • Zero runtime coupling to the Node SDK. Wire types are imported from @padosoft/laravel-iam-node with import type — completely erased at build time. No Node SDK runtime code runs in your app.
  • First-class React hooks. usePermission and useCan integrate with React context — check a permission in one line.

Install

npm install @padosoft/laravel-iam-react-native

Requires React Native >= 0.71 (Hermes with globalThis.crypto.subtle) for verifyToken. For check() / hooks only, any RN version with fetch works.

Quick start

1. Configure the client and provider

import { IamClient, IamProvider } from '@padosoft/laravel-iam-react-native';

const iam = new IamClient({
  baseUrl: 'https://iam.example.com/api/iam/v1',
  token: process.env.IAM_SERVICE_TOKEN,
  timeoutMs: 2000,
  cache: { ttlMs: 5000 },
});

export default function App() {
  const userId = useAuth().userId;

  return (
    <IamProvider client={iam} subject={{ type: 'user', id: userId }}>
      <Navigation />
    </IamProvider>
  );
}

2. Check permissions with hooks

import { usePermission } from '@padosoft/laravel-iam-react-native';

function StockAdjustButton({ warehouseId }: { warehouseId: string }) {
  const { allowed, loading } = usePermission(
    'stock.adjust',
    { type: 'warehouse', id: warehouseId },
  );

  if (loading) return <ActivityIndicator />;
  if (!allowed) return null;
  return <Button title="Adjust stock" onPress={handleAdjust} />;
}

3. Or use the client imperatively

import { IamClient } from '@padosoft/laravel-iam-react-native';

const decision = await iam.check({
  subject: { type: 'user', id: 'usr_123' },
  application: 'warehouse',
  permission: 'stock.adjust',
  resource: { type: 'warehouse', id: 'wh_milan' },
  context: { amount: 300 },
});

if (!decision.allowed) throw new Error('Forbidden');
if (decision.requiresStepUp) promptStepUp(decision.requiredAal);

Fail-closed: read this

allowed === true alone is not permission. When requiresStepUp is true, the action is only permitted at a higher AAL. The hooks apply isGranted() automatically — they only set allowed: true when the PDP allowed and no step-up is pending.

Provider & Hooks API

<IamProvider client={...} subject={...}>

| Prop | Type | Description | |------|------|-------------| | client | IamClient | Pre-configured client instance. | | subject | Subject | The authenticated user. Hooks use it automatically. | | children | ReactNode | Your app tree. |

useIam()

Returns { client, subject } from the nearest IamProvider. Throws if no provider is found.

useCan(query: DecisionQuery): PermissionState

Full-control hook — accepts a complete DecisionQuery.

const { allowed, loading, requiresStepUp } = useCan({
  subject: { type: 'user', id: userId },
  permission: 'doc.publish',
  resource: { type: 'document', id: docId },
});

usePermission(permission, resource?, extra?): PermissionState

Convenience hook — reads subject from context, you supply permission and optionally resource.

const { allowed } = usePermission('orders.approve', { type: 'order', id: orderId });

PermissionState

| Field | Type | Description | |-------|------|-------------| | allowed | boolean | true only when PDP granted AND no step-up pending. false while loading. | | loading | boolean | true while the check is in flight. | | requiresStepUp | boolean | true if a higher AAL is required. |

Client API

new IamClient(config)

| Option | Default | Description | |--------|---------|-------------| | baseUrl | required | Full API base, e.g. https://iam.example.com/api/iam/v1. | | token | — | The user's access token, sent as Authorization: Bearer. | | timeoutMs | 2000 | Per-request timeout in ms. | | retries | 0 | Retries for idempotent network errors (never on 4xx/5xx). | | cache | off | { ttlMs, maxEntries? } in-memory decision cache. | | verify | — | { issuer?, audience?, jwksUri? } defaults for verifyToken. | | fetch | globalThis.fetch | Inject a custom fetch (tests, proxies). |

Credential model — this is a PUBLIC client (no shared secret)

Unlike the server SDKs (laravel-iam-client, laravel-iam-node, laravel-iam-rust), a mobile app is a public OAuth client: it must never embed a client_secret (anything shipped in an app binary is extractable). So the client-secret rotation / self-fetch feature does not apply here — there is no secret to rotate.

Instead, obtain the token through the Authorization Code + PKCE flow (the user logs in against IAM), keep it short-lived, and refresh it with the refresh token — never with a static secret. Pass the current user access token as token; when it expires, run the refresh/re-auth flow and update the client. See Application credentials & lifecycle (§ public clients).

check(query): Promise<Decision>

POST {baseUrl}/decisions/check. Returns a normalised Decision. Never throws.

can(query): Promise<boolean>

check() reduced to the fail-safe boolean.

listResources(subject, relation): Promise<Resource[]>

ReBAC list-resources. Returns [] on any error.

verifyToken(jwt, options?): Promise<Claims>

Verifies an ES256 token against the server JWKS. Rejects with TokenVerificationError on any failure.

Managing permissions/roles? Not here — this SDK is a consumer

This package consumes decisions (it asks "can this subject do X?"); it does not own a permission catalog and does not declare or push manifests. A mobile/public client has no server-side catalog to sync. Permissions and roles are declared by the service that owns them (a Laravel app with the bridge, or a Node/Rust service, or the console) and synced there. See Keeping IAM in sync.

Ecosystem

| Package | Runtime | Description | Docs | |---------|---------|-------------|------| | laravel-iam-server | PHP/Laravel | The IAM server — the PDP itself (RBAC + ABAC + ReBAC, OAuth/OIDC, audit) | docs | | @padosoft/laravel-iam-node | Node 18+ | Core TypeScript/Node SDK — this package builds on its wire types | docs | | @padosoft/laravel-iam-react-native | React Native / React | This package | docs | | padosoft/laravel-iam-client | PHP 8.1+ | PHP client — the wire-contract reference | docs | | laravel-iam-rust | Rust | Rust client SDK (crate laravel-iam), async + blocking | docs |

License

MIT (c) Padosoft