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

@authentify2026/consent-widget

v0.5.0

Published

Embeddable React consent widget for agent authorization

Downloads

47

Readme

@authentify2026/consent-widget

React modal component for Authentify's B2B2C agent-consent flow: a bank or fintech embeds it in their own end-user-facing app so their customer can approve or decline an AI agent acting on their behalf.

For the full pilot walkthrough (sandbox signup, API keys, wiring this widget in, UTP signals, go-live), see the integration guide on the Authentify site.

Install

npm install @authentify2026/consent-widget

Requires React 16.14+ (the build uses the automatic JSX runtime, react/jsx-runtime, added in 16.14).

Usage

import AuthentifyConsentWidget from '@authentify2026/consent-widget';

export function LoanApplicationForm() {
  const [consentToken, setConsentToken] = useState('');

  return (
    <div>
      {!consentToken ? (
        <AuthentifyConsentWidget
          sessionId="sess_abc123xyz"
          apiBaseUrl="https://www.authentify.bz"
          onApprove={(token) => setConsentToken(token)}
          onDecline={() => console.log('User declined')}
        />
      ) : (
        <p>✅ Consent approved! Processing...</p>
      )}
    </div>
  );
}

sessionId comes from the widget_url/session_id your backend receives from POST /api/v1/consent-sessions.

Props

interface AuthentifyConsentWidgetProps {
  sessionId: string;
  onApprove?: (consentToken: string, expiresAt: string, agentName: string) => void;
  onDecline?: () => void;
  onError?: (error: string) => void;
  apiBaseUrl?: string;   // default: https://www.authentify.bz
  theme?: 'light' | 'dark'; // default: 'dark'
  branding?: {
    tokens?: Partial<DesignTokens>; // override any subset of the theme's colors
    logoUrl?: string;               // shown in the modal header, left of the title
    fontFamily?: string;            // default: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif'
  };
  environment?: 'sandbox' | 'production'; // default: 'sandbox'
  width?: string;        // default: '100%'
  height?: string;       // default: 'auto'
  agentName?: string;
  scope?: string[];
  expiresInDays?: number;
  authRedirectUrl?: string;
  onAuthRedirect?: (url: string) => void;
}

environment isn't in the original spec's prop list but was added because without it the widget could only ever authorize sandbox consents — every call it makes takes environment as a request field, so a bank integrating in production needs a way to set it.

agentName / scope / expiresInDays

Optional fallbacks. On open, the widget loads the session's own details from GET {apiBaseUrl}/api/v1/consent-sessions/:id/status — the agent's name, the institution's name, each requested permission in plain language, and how long access lasts — and shows those, so the end user always sees what was actually requested. These props are used only when an older API deployment doesn't return the details. Approve is not available until the details have loaded; an expired or unknown session shows "This request has expired" with nothing to approve.

