@grantjs/client 1.0.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/LICENSE +21 -0
- package/README.md +417 -0
- package/dist/grant-client.d.ts +75 -0
- package/dist/grant-client.d.ts.map +1 -0
- package/dist/index.cjs +228 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +228 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/components/GrantGate.d.ts +58 -0
- package/dist/react/components/GrantGate.d.ts.map +1 -0
- package/dist/react/context.d.ts +73 -0
- package/dist/react/context.d.ts.map +1 -0
- package/dist/react/hooks/useGrant.d.ts +61 -0
- package/dist/react/hooks/useGrant.d.ts.map +1 -0
- package/dist/react/index.d.ts +9 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react.cjs +139 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.mjs +139 -0
- package/dist/react.mjs.map +1 -0
- package/dist/types.d.ts +149 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +96 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alejandro Heredia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
# @grantjs/client
|
|
2
|
+
|
|
3
|
+
Browser SDK for Grant authorization platform. Provides a lightweight client for permission checks with React hooks and components for conditional UI rendering.
|
|
4
|
+
|
|
5
|
+
**Documentation:** [Client SDK](https://github.com/logusgraphics/grant/blob/main/docs/integration/client-sdk.md) in the official docs.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **REST-based API** - Uses native `fetch`, no GraphQL client required
|
|
10
|
+
- **Automatic token refresh** - Handles 401 errors with token refresh and retry
|
|
11
|
+
- **Built-in caching** - Configurable TTL-based cache to minimize API calls
|
|
12
|
+
- **Multi-tenant support** - Dynamic scope switching for session tokens
|
|
13
|
+
- **React integration** - Hooks and components for declarative permission checks
|
|
14
|
+
- **TypeScript** - Full type safety with types from `@grantjs/schema`
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @grantjs/client
|
|
20
|
+
# or
|
|
21
|
+
pnpm add @grantjs/client
|
|
22
|
+
# or
|
|
23
|
+
yarn add @grantjs/client
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Create the Client
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { GrantClient } from '@grantjs/client';
|
|
32
|
+
|
|
33
|
+
const grant = new GrantClient({
|
|
34
|
+
apiUrl: 'https://api.your-app.com',
|
|
35
|
+
|
|
36
|
+
getAccessToken: () => localStorage.getItem('accessToken'),
|
|
37
|
+
|
|
38
|
+
// Cookie-based refresh: your callback calls POST /api/auth/refresh with credentials: 'include',
|
|
39
|
+
// then updates app token storage. Refresh token is in HttpOnly cookie; response body has only accessToken.
|
|
40
|
+
onRefreshWithCredentials: async () => {
|
|
41
|
+
const res = await fetch('https://api.your-app.com/api/auth/refresh', {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
credentials: 'include',
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) return false;
|
|
46
|
+
const { data } = await res.json();
|
|
47
|
+
if (data?.accessToken) {
|
|
48
|
+
localStorage.setItem('accessToken', data.accessToken);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
onTokenRefresh: (tokens) => {
|
|
55
|
+
localStorage.setItem('accessToken', tokens.accessToken);
|
|
56
|
+
// tokens.refreshToken may be undefined when using cookie-based refresh
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
onUnauthorized: () => {
|
|
60
|
+
window.location.href = '/login';
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Check permission
|
|
65
|
+
const canEdit = await grant.can('Document', 'Update');
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. React Setup
|
|
69
|
+
|
|
70
|
+
Wrap your app with `GrantProvider` and integrate with your auth store:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
'use client';
|
|
74
|
+
|
|
75
|
+
import { useMemo } from 'react';
|
|
76
|
+
import { GrantProvider, type GrantClientConfig } from '@grantjs/client/react';
|
|
77
|
+
import { useAuthStore } from '@/stores/auth.store';
|
|
78
|
+
|
|
79
|
+
export function AppProviders({ children }: { children: React.ReactNode }) {
|
|
80
|
+
const config = useMemo<GrantClientConfig>(
|
|
81
|
+
() => ({
|
|
82
|
+
apiUrl: process.env.NEXT_PUBLIC_API_URL!,
|
|
83
|
+
|
|
84
|
+
getAccessToken: () => useAuthStore.getState().accessToken,
|
|
85
|
+
|
|
86
|
+
// Cookie-based refresh: POST /api/auth/refresh with credentials: 'include', then update store
|
|
87
|
+
onRefreshWithCredentials: async () => {
|
|
88
|
+
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/auth/refresh`, {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
credentials: 'include',
|
|
91
|
+
});
|
|
92
|
+
if (!res.ok) return false;
|
|
93
|
+
const { data } = await res.json();
|
|
94
|
+
if (data?.accessToken) {
|
|
95
|
+
useAuthStore.getState().setAccessToken(data.accessToken);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
onTokenRefresh: (tokens) => {
|
|
102
|
+
useAuthStore.getState().setAccessToken(tokens.accessToken);
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
onUnauthorized: () => {
|
|
106
|
+
useAuthStore.getState().logout();
|
|
107
|
+
if (typeof window !== 'undefined') {
|
|
108
|
+
window.location.href = '/auth/login';
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
cache: {
|
|
113
|
+
ttl: 5 * 60 * 1000, // 5 minutes
|
|
114
|
+
prefix: 'grant',
|
|
115
|
+
},
|
|
116
|
+
}),
|
|
117
|
+
[]
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return <GrantProvider config={config}>{children}</GrantProvider>;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 3. Use useGrant with Scopes
|
|
125
|
+
|
|
126
|
+
For multi-tenant apps, pass the scope to check permissions in a specific context:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
'use client';
|
|
130
|
+
|
|
131
|
+
import { useGrant } from '@grantjs/client/react';
|
|
132
|
+
import { Tenant } from '@grantjs/schema';
|
|
133
|
+
|
|
134
|
+
interface OrganizationActionsProps {
|
|
135
|
+
organization: { id: string; name: string };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function OrganizationActions({ organization }: OrganizationActionsProps) {
|
|
139
|
+
// Scope permissions to this specific organization
|
|
140
|
+
const scope = { tenant: Tenant.Organization, id: organization.id };
|
|
141
|
+
|
|
142
|
+
// Check permissions - these call POST /api/auth/is-authorized
|
|
143
|
+
const canUpdate = useGrant('Organization', 'Update', { scope });
|
|
144
|
+
const canDelete = useGrant('Organization', 'Delete', { scope });
|
|
145
|
+
|
|
146
|
+
// Hide component entirely if user has no permissions
|
|
147
|
+
if (!canUpdate && !canDelete) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<div>
|
|
153
|
+
{canUpdate && <button>Edit</button>}
|
|
154
|
+
{canDelete && <button>Delete</button>}
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 4. Use Components
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import { GrantGate } from '@grantjs/client/react';
|
|
164
|
+
import { Tenant } from '@grantjs/schema';
|
|
165
|
+
|
|
166
|
+
function Dashboard({ projectId }: { projectId: string }) {
|
|
167
|
+
const scope = { tenant: Tenant.Organization, id: projectId };
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<div>
|
|
171
|
+
{/* Hide if no permission */}
|
|
172
|
+
<GrantGate resource="Analytics" action="Read" scope={scope}>
|
|
173
|
+
<AnalyticsWidget />
|
|
174
|
+
</GrantGate>
|
|
175
|
+
|
|
176
|
+
{/* Show fallback if denied */}
|
|
177
|
+
<GrantGate
|
|
178
|
+
resource="Settings"
|
|
179
|
+
action="Update"
|
|
180
|
+
scope={scope}
|
|
181
|
+
fallback={<p>Contact admin for access</p>}
|
|
182
|
+
>
|
|
183
|
+
<SettingsPanel />
|
|
184
|
+
</GrantGate>
|
|
185
|
+
|
|
186
|
+
{/* With loading state */}
|
|
187
|
+
<GrantGate resource="Report" action="Create" scope={scope} loading={<Spinner />}>
|
|
188
|
+
<ExportButton />
|
|
189
|
+
</GrantGate>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## API Reference
|
|
196
|
+
|
|
197
|
+
### GrantClient
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const grant = new GrantClient(config: GrantClientConfig);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### Configuration
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
interface GrantClientConfig {
|
|
207
|
+
apiUrl: string;
|
|
208
|
+
|
|
209
|
+
getAccessToken?: () => string | null | Promise<string | null>;
|
|
210
|
+
/** Called after cookie-based refresh; use to update app token storage. `tokens.refreshToken` may be undefined. */
|
|
211
|
+
onTokenRefresh?: (tokens: AuthTokens) => void | Promise<void>;
|
|
212
|
+
onUnauthorized?: () => void;
|
|
213
|
+
/** Cookie-based refresh on 401: call POST /api/auth/refresh with credentials: 'include', update token, return true on success. */
|
|
214
|
+
onRefreshWithCredentials?: () => Promise<boolean>;
|
|
215
|
+
|
|
216
|
+
fetch?: typeof fetch;
|
|
217
|
+
credentials?: RequestCredentials; // default: 'include'
|
|
218
|
+
cache?: { ttl?: number; prefix?: string };
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### Methods
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Permission checks
|
|
226
|
+
grant.can(resource, action, options?): Promise<boolean>
|
|
227
|
+
grant.hasPermission(resource, action, options?): Promise<boolean> // Alias
|
|
228
|
+
grant.isAuthorized(resource, action, options?): Promise<AuthorizationResult>
|
|
229
|
+
|
|
230
|
+
// Cache management
|
|
231
|
+
grant.clearCache(): void
|
|
232
|
+
grant.clearScopeCache(scope?): void
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### React Hooks
|
|
236
|
+
|
|
237
|
+
#### `useGrant(resource, action, options?)`
|
|
238
|
+
|
|
239
|
+
Returns a boolean by default, or an object with `isGranted` and `isLoading` when `returnLoading: true`.
|
|
240
|
+
|
|
241
|
+
**Default (boolean):**
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
import { useGrant } from '@grantjs/client/react';
|
|
245
|
+
import { Tenant } from '@grantjs/schema';
|
|
246
|
+
|
|
247
|
+
const canEdit = useGrant('Document', 'Update', {
|
|
248
|
+
scope: { tenant: Tenant.Organization, id: orgId },
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
return <div>{canEdit && <EditButton />}</div>;
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**With loading state:**
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
const { isGranted, isLoading } = useGrant('Document', 'Update', {
|
|
258
|
+
scope: { tenant: Tenant.Organization, id: orgId },
|
|
259
|
+
returnLoading: true,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (isLoading) return <Spinner />;
|
|
263
|
+
if (!isGranted) return null;
|
|
264
|
+
|
|
265
|
+
return <EditButton />;
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### Hook Options
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
interface UseGrantOptions {
|
|
272
|
+
scope?: Scope; // Multi-tenant scope override
|
|
273
|
+
enabled?: boolean; // Skip check if false (default: true)
|
|
274
|
+
useCache?: boolean; // Use cached result (default: true)
|
|
275
|
+
returnLoading?: boolean; // Return object with isGranted and isLoading (default: false)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
interface UseGrantResult {
|
|
279
|
+
isGranted: boolean; // Whether the user is granted permission
|
|
280
|
+
isLoading: boolean; // Whether the permission check is loading
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### React Components
|
|
285
|
+
|
|
286
|
+
#### `<GrantGate>`
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
<GrantGate
|
|
290
|
+
resource="Resource"
|
|
291
|
+
action="Action"
|
|
292
|
+
scope={{ tenant: Tenant.Organization, id: '...' }} // Optional
|
|
293
|
+
fallback={<FallbackComponent />} // Optional
|
|
294
|
+
loading={<LoadingSpinner />} // Optional
|
|
295
|
+
>
|
|
296
|
+
<ProtectedContent />
|
|
297
|
+
</GrantGate>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Multi-Tenant Scope Override
|
|
301
|
+
|
|
302
|
+
The Grant platform supports multi-tenant authorization with dynamic scope switching:
|
|
303
|
+
|
|
304
|
+
- **Session tokens**: Can override scope at request time (for users switching between organizations)
|
|
305
|
+
- **API key tokens**: Use their embedded scope (cannot be overridden)
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
import { useGrant } from '@grantjs/client/react';
|
|
309
|
+
import { Tenant } from '@grantjs/schema';
|
|
310
|
+
|
|
311
|
+
// User is viewing Organization A
|
|
312
|
+
const scopeA = { tenant: Tenant.Organization, id: 'org-a-id' };
|
|
313
|
+
const canEditA = useGrant('Project', 'Update', { scope: scopeA });
|
|
314
|
+
|
|
315
|
+
// User switches to Organization B
|
|
316
|
+
const scopeB = { tenant: Tenant.Organization, id: 'org-b-id' };
|
|
317
|
+
const canEditB = useGrant('Project', 'Update', { scope: scopeB });
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Available tenant types (from `@grantjs/schema`):
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
enum Tenant {
|
|
324
|
+
System = 'system',
|
|
325
|
+
Account = 'account',
|
|
326
|
+
Organization = 'organization',
|
|
327
|
+
AccountProject = 'accountProject',
|
|
328
|
+
OrganizationProject = 'organizationProject',
|
|
329
|
+
ProjectUser = 'projectUser',
|
|
330
|
+
// ... and more
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Caching
|
|
335
|
+
|
|
336
|
+
The client caches permission results by default (5 minute TTL). You can:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
// Configure TTL
|
|
340
|
+
const grant = new GrantClient({
|
|
341
|
+
apiUrl: '...',
|
|
342
|
+
cache: { ttl: 10 * 60 * 1000 }, // 10 minutes
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Bypass cache for a specific check
|
|
346
|
+
const fresh = await grant.can('Resource', 'Action', { useCache: false });
|
|
347
|
+
|
|
348
|
+
// Clear all cache
|
|
349
|
+
grant.clearCache();
|
|
350
|
+
|
|
351
|
+
// Clear cache for a specific scope
|
|
352
|
+
grant.clearScopeCache({ tenant: Tenant.Organization, id: orgId });
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Authentication Flow
|
|
356
|
+
|
|
357
|
+
1. **Access token** is sent via `Authorization: Bearer <token>` (when `getAccessToken` is provided).
|
|
358
|
+
2. **Cookies** are included by default (`credentials: 'include'`).
|
|
359
|
+
3. On **401**, the client uses **cookie-based refresh only** (no body-based refresh for security):
|
|
360
|
+
- If `onRefreshWithCredentials` is set, it is called. Your callback should call `POST /api/auth/refresh` with `credentials: 'include'`, read the new `accessToken` from the response body, update your storage, and return `true` on success.
|
|
361
|
+
- On success the client retries the original request; you may also use `onTokenRefresh` to sync token state.
|
|
362
|
+
- On failure or if `onRefreshWithCredentials` is not set, `onUnauthorized()` is called (e.g. redirect to login).
|
|
363
|
+
- The refresh token lives in an HttpOnly cookie; the API returns only `accessToken` in the refresh response body.
|
|
364
|
+
|
|
365
|
+
## Development Notes
|
|
366
|
+
|
|
367
|
+
### React Strict Mode
|
|
368
|
+
|
|
369
|
+
In development with React Strict Mode enabled (default in Next.js 13+), you'll see **2 API calls** per permission check. This is expected behavior:
|
|
370
|
+
|
|
371
|
+
1. Component mounts → effect runs → API call #1
|
|
372
|
+
2. Strict Mode unmounts component
|
|
373
|
+
3. Component remounts → effect runs → API call #2
|
|
374
|
+
|
|
375
|
+
This only happens in development. Production builds make a single call.
|
|
376
|
+
|
|
377
|
+
### Stable Scope References
|
|
378
|
+
|
|
379
|
+
The hooks automatically handle scope object reference changes. You don't need to memoize the scope object:
|
|
380
|
+
|
|
381
|
+
```tsx
|
|
382
|
+
// This is fine - hooks compare by value, not reference
|
|
383
|
+
const scope = { tenant: Tenant.Organization, id: organization.id };
|
|
384
|
+
const canEdit = useGrant('Resource', 'Action', { scope });
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## TypeScript
|
|
388
|
+
|
|
389
|
+
Full type definitions are included. Import types from the package or re-exported from `@grantjs/schema`:
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import type {
|
|
393
|
+
GrantClientConfig,
|
|
394
|
+
AuthorizationResult,
|
|
395
|
+
Permission,
|
|
396
|
+
Resource,
|
|
397
|
+
Scope,
|
|
398
|
+
Tenant,
|
|
399
|
+
} from '@grantjs/client';
|
|
400
|
+
|
|
401
|
+
// Or import Tenant enum directly from schema
|
|
402
|
+
import { Tenant } from '@grantjs/schema';
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Contributing
|
|
406
|
+
|
|
407
|
+
Contributions are welcome! Please see the [main repository](https://github.com/logusgraphics/grant) for contribution guidelines.
|
|
408
|
+
|
|
409
|
+
## Support
|
|
410
|
+
|
|
411
|
+
- **Documentation**: See the [main Grant documentation](https://github.com/logusgraphics/grant)
|
|
412
|
+
- **Issues**: Report bugs or request features on [GitHub Issues](https://github.com/logusgraphics/grant/issues)
|
|
413
|
+
- **Email**: ale@logus.graphics
|
|
414
|
+
|
|
415
|
+
## License
|
|
416
|
+
|
|
417
|
+
MIT
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { GrantClientConfig, AuthorizationResult, PermissionQueryOptions, Scope, SignInWithProjectAppOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Grant Client for browser applications
|
|
4
|
+
*
|
|
5
|
+
* Makes HTTP requests to the Grant API to check permissions
|
|
6
|
+
* and retrieve authorization data. Supports both token-based
|
|
7
|
+
* and cookie-based authentication with automatic token refresh.
|
|
8
|
+
*/
|
|
9
|
+
export declare class GrantClient {
|
|
10
|
+
private config;
|
|
11
|
+
private cache;
|
|
12
|
+
private defaultTtl;
|
|
13
|
+
constructor(config: GrantClientConfig);
|
|
14
|
+
/**
|
|
15
|
+
* Check if the current user has a specific permission
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const canEdit = await grant.can('document', 'update');
|
|
20
|
+
* if (canEdit) {
|
|
21
|
+
* // Show edit button
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
can(resource: string, action: string, options?: PermissionQueryOptions): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Alias for `can` - check if user has permission
|
|
28
|
+
*/
|
|
29
|
+
hasPermission(resource: string, action: string, options?: PermissionQueryOptions): Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* Start project-app OAuth flow (redirect only).
|
|
32
|
+
* Navigates the current window to the Grant OAuth entry page; after sign-in and consent,
|
|
33
|
+
* the user is redirected to the app's `redirect_uri` with token in the URL fragment.
|
|
34
|
+
*
|
|
35
|
+
* Requires `config.frontendUrl` and `redirectUri`.
|
|
36
|
+
*/
|
|
37
|
+
signInWithProjectApp(options: SignInWithProjectAppOptions): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Check authorization with full result details
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const result = await grant.isAuthorized('document', 'update');
|
|
44
|
+
* if (!result.authorized) {
|
|
45
|
+
* console.log('Denied:', result.reason);
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
isAuthorized(resource: string, action: string, options?: PermissionQueryOptions): Promise<AuthorizationResult>;
|
|
50
|
+
/**
|
|
51
|
+
* Clear all cached data
|
|
52
|
+
*/
|
|
53
|
+
clearCache(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Clear cached data for a specific scope
|
|
56
|
+
*/
|
|
57
|
+
clearScopeCache(scope?: Scope): void;
|
|
58
|
+
/**
|
|
59
|
+
* Make an authenticated fetch request with automatic token refresh on 401
|
|
60
|
+
*/
|
|
61
|
+
private fetchWithAuth;
|
|
62
|
+
/**
|
|
63
|
+
* Perform the actual fetch request
|
|
64
|
+
*/
|
|
65
|
+
private doFetch;
|
|
66
|
+
/**
|
|
67
|
+
* Get the current access token
|
|
68
|
+
*/
|
|
69
|
+
private getToken;
|
|
70
|
+
private buildUrl;
|
|
71
|
+
private getCacheKey;
|
|
72
|
+
private getFromCache;
|
|
73
|
+
private setCache;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=grant-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grant-client.d.ts","sourceRoot":"","sources":["../src/grant-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,EACL,2BAA2B,EAC5B,MAAM,SAAS,CAAC;AAQjB;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAkE;IAChF,OAAO,CAAC,KAAK,CAA8D;IAC3E,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,iBAAiB;IASrC;;;;;;;;;;OAUG;IACG,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC;IAK/F;;OAEG;IACG,aAAa,CACjB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,OAAO,CAAC;IAQnB;;;;;;OAMG;IACG,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB/E;;;;;;;;;;OAUG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,mBAAmB,CAAC;IA8D/B;;OAEG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACH,eAAe,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI;IAapC;;OAEG;YACW,aAAa;IAqB3B;;OAEG;YACW,OAAO;IAuBrB;;OAEG;YACW,QAAQ;IAYtB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,QAAQ;CAMjB"}
|