@passportauth/elements
v0.1.0
Published
PassportAuth Elements — drop-in embeddable UI components for access control
Downloads
88
Maintainers
Readme
@passportauth/elements
Embeddable React UI components for PassportAuth authorization.
Drop these components into any React app to add permission-aware UI, user profile management, and access control panels — without building auth logic from scratch. Each component talks directly to the PassportAuth API; no Redux, no context provider required.
Security note:
PermissionGuardperforms client-side permission checks and is intended for UI feedback only. For security-critical decisions, enforce permissions server-side using the PassportAuth Check API.
Installation
npm install @passportauth/elementsPeer dependencies
npm install react@^18Configuration
Every component accepts a config prop of type ElementsConfig:
import type { ElementsConfig } from '@passportauth/elements';
const elementsConfig: ElementsConfig = {
apiUrl: 'https://auth.passportauth.com',
tenantId: 'ten_your_tenant_id',
// serviceToken is optional — omit for public (user-facing) components,
// include for components embedded in admin surfaces.
serviceToken: process.env.NEXT_PUBLIC_PA_SERVICE_TOKEN,
};You can create this object once and share it across all components on a page.
Quick start
Settings page with UserProfile
// src/pages/settings.tsx
import { UserProfile } from '@passportauth/elements';
const elementsConfig = {
apiUrl: 'https://auth.passportauth.com',
tenantId: 'ten_acme',
};
export default function SettingsPage({ userId }: { userId: string }) {
return (
<div className="max-w-lg mx-auto py-8">
<h1 className="text-xl font-semibold mb-6">Your profile</h1>
<UserProfile
userId={userId}
config={elementsConfig}
editable
onSave={async (updated) => {
console.log('Profile saved:', updated);
}}
/>
</div>
);
}Conditionally showing UI with PermissionGuard
import { PermissionGuard } from '@passportauth/elements';
// Only show the "Delete" button to users with the `owner` relation on the document.
function DocumentActions({ userId, documentId }: { userId: string; documentId: string }) {
return (
<div>
<button>View</button>
<PermissionGuard
user={userId}
relation="editor"
object={`document:${documentId}`}
config={elementsConfig}
fallback={null}
loadingFallback={<span>Checking permissions…</span>}
>
<button>Edit</button>
</PermissionGuard>
<PermissionGuard
user={userId}
relation="owner"
object={`document:${documentId}`}
config={elementsConfig}
>
<button className="text-red-600">Delete</button>
</PermissionGuard>
</div>
);
}Sharing a resource with ResourceSharing
import { ResourceSharing } from '@passportauth/elements';
function ShareDialog({ currentUserId, documentId }: { currentUserId: string; documentId: string }) {
return (
<ResourceSharing
object={`document:${documentId}`}
currentUser={currentUserId}
config={elementsConfig}
availableRelations={['viewer', 'editor', 'owner']}
title="Share document"
onChange={() => {
// Refetch your own data after membership changes
console.log('Membership updated');
}}
/>
);
}Components
UserProfile
Displays a user's profile — avatar, name, email, verification status, and
account state. Set editable to show an inline edit form.
<UserProfile
userId="usr_abc123"
config={elementsConfig}
editable={true}
onSave={async (data) => { /* Partial<UserProfileData> */ }}
className="my-custom-class"
/>Props:
| Prop | Type | Default | Description |
|---|---|---|---|
| userId | string | required | PassportAuth user ID |
| config | ElementsConfig | required | API config |
| editable | boolean | false | Show edit form |
| onSave | (data: Partial<UserProfileData>) => Promise<void> | — | Called after a successful save |
| className | string | — | CSS class for the outer element |
PermissionGuard
Renders children when the specified permission check passes, fallback
otherwise. Calls POST /authz/check on the PassportAuth API.
<PermissionGuard
user="usr_abc123"
relation="write"
object="document:doc_xyz"
config={elementsConfig}
fallback={<p>Read-only</p>}
loadingFallback={<Spinner />}
context={{ resource_owner: 'org_acme' }}
>
<EditButton />
</PermissionGuard>Props:
| Prop | Type | Default | Description |
|---|---|---|---|
| user | string | required | User URN or ID |
| relation | string | required | Permission / relation name |
| object | string | required | Object in <type>:<id> format |
| config | ElementsConfig | required | API config |
| children | ReactNode | required | Rendered when allowed |
| fallback | ReactNode | null | Rendered when denied or error |
| loadingFallback | ReactNode | null | Rendered while checking |
| context | Record<string, unknown> | — | ABAC evaluation context |
ResourceSharing
Panel that lists who has access to a resource and lets owners add or remove
members. Calls /authz/list-users, /authz/check, and /management/tuples.
<ResourceSharing
object="project:proj_123"
currentUser="usr_admin"
config={elementsConfig}
availableRelations={['viewer', 'editor', 'owner']}
title="Manage access"
onChange={() => refetch()}
className="shadow-md"
/>Props:
| Prop | Type | Default | Description |
|---|---|---|---|
| object | string | required | Resource in <type>:<id> format |
| currentUser | string | required | Authenticated user ID |
| config | ElementsConfig | required | API config |
| availableRelations | string[] | ['viewer', 'editor', 'owner'] | Relations to display and assign |
| title | string | 'Share' | Panel heading |
| onChange | () => void | — | Called after any membership change |
| className | string | — | CSS class for the outer element |
The component automatically checks whether currentUser has the owner
relation before showing add/remove controls.
AccessRequest
Lets a user submit a request to access a resource. Calls
POST /management/access-requests.
<AccessRequest
requesterUser="usr_abc123"
object="document:doc_xyz"
relation="editor"
config={elementsConfig}
onSubmit={async (request) => {
console.log('Request submitted:', request.id);
}}
/>Props:
| Prop | Type | Default | Description |
|---|---|---|---|
| requesterUser | string | required | User requesting access |
| object | string | required | Resource in <type>:<id> format |
| relation | string | required | Requested permission level |
| config | ElementsConfig | required | API config |
| onSubmit | (data: AccessRequestData) => Promise<void> | — | Called after successful submission |
UserRolesPanel
Displays and manages the roles assigned to a user across one or more objects.
Calls /management/tuples to list and modify role assignments.
<UserRolesPanel
userId="usr_abc123"
config={elementsConfig}
editable={isAdmin}
onChange={() => refetch()}
/>Props:
| Prop | Type | Default | Description |
|---|---|---|---|
| userId | string | required | User whose roles to display |
| config | ElementsConfig | required | API config |
| editable | boolean | false | Allow adding/removing roles |
| onChange | () => void | — | Called after any role change |
| className | string | — | CSS class for the outer element |
Types
import type {
ElementsConfig,
ElementsTheme,
UserProfileData,
RoleAssignment,
AccessRequestData,
} from '@passportauth/elements';
interface ElementsConfig {
apiUrl: string;
tenantId: string;
serviceToken?: string;
}
interface ElementsTheme {
primaryColor?: string;
fontFamily?: string;
borderRadius?: string;
colorScheme?: 'light' | 'dark' | 'auto';
}
interface UserProfileData {
id: string;
email: string;
name?: string;
avatar_url?: string;
email_verified: boolean;
is_active: boolean;
}
interface RoleAssignment {
user: string;
relation: string;
object: string;
}
interface AccessRequestData {
id?: string;
requester_user: string;
relation: string;
object: string;
status: 'pending' | 'approved' | 'denied';
requested_at?: string;
}Styling
All components render unstyled inline styles by default — no CSS file to import.
Pass a className prop to override the outer container. Component internals use
inline styles built with system-ui as the base font and a neutral palette that
works in both light and dark contexts.
Custom theming via ElementsTheme is planned for a future release.
License
Apache 2.0 — see LICENSE.