What it does

  1. Loads the session, then renders a modal: who is asking (institution and agent), each permission in plain language, how long access lasts, and Approve/Decline buttons.
  2. Approve (a click on the button — there is deliberately no Enter shortcut, so a keypress meant for the host page can never grant access) → POST {apiBaseUrl}/api/v1/consent/authorize with { session_id, customer_approval: true, environment }. On success, calls onApprove(consent_token, expires_at, agent_name) and closes.
  3. Decline (button, backdrop click, Esc, or the header's close button) → calls onDecline(), closes, and records the refusal: POST {apiBaseUrl}/api/v1/consent/authorize with customer_approval: false, which writes CONSENT_DECLINED to your audit log and ends the session so the same link can't be approved later.
  4. Errors from the API surface inline in the modal and via onError.

authRedirectUrl / onAuthRedirect — identity verification

Set authRedirectUrl when the session was created with require_auth: true (POST /consent-sessions). The widget then shows a "Verify it's you" step before the approval screen instead of rendering an in-widget challenge — Authentify never touches credentials or biometric data.

<AuthentifyConsentWidget
  sessionId={sessionId}
  authRedirectUrl="https://mybank.com/verify?session=abc123"
  onAuthRedirect={(url) => { window.location.href = url; }}
  /* ...other props */
/>

Flow:

  1. Widget shows "Confirm your identity". Clicking calls onAuthRedirect(authRedirectUrl).
  2. You must implement onAuthRedirect yourself — the widget runs in a cross-origin iframe and cannot perform a top-level navigation on its own. Omitting the prop leaves the button showing an error instead of silently doing nothing.
  3. Your page does the actual window.location redirect to your own auth (OTP, MFA, whatever you already have).
  4. After the customer authenticates, your backend calls POST {apiBaseUrl}/api/v1/consent-sessions/{session_id}/confirm-auth with Authorization: Bearer <your_api_key>, server-to-server. This is deliberately not "the browser came back from a redirect" as proof — that's spoofable by hitting the return URL directly without ever authenticating.
  5. Redirect the browser back to the same page that renders the widget with the same sessionId. On mount, the widget calls GET {apiBaseUrl}/api/v1/consent-sessions/{session_id}/status to check whether step 4 happened yet, shows "✓ Verified", and moves to the approval screen.
  6. POST /consent/authorize refuses to grant (403) until confirm-auth has been called for that session — Approve will fail with "Identity verification required before approval" if you skip straight to it.

Omit authRedirectUrl entirely (the default) and the widget behaves exactly as it did before this existed — no extra network call, straight to the approval screen.

ConsentManager

The revocation screen — not part of the approval flow above, used separately (e.g. on an account-settings page):

import { ConsentManager } from '@authentify2026/consent-widget';

<ConsentManager apiBaseUrl="https://www.authentify.bz" customerToken={customerToken} />

customerToken is the end user's consent_token from POST /consent/authorize. It reaches only that end user's own consents — never other end users' at the same institution — and stops working once it expires. Lists active, paused and revoked consents (GET /customer/consents), revokes one (DELETE /customer/consents/:id), and pauses or resumes one (POST / DELETE /customer/consents/:id/pause), all authenticated via customer_token as a query parameter. While an agent is paused, Authentify denies everything it tries for that end user (consent_paused) until they resume it; pausing keeps the consent, so resuming needs no new approval.

ActivityFeed

The end user's own audit trail: every action an agent took or tried on their behalf (allowed, blocked, approved, denied, waiting for approval), and when they connected, declined, paused or disconnected agents, grouped by day, in plain language with amounts.

import { ActivityFeed } from '@authentify2026/consent-widget';

<ActivityFeed apiBaseUrl="https://www.authentify.bz" customerToken={customerToken} theme="light" />

Same customerToken as ConsentManager; reads GET /api/v1/customer/activity. Requests show up when your agent passes the end user's id as endUserId to POST /api/v2/authorize.

Styling

No CSS file to import. Styles are injected as a single <style> tag on first mount (once per page, however many widget instances you render) — this package builds with plain tsc and no bundler, so a static .css file wouldn't reliably end up in dist/ for every consumer's toolchain. Colors come from the Authentify design tokens (src/tokens.ts), switched by the theme prop, then layered with any overrides you pass via branding (see below).

Branding

Pass branding to make the modal match your own product instead of Authentify's default look — every field is optional and additive; omit it entirely and the widget renders exactly as it always has.

<AuthentifyConsentWidget
  sessionId={sessionId}
  theme="light"
  branding={{
    logoUrl: 'https://mybank.com/logo.png',
    tokens: { accentBlue: '#c0392b', accentBlueText: '#ffffff' },
    fontFamily: "'Helvetica Neue', Arial, sans-serif",
  }}
  /* ...other props */
/>
  • branding.tokens overrides any subset of the active theme's DesignTokens (src/tokens.ts) — e.g. just accentBlue to recolor the Approve button and scope pills, leaving everything else (backgrounds, borders, text colors) on the Authentify default for that theme.
  • branding.logoUrl renders an image (recommended height ~24px) to the left of the modal title in the header.
  • branding.fontFamily overrides the default 'Inter', -apple-system, BlinkMacSystemFont, sans-serif stack for the whole modal.

Try it live on the deployed demo with ?logo_url=...&accent_color=... (see "Demo" below).

Development

npm install
npm run build   # tsc -> dist/
npm run dev     # tsc --watch

Publishing

npm run build
npm publish --access public

Requires npm auth for the @authentify org (npm login / NPM_TOKEN) — not something this repo or CI has by default.

Demo (deployed to Vercel)

This package itself has no HTML/bundler — npm run build only emits dist/index.js via tsc, nothing servable. demo/ is a small separate Vite app that imports the widget from this repo directly (file:..) and renders it, so there's something Vercel can actually deploy as a static site.

npm run build:demo   # builds the library, then the demo -> demo/dist
npm run dev:demo      # same, but starts the demo's dev server

Visiting the deployed URL with no query string renders the widget against example data (sess_demo_example — not a real session, so Approve will surface whatever error the live API returns for it). Pass a real consent session via query params to test an actual one:

https://<deployment>/?session=sess_abc123&api_base_url=https://www.authentify.bz&environment=sandbox

Add &auth_redirect_url=... to also exercise the identity-verification step — the demo page implements onAuthRedirect itself (a real window.location redirect), since that's the host's responsibility, not the widget's.

vercel.json at the repo root builds the library first, then the demo, and serves demo/dist as the output directory — that's all a Vercel project pointed at this repo needs.

ActionApproval

The end user approves or denies one escalated agent action: the amount, the payee, the agent's stated reason and why they're being asked, with a countdown, then a receipt of their decision.

import { ActionApproval } from '@authentify2026/consent-widget';

<ActionApproval approvalToken={tokenFromLink} apiBaseUrl="https://www.authentify.bz" theme="light" />

When an agent with end_user_approval turned on escalates an action it is taking for a named endUserId, POST /api/v2/authorize returns endUserApproval: { url, expiresAt }. Send or show that link to the end user; the hosted page renders this component for ?approval=…. Links work once and expire (30 minutes). Your staff can still decide from the dashboard; whichever decision comes first stands. Put the agent's reason in the authorize call's context.reason so the end user sees it.