prividium 0.21.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -414,7 +414,68 @@ interface PrividiumSiweConfig {
414
414
  ```
415
415
 
416
416
  **Returns:** `PrividiumSiweChain` - with all the same methods as `PrividiumChain` (except `addNetworkToWallet` which is
417
- browser-only), plus `address` (the wallet address derived from the account).
417
+ browser-only), plus `address` (the wallet address derived from the account) and an `admin` namespace for permissions-API
418
+ administration operations.
419
+
420
+ ### Admin API: `prividium.admin.*`
421
+
422
+ The `admin` namespace on a `PrividiumSiweChain` instance wraps permissions-API endpoints that require admin
423
+ authorization. Calls go through the same authenticated transport as the chain itself, so session refresh and 401 retries
424
+ are handled automatically.
425
+
426
+ ```typescript
427
+ // Look up a user by id, then associate additional wallet addresses
428
+ const user = await prividium.admin.users.getById('usr_abc123');
429
+ const merged = [...new Set([...user.wallets.map((w) => w.walletAddress), '0xNEW_WALLET_ADDRESS'])];
430
+ await prividium.admin.users.update(user.id, { wallets: merged });
431
+
432
+ // Whitelist a freshly deployed contract against a permissions template
433
+ await prividium.admin.contracts.create({
434
+ contractAddress: '0xCONTRACT_ADDRESS',
435
+ templateKey: 'erc20-token',
436
+ abi: '[]',
437
+ name: null,
438
+ description: null,
439
+ discloseErc20TotalSupply: false,
440
+ discloseBytecode: false
441
+ });
442
+ ```
443
+
444
+ **Methods:**
445
+
446
+ | Method | HTTP | Endpoint | Description |
447
+ | -------------------------------- | ------ | ---------------- | ------------------------------------------------------------------- |
448
+ | `admin.users.getById(id)` | `GET` | `/api/users/:id` | Fetch a single user, including roles and wallet records. |
449
+ | `admin.users.update(id, params)` | `PUT` | `/api/users/:id` | Replace a user's editable fields (display name, roles, wallets, …). |
450
+ | `admin.contracts.create(params)` | `POST` | `/api/contracts` | Whitelist a contract address, optionally linked to a template. |
451
+
452
+ Types are exported from `prividium/siwe`: `AdminUser`, `AdminUserUpdate`, `AdminContract`, `AdminContractCreate`. Calls
453
+ throw on non-2xx responses; treat them like any other awaited fetch. Failed authorization (`401` / `403`) follows the
454
+ normal session-refresh path described in [Auto-Reauthentication](#auto-reauthentication).
455
+
456
+ ### Verifying a user access token: `verifyUserAccessToken(token, options)`
457
+
458
+ Stateless helper for backend services that need to validate an end-user's Prividium access token (e.g. a token forwarded
459
+ from a frontend) without setting up a SIWE chain. Calls `GET /api/profiles/me` with the bearer token and returns the
460
+ verified user id, or a typed error.
461
+
462
+ ```typescript
463
+ import { verifyUserAccessToken } from 'prividium/siwe';
464
+
465
+ const result = await verifyUserAccessToken(req.headers.authorization, {
466
+ apiUrl: 'https://permissions.prividium.io'
467
+ });
468
+
469
+ if (!result.valid) {
470
+ // result.error is 'network_error' | 'invalid_token' | 'server_error'
471
+ return res.status(401).json({ error: result.error });
472
+ }
473
+
474
+ // result.userId is the Prividium user id corresponding to the token
475
+ req.userId = result.userId;
476
+ ```
477
+
478
+ The helper accepts either a raw token (`abc123…`) or a full `Bearer abc123…` header and won't double-prefix.
418
479
 
419
480
  ### Auto-Reauthentication
420
481
 
@@ -0,0 +1,9 @@
1
+ import type { ApiCaller } from '../chain-core.js';
2
+ import type { AdminContract, AdminContractCreate } from './types.js';
3
+ export interface ContractsAdminMethods {
4
+ create(params: AdminContractCreate): Promise<AdminContract>;
5
+ }
6
+ export declare function createContractsAdminMethods(deps: {
7
+ prividiumApiCall: ApiCaller;
8
+ prividiumApiBaseUrl: string;
9
+ }): ContractsAdminMethods;
@@ -0,0 +1,10 @@
1
+ import { adminContractSchema } from './schemas.js';
2
+ export function createContractsAdminMethods(deps) {
3
+ const { prividiumApiCall, prividiumApiBaseUrl } = deps;
4
+ const url = `${prividiumApiBaseUrl}/api/contracts`;
5
+ return {
6
+ async create(params) {
7
+ return prividiumApiCall(adminContractSchema, url, 'POST', JSON.stringify(params));
8
+ }
9
+ };
10
+ }
@@ -0,0 +1,14 @@
1
+ import type { ApiCaller } from '../chain-core.js';
2
+ import { type ContractsAdminMethods } from './contracts.js';
3
+ import { type UsersAdminMethods } from './users.js';
4
+ export interface AdminMethods {
5
+ users: UsersAdminMethods;
6
+ contracts: ContractsAdminMethods;
7
+ }
8
+ export declare function createAdminMethods(deps: {
9
+ prividiumApiCall: ApiCaller;
10
+ prividiumApiBaseUrl: string;
11
+ }): AdminMethods;
12
+ export type { ContractsAdminMethods } from './contracts.js';
13
+ export type { AdminContract, AdminContractCreate, AdminUser, AdminUserUpdate } from './types.js';
14
+ export type { UsersAdminMethods } from './users.js';
@@ -0,0 +1,8 @@
1
+ import { createContractsAdminMethods } from './contracts.js';
2
+ import { createUsersAdminMethods } from './users.js';
3
+ export function createAdminMethods(deps) {
4
+ return {
5
+ users: createUsersAdminMethods(deps),
6
+ contracts: createContractsAdminMethods(deps)
7
+ };
8
+ }
@@ -0,0 +1,64 @@
1
+ import { z } from 'zod';
2
+ export declare const adminUserSchema: z.ZodObject<{
3
+ id: z.ZodString;
4
+ oidcSub: z.ZodNullable<z.ZodString>;
5
+ displayName: z.ZodString;
6
+ source: z.ZodEnum<{
7
+ oidc: "oidc";
8
+ adminPanel: "adminPanel";
9
+ tenant: "tenant";
10
+ crypto_native: "crypto_native";
11
+ m2m_app: "m2m_app";
12
+ }>;
13
+ organizationId: z.ZodNullable<z.ZodString>;
14
+ walletToken: z.ZodNullable<z.ZodString>;
15
+ createdAt: z.ZodString;
16
+ updatedAt: z.ZodString;
17
+ organization: z.ZodNullable<z.ZodObject<{
18
+ id: z.ZodString;
19
+ name: z.ZodString;
20
+ }, z.core.$strip>>;
21
+ roles: z.ZodArray<z.ZodObject<{
22
+ roleName: z.ZodString;
23
+ systemPermissions: z.ZodOptional<z.ZodArray<z.ZodEnum<{
24
+ contract_deployment: "contract_deployment";
25
+ full_sequencer_rpc_access: "full_sequencer_rpc_access";
26
+ full_read_access: "full_read_access";
27
+ admin_read: "admin_read";
28
+ org_users_manage: "org_users_manage";
29
+ org_wallets_manage: "org_wallets_manage";
30
+ org_rpc_access: "org_rpc_access";
31
+ rpc_read_eth_getBlockByNumber: "rpc_read_eth_getBlockByNumber";
32
+ rpc_read_eth_getLogs: "rpc_read_eth_getLogs";
33
+ rpc_read_eth_getTransactionByHash: "rpc_read_eth_getTransactionByHash";
34
+ rpc_read_eth_getTransactionReceipt: "rpc_read_eth_getTransactionReceipt";
35
+ check_user_read_access: "check_user_read_access";
36
+ contract_metadata_read: "contract_metadata_read";
37
+ }>>>;
38
+ isSystemRole: z.ZodOptional<z.ZodBoolean>;
39
+ createdAt: z.ZodOptional<z.ZodString>;
40
+ updatedAt: z.ZodOptional<z.ZodString>;
41
+ }, z.core.$strip>>;
42
+ wallets: z.ZodArray<z.ZodObject<{
43
+ id: z.ZodNumber;
44
+ walletAddress: z.ZodString;
45
+ userId: z.ZodString;
46
+ createdAt: z.ZodString;
47
+ updatedAt: z.ZodString;
48
+ }, z.core.$strip>>;
49
+ }, z.core.$strip>;
50
+ export declare const adminContractSchema: z.ZodObject<{
51
+ contractAddress: z.ZodString;
52
+ abi: z.ZodString;
53
+ name: z.ZodNullable<z.ZodString>;
54
+ description: z.ZodNullable<z.ZodString>;
55
+ discloseErc20TotalSupply: z.ZodBoolean;
56
+ discloseBytecode: z.ZodBoolean;
57
+ templateId: z.ZodNullable<z.ZodNumber>;
58
+ isSystemContract: z.ZodBoolean;
59
+ disclosedAddresses: z.ZodArray<z.ZodObject<{
60
+ address: z.ZodString;
61
+ }, z.core.$strip>>;
62
+ createdAt: z.ZodString;
63
+ updatedAt: z.ZodString;
64
+ }, z.core.$strip>;
@@ -0,0 +1,62 @@
1
+ import { z } from 'zod';
2
+ const systemPermissionSchema = z.enum([
3
+ 'contract_deployment',
4
+ 'full_sequencer_rpc_access',
5
+ 'full_read_access',
6
+ 'admin_read',
7
+ 'org_users_manage',
8
+ 'org_wallets_manage',
9
+ 'org_rpc_access',
10
+ 'rpc_read_eth_getBlockByNumber',
11
+ 'rpc_read_eth_getLogs',
12
+ 'rpc_read_eth_getTransactionByHash',
13
+ 'rpc_read_eth_getTransactionReceipt',
14
+ 'check_user_read_access',
15
+ 'contract_metadata_read'
16
+ ]);
17
+ const userWalletSchema = z.object({
18
+ id: z.number(),
19
+ walletAddress: z.string(),
20
+ userId: z.string(),
21
+ createdAt: z.string(),
22
+ updatedAt: z.string()
23
+ });
24
+ const userRoleSchema = z.object({
25
+ roleName: z.string(),
26
+ systemPermissions: z.array(systemPermissionSchema).optional(),
27
+ isSystemRole: z.boolean().optional(),
28
+ createdAt: z.string().optional(),
29
+ updatedAt: z.string().optional()
30
+ });
31
+ const userOrganizationSchema = z.object({
32
+ id: z.string(),
33
+ name: z.string()
34
+ });
35
+ const userSourceSchema = z.enum(['oidc', 'adminPanel', 'tenant', 'crypto_native', 'm2m_app']);
36
+ export const adminUserSchema = z.object({
37
+ id: z.string(),
38
+ oidcSub: z.string().nullable(),
39
+ displayName: z.string(),
40
+ source: userSourceSchema,
41
+ organizationId: z.string().nullable(),
42
+ walletToken: z.string().nullable(),
43
+ createdAt: z.string(),
44
+ updatedAt: z.string(),
45
+ organization: userOrganizationSchema.nullable(),
46
+ roles: z.array(userRoleSchema),
47
+ wallets: z.array(userWalletSchema)
48
+ });
49
+ const disclosedAddressSchema = z.object({ address: z.string() });
50
+ export const adminContractSchema = z.object({
51
+ contractAddress: z.string(),
52
+ abi: z.string(),
53
+ name: z.string().nullable(),
54
+ description: z.string().nullable(),
55
+ discloseErc20TotalSupply: z.boolean(),
56
+ discloseBytecode: z.boolean(),
57
+ templateId: z.number().nullable(),
58
+ isSystemContract: z.boolean(),
59
+ disclosedAddresses: z.array(disclosedAddressSchema),
60
+ createdAt: z.string(),
61
+ updatedAt: z.string()
62
+ });
@@ -0,0 +1,71 @@
1
+ export type AdminUserSource = 'oidc' | 'adminPanel' | 'tenant' | 'crypto_native' | 'm2m_app';
2
+ export type AdminSystemPermission = 'contract_deployment' | 'full_sequencer_rpc_access' | 'full_read_access' | 'admin_read' | 'org_users_manage' | 'org_wallets_manage' | 'org_rpc_access' | 'rpc_read_eth_getBlockByNumber' | 'rpc_read_eth_getLogs' | 'rpc_read_eth_getTransactionByHash' | 'rpc_read_eth_getTransactionReceipt' | 'check_user_read_access' | 'contract_metadata_read';
3
+ export interface AdminUserRole {
4
+ roleName: string;
5
+ systemPermissions?: Array<AdminSystemPermission>;
6
+ isSystemRole?: boolean;
7
+ createdAt?: string;
8
+ updatedAt?: string;
9
+ }
10
+ export interface AdminUserWallet {
11
+ id: number;
12
+ walletAddress: string;
13
+ userId: string;
14
+ createdAt: string;
15
+ updatedAt: string;
16
+ }
17
+ export interface AdminUserOrganization {
18
+ id: string;
19
+ name: string;
20
+ }
21
+ export interface AdminUser {
22
+ id: string;
23
+ oidcSub: string | null;
24
+ displayName: string;
25
+ source: AdminUserSource;
26
+ organizationId: string | null;
27
+ walletToken: string | null;
28
+ createdAt: string;
29
+ updatedAt: string;
30
+ organization: AdminUserOrganization | null;
31
+ roles: Array<AdminUserRole>;
32
+ wallets: Array<AdminUserWallet>;
33
+ }
34
+ export interface AdminUserUpdate {
35
+ id?: string;
36
+ oidcSub?: string | null;
37
+ displayName?: string;
38
+ source?: AdminUserSource;
39
+ walletToken?: string | null;
40
+ createdAt?: unknown;
41
+ updatedAt?: unknown;
42
+ roles?: Array<string>;
43
+ wallets?: Array<string>;
44
+ }
45
+ export interface AdminDisclosedAddress {
46
+ address: string;
47
+ }
48
+ export interface AdminContract {
49
+ contractAddress: string;
50
+ abi: string;
51
+ name: string | null;
52
+ description: string | null;
53
+ discloseErc20TotalSupply: boolean;
54
+ discloseBytecode: boolean;
55
+ templateId: number | null;
56
+ isSystemContract: boolean;
57
+ disclosedAddresses: Array<AdminDisclosedAddress>;
58
+ createdAt: string;
59
+ updatedAt: string;
60
+ }
61
+ export interface AdminContractCreate {
62
+ contractAddress: string;
63
+ abi: string;
64
+ name: string | null;
65
+ description: string | null;
66
+ discloseErc20TotalSupply?: boolean;
67
+ discloseBytecode?: boolean;
68
+ disclosedAddresses?: Array<AdminDisclosedAddress>;
69
+ templateId?: number | null;
70
+ templateKey?: string | null;
71
+ }
@@ -0,0 +1,5 @@
1
+ // Types mirror the permissions-api OpenAPI spec at /api/users/{id} and /api/contracts.
2
+ // Hand-written so the published declarations are self-contained. The alignment test at
3
+ // src/admin-api/types-alignment.test.ts cross-checks these against the workspace
4
+ // OpenAPI-generated types and fails typecheck on drift.
5
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { ApiCaller } from '../chain-core.js';
2
+ import type { AdminUser, AdminUserUpdate } from './types.js';
3
+ export interface UsersAdminMethods {
4
+ getById(userId: string): Promise<AdminUser>;
5
+ update(userId: string, params: AdminUserUpdate): Promise<AdminUser>;
6
+ }
7
+ export declare function createUsersAdminMethods(deps: {
8
+ prividiumApiCall: ApiCaller;
9
+ prividiumApiBaseUrl: string;
10
+ }): UsersAdminMethods;
@@ -0,0 +1,13 @@
1
+ import { adminUserSchema } from './schemas.js';
2
+ export function createUsersAdminMethods(deps) {
3
+ const { prividiumApiCall, prividiumApiBaseUrl } = deps;
4
+ const baseUrl = (id) => `${prividiumApiBaseUrl}/api/users/${encodeURIComponent(id)}`;
5
+ return {
6
+ async getById(userId) {
7
+ return prividiumApiCall(adminUserSchema, baseUrl(userId), 'GET');
8
+ },
9
+ async update(userId, params) {
10
+ return prividiumApiCall(adminUserSchema, baseUrl(userId), 'PUT', JSON.stringify(params));
11
+ }
12
+ };
13
+ }
@@ -7,7 +7,8 @@ export declare function buildChainObject(config: {
7
7
  chain: Omit<Chain, 'rpcUrls'>;
8
8
  prividiumApiBaseUrl: string;
9
9
  }): Chain;
10
- export type ApiCaller = <Schema extends ZodType>(schema: Schema, url: string, method: 'GET' | 'POST', body?: string) => Promise<z.infer<Schema>>;
10
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
11
+ export type ApiCaller = <Schema extends ZodType>(schema: Schema, url: string, method: HttpMethod, body?: string) => Promise<z.infer<Schema>>;
11
12
  export declare function createApiMethods(deps: {
12
13
  prividiumApiCall: ApiCaller;
13
14
  prividiumApiBaseUrl: string;
@@ -7,3 +7,5 @@ export { LocalStorage, TokenManager } from './storage.js';
7
7
  export { generateRandomState } from './token-utils.js';
8
8
  export type { AddNetworkParams, ContractAbiResponse, PopupOptions, PrividiumChain, PrividiumConfig, Storage, TokenData, UserProfile, UserRole } from './types.js';
9
9
  export { AUTH_ERRORS, STORAGE_KEYS } from './types.js';
10
+ export type { UserTokenVerifyError, UserTokenVerifyResult, VerifyUserAccessTokenOptions } from './verify-user-access-token.js';
11
+ export { verifyUserAccessToken } from './verify-user-access-token.js';
package/dist/sdk/index.js CHANGED
@@ -6,3 +6,4 @@ export * from './selective-disclosure/index.js';
6
6
  export { LocalStorage, TokenManager } from './storage.js';
7
7
  export { generateRandomState } from './token-utils.js';
8
8
  export { AUTH_ERRORS, STORAGE_KEYS } from './types.js';
9
+ export { verifyUserAccessToken } from './verify-user-access-token.js';
@@ -1,4 +1,5 @@
1
1
  import { type Address, type Chain, type LocalAccount, type Transport } from 'viem';
2
+ import { type AdminMethods } from './admin-api/index.js';
2
3
  import type { AuthorizeTransactionParams, AuthorizeTransactionResponse, Storage, TokenData, UserProfile } from './types.js';
3
4
  export interface PrividiumSiweConfig {
4
5
  chain: Omit<Chain, 'rpcUrls'>;
@@ -24,5 +25,6 @@ export interface PrividiumSiweChain {
24
25
  getWalletRpcUrl(): Promise<string>;
25
26
  invalidateWalletToken(): Promise<string>;
26
27
  authorizeTransaction(params: AuthorizeTransactionParams): Promise<AuthorizeTransactionResponse>;
28
+ admin: AdminMethods;
27
29
  }
28
30
  export declare function createPrividiumSiweChain(config: PrividiumSiweConfig): PrividiumSiweChain;
@@ -1,4 +1,5 @@
1
1
  import { http } from 'viem';
2
+ import { createAdminMethods } from './admin-api/index.js';
2
3
  import { buildChainObject, createApiMethods, rpcUrl } from './chain-core.js';
3
4
  import { extractResponseError, isPrividiumUnauthorizedRpcError } from './error-utils.js';
4
5
  import { PrividiumSessionError } from './errors.js';
@@ -152,6 +153,7 @@ export function createPrividiumSiweChain(config) {
152
153
  };
153
154
  };
154
155
  const apiMethods = createApiMethods({ prividiumApiCall, prividiumApiBaseUrl: config.prividiumApiBaseUrl });
156
+ const admin = createAdminMethods({ prividiumApiCall, prividiumApiBaseUrl: config.prividiumApiBaseUrl });
155
157
  return {
156
158
  chain: buildChainObject(config),
157
159
  transport,
@@ -170,6 +172,7 @@ export function createPrividiumSiweChain(config) {
170
172
  getWalletToken: apiMethods.getWalletToken,
171
173
  getWalletRpcUrl: apiMethods.getWalletRpcUrl,
172
174
  invalidateWalletToken: apiMethods.invalidateWalletToken,
173
- authorizeTransaction: apiMethods.authorizeTransaction
175
+ authorizeTransaction: apiMethods.authorizeTransaction,
176
+ admin
174
177
  };
175
178
  }
@@ -1,3 +1,4 @@
1
+ export type { AdminContract, AdminContractCreate, AdminMethods, AdminUser, AdminUserUpdate, ContractsAdminMethods, UsersAdminMethods } from './admin-api/index.js';
1
2
  export { createPrividiumClient } from './create-prividium-client.js';
2
3
  export { MemoryStorage } from './memory-storage.js';
3
4
  export { FORBIDDEN_ERROR_CODE, METHOD_NOT_FOUND_ERROR_CODE, UNAUTHORIZED_ERROR_CODE } from './rpc-error-codes.js';
@@ -8,3 +9,5 @@ export { createPrividiumSiweChain } from './siwe-chain.js';
8
9
  export { TokenManager } from './storage.js';
9
10
  export type { AuthorizeTransactionParams, AuthorizeTransactionResponse, Storage, TokenData, UserProfile, UserRole } from './types.js';
10
11
  export { AUTH_ERRORS } from './types.js';
12
+ export type { UserTokenVerifyError, UserTokenVerifyResult, VerifyUserAccessTokenOptions } from './verify-user-access-token.js';
13
+ export { verifyUserAccessToken } from './verify-user-access-token.js';
package/dist/sdk/siwe.js CHANGED
@@ -9,3 +9,4 @@ export { SiweAuth } from './siwe-auth.js';
9
9
  export { createPrividiumSiweChain } from './siwe-chain.js';
10
10
  export { TokenManager } from './storage.js';
11
11
  export { AUTH_ERRORS } from './types.js';
12
+ export { verifyUserAccessToken } from './verify-user-access-token.js';
@@ -0,0 +1,15 @@
1
+ export type UserTokenVerifyError = 'network_error' | 'invalid_token' | 'server_error';
2
+ export type UserTokenVerifyResult = {
3
+ valid: true;
4
+ userId: string;
5
+ } | {
6
+ valid: false;
7
+ error: UserTokenVerifyError;
8
+ cause?: unknown;
9
+ };
10
+ export interface VerifyUserAccessTokenOptions {
11
+ apiUrl: string;
12
+ /** Called with the underlying Error before returning a `{ valid: false }` result. */
13
+ onError?: (error: unknown) => void;
14
+ }
15
+ export declare function verifyUserAccessToken(token: string, options: VerifyUserAccessTokenOptions): Promise<UserTokenVerifyResult>;
@@ -0,0 +1,35 @@
1
+ export async function verifyUserAccessToken(token, options) {
2
+ const authorizationHeader = token.startsWith('Bearer ') ? token : `Bearer ${token}`;
3
+ let response;
4
+ try {
5
+ response = await fetch(new URL('/api/profiles/me', options.apiUrl), {
6
+ method: 'GET',
7
+ headers: {
8
+ Authorization: authorizationHeader,
9
+ 'Content-Type': 'application/json'
10
+ }
11
+ });
12
+ }
13
+ catch (cause) {
14
+ options.onError?.(cause);
15
+ return { valid: false, error: 'network_error', cause };
16
+ }
17
+ if (!response.ok) {
18
+ if (response.status === 401 || response.status === 403) {
19
+ return { valid: false, error: 'invalid_token' };
20
+ }
21
+ return { valid: false, error: 'server_error' };
22
+ }
23
+ let parsed;
24
+ try {
25
+ parsed = await response.json();
26
+ }
27
+ catch (cause) {
28
+ options.onError?.(cause);
29
+ return { valid: false, error: 'server_error', cause };
30
+ }
31
+ if (!parsed || typeof parsed !== 'object' || typeof parsed.id !== 'string') {
32
+ return { valid: false, error: 'invalid_token' };
33
+ }
34
+ return { valid: true, userId: parsed.id };
35
+ }