authvital-sdk 0.1.1-dev.3.cefb119.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 +657 -0
- package/dist/chunk-ETJ5ICJ7.mjs +412 -0
- package/dist/chunk-FXVD4Y5G.js +412 -0
- package/dist/chunk-JNEJMHGA.mjs +235 -0
- package/dist/chunk-JPODZIZT.mjs +95 -0
- package/dist/chunk-QPYBK2J4.js +235 -0
- package/dist/chunk-R4OHZZQP.js +95 -0
- package/dist/index.d.mts +615 -0
- package/dist/index.d.ts +615 -0
- package/dist/index.js +1299 -0
- package/dist/index.mjs +1299 -0
- package/dist/invitations-EFJA5C6L.mjs +22 -0
- package/dist/invitations-LZHJ3AZY.js +22 -0
- package/dist/oauth-K7E7OCWI.js +34 -0
- package/dist/oauth-UAFXEKZ7.mjs +34 -0
- package/dist/server.d.mts +2656 -0
- package/dist/server.d.ts +2656 -0
- package/dist/server.js +2915 -0
- package/dist/server.mjs +2915 -0
- package/dist/webhook-router-DdfXLtHa.d.mts +461 -0
- package/dist/webhook-router-DdfXLtHa.d.ts +461 -0
- package/package.json +71 -0
|
@@ -0,0 +1,2656 @@
|
|
|
1
|
+
import { y as AuthVitalEventHandler, f as SubjectCreatedEvent, g as SubjectUpdatedEvent, h as SubjectDeletedEvent, i as SubjectDeactivatedEvent, M as MemberJoinedEvent, j as MemberLeftEvent, k as MemberRoleChangedEvent, A as AppAccessGrantedEvent, n as AppAccessRevokedEvent, p as AppAccessRoleChangedEvent } from './webhook-router-DdfXLtHa.mjs';
|
|
2
|
+
export { o as AppAccessDeactivatedEvent, D as AppAccessEventHandler, K as IAppAccessEventHandler, F as IAuthVitalEventHandler, G as IInviteEventHandler, N as ILicenseEventHandler, J as IMemberEventHandler, H as ISubjectEventHandler, b as InviteAcceptedEvent, I as InviteCreatedEvent, c as InviteDeletedEvent, z as InviteEventHandler, d as InviteExpiredEvent, L as LicenseAssignedEvent, r as LicenseChangedEvent, E as LicenseEventHandler, q as LicenseRevokedEvent, m as MemberActivatedEvent, C as MemberEventHandler, l as MemberSuspendedEvent, x as SYNC_EVENT_TYPES, e as SubjectData, B as SubjectEventHandler, S as SyncEvent, a as SyncEventType, W as WebhookRouter, O as WebhookRouterOptions, P as WebhookVerifier, Q as WebhookVerifierOptions, v as isAppAccessEvent, s as isInviteEvent, w as isLicenseEvent, u as isMemberEvent, t as isSubjectEvent } from './webhook-router-DdfXLtHa.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @authvital/sdk - JWT Validator
|
|
6
|
+
*
|
|
7
|
+
* Validates JWTs issued by AuthVital using JWKS (JSON Web Key Set).
|
|
8
|
+
* Automatically fetches keys from the IDP's well-known endpoint,
|
|
9
|
+
* handles caching, and supports key rotation.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { createJwtValidator } from '@authvital/sdk/server';
|
|
14
|
+
*
|
|
15
|
+
* const validator = createJwtValidator({
|
|
16
|
+
* authVitalHost: process.env.AV_HOST,
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Validate a token
|
|
20
|
+
* const payload = await validator.validateToken(token);
|
|
21
|
+
*
|
|
22
|
+
* // Get public keys for manual verification
|
|
23
|
+
* const keys = await validator.getPublicKeys();
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
interface JwtValidatorConfig {
|
|
27
|
+
/** AuthVital IDP URL (e.g., "https://auth.example.com") */
|
|
28
|
+
authVitalHost: string;
|
|
29
|
+
/** Cache TTL in seconds (default: 3600 = 1 hour) */
|
|
30
|
+
cacheTtl?: number;
|
|
31
|
+
/** Expected audience (client_id) - optional but recommended */
|
|
32
|
+
audience?: string;
|
|
33
|
+
/** Expected issuer - defaults to authVitalHost */
|
|
34
|
+
issuer?: string;
|
|
35
|
+
}
|
|
36
|
+
interface JwksKey {
|
|
37
|
+
kty: string;
|
|
38
|
+
kid: string;
|
|
39
|
+
use?: string;
|
|
40
|
+
alg?: string;
|
|
41
|
+
n?: string;
|
|
42
|
+
e?: string;
|
|
43
|
+
x5c?: string[];
|
|
44
|
+
}
|
|
45
|
+
interface Jwks {
|
|
46
|
+
keys: JwksKey[];
|
|
47
|
+
}
|
|
48
|
+
interface JwtHeader {
|
|
49
|
+
alg: string;
|
|
50
|
+
typ?: string;
|
|
51
|
+
kid?: string;
|
|
52
|
+
}
|
|
53
|
+
interface JwtPayload {
|
|
54
|
+
iss?: string;
|
|
55
|
+
sub?: string;
|
|
56
|
+
aud?: string | string[];
|
|
57
|
+
exp?: number;
|
|
58
|
+
nbf?: number;
|
|
59
|
+
iat?: number;
|
|
60
|
+
jti?: string;
|
|
61
|
+
[key: string]: unknown;
|
|
62
|
+
}
|
|
63
|
+
interface ValidateTokenResult {
|
|
64
|
+
valid: boolean;
|
|
65
|
+
payload?: JwtPayload;
|
|
66
|
+
error?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* JWT Validator for AuthVital tokens
|
|
70
|
+
*
|
|
71
|
+
* Fetches JWKS from the IDP, caches keys, and validates tokens.
|
|
72
|
+
* Handles key rotation automatically by refetching JWKS when a key is not found.
|
|
73
|
+
*/
|
|
74
|
+
declare class JwtValidator {
|
|
75
|
+
private config;
|
|
76
|
+
private jwksCache;
|
|
77
|
+
private jwksCacheTime;
|
|
78
|
+
private fetchPromise;
|
|
79
|
+
constructor(config: JwtValidatorConfig);
|
|
80
|
+
/**
|
|
81
|
+
* Get the JWKS URL for this IDP
|
|
82
|
+
*/
|
|
83
|
+
getJwksUrl(): string;
|
|
84
|
+
/**
|
|
85
|
+
* Get the OpenID Configuration URL
|
|
86
|
+
*/
|
|
87
|
+
getOpenIdConfigUrl(): string;
|
|
88
|
+
/**
|
|
89
|
+
* Fetch public keys from the JWKS endpoint
|
|
90
|
+
* Results are cached according to cacheTtl
|
|
91
|
+
*/
|
|
92
|
+
getPublicKeys(forceRefresh?: boolean): Promise<Jwks>;
|
|
93
|
+
/**
|
|
94
|
+
* Fetch JWKS from the IDP
|
|
95
|
+
*/
|
|
96
|
+
private fetchJwks;
|
|
97
|
+
/**
|
|
98
|
+
* Find a key by its ID (kid)
|
|
99
|
+
*/
|
|
100
|
+
private findKey;
|
|
101
|
+
/**
|
|
102
|
+
* Decode a JWT without verification (to get header/payload)
|
|
103
|
+
*/
|
|
104
|
+
decodeToken(token: string): {
|
|
105
|
+
header: JwtHeader;
|
|
106
|
+
payload: JwtPayload;
|
|
107
|
+
} | null;
|
|
108
|
+
/**
|
|
109
|
+
* Validate a JWT token
|
|
110
|
+
*
|
|
111
|
+
* @param token - The JWT to validate
|
|
112
|
+
* @returns Validation result with payload if valid
|
|
113
|
+
*/
|
|
114
|
+
validateToken(token: string): Promise<ValidateTokenResult>;
|
|
115
|
+
/**
|
|
116
|
+
* Verify RS256 signature using Web Crypto API
|
|
117
|
+
*/
|
|
118
|
+
private verifySignature;
|
|
119
|
+
/**
|
|
120
|
+
* Base64URL decode to string
|
|
121
|
+
*/
|
|
122
|
+
private base64UrlDecode;
|
|
123
|
+
/**
|
|
124
|
+
* Base64URL decode to Uint8Array
|
|
125
|
+
*/
|
|
126
|
+
private base64UrlDecodeToBuffer;
|
|
127
|
+
/**
|
|
128
|
+
* Clear the JWKS cache (useful for testing or forced refresh)
|
|
129
|
+
*/
|
|
130
|
+
clearCache(): void;
|
|
131
|
+
/**
|
|
132
|
+
* Check if the JWT payload has a specific tenant permission
|
|
133
|
+
*
|
|
134
|
+
* @param payload - Decoded JWT payload
|
|
135
|
+
* @param permission - Permission to check (e.g., 'licenses:manage')
|
|
136
|
+
* @returns true if user has the permission (wildcards supported)
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* const { user } = await validator.getCurrentUser(authHeader);
|
|
141
|
+
* if (validator.hasTenantPermission(user, 'licenses:manage')) {
|
|
142
|
+
* // User can manage licenses
|
|
143
|
+
* }
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
hasTenantPermission(payload: JwtPayload, permission: string): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Check if the JWT payload has a specific app permission
|
|
149
|
+
*
|
|
150
|
+
* @param payload - Decoded JWT payload
|
|
151
|
+
* @param permission - Permission to check (e.g., 'projects:create')
|
|
152
|
+
* @returns true if user has the permission (wildcards supported)
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* const { user } = await validator.getCurrentUser(authHeader);
|
|
157
|
+
* if (validator.hasAppPermission(user, 'projects:create')) {
|
|
158
|
+
* // User can create projects
|
|
159
|
+
* }
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
hasAppPermission(payload: JwtPayload, permission: string): boolean;
|
|
163
|
+
/**
|
|
164
|
+
* Check if the JWT payload has a specific feature enabled
|
|
165
|
+
*
|
|
166
|
+
* This reads from the `license.features` array in the JWT.
|
|
167
|
+
* No API call needed - feature information is embedded in the token!
|
|
168
|
+
*
|
|
169
|
+
* @param payload - Decoded JWT payload
|
|
170
|
+
* @param featureKey - Feature to check (e.g., 'sso', 'audit_logs')
|
|
171
|
+
* @returns true if feature is enabled
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```ts
|
|
175
|
+
* const { user } = await validator.getCurrentUser(authHeader);
|
|
176
|
+
* if (validator.hasFeature(user, 'sso')) {
|
|
177
|
+
* // User's tenant has SSO enabled
|
|
178
|
+
* }
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
hasFeature(payload: JwtPayload, featureKey: string): boolean;
|
|
182
|
+
/**
|
|
183
|
+
* Get the license type from JWT payload
|
|
184
|
+
*
|
|
185
|
+
* @param payload - Decoded JWT payload
|
|
186
|
+
* @returns License type slug (e.g., 'pro', 'enterprise') or null
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* const { user } = await validator.getCurrentUser(authHeader);
|
|
191
|
+
* const licenseType = validator.getLicenseType(user);
|
|
192
|
+
* if (licenseType === 'enterprise') {
|
|
193
|
+
* // Show enterprise features
|
|
194
|
+
* }
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
getLicenseType(payload: JwtPayload): string | null;
|
|
198
|
+
/**
|
|
199
|
+
* Check if wildcard pattern matches permission
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* - '*' matches everything
|
|
203
|
+
* - 'licenses:*' matches 'licenses:manage', 'licenses:view', etc.
|
|
204
|
+
* - 'licenses:manage' only matches 'licenses:manage'
|
|
205
|
+
*/
|
|
206
|
+
private matchesWildcard;
|
|
207
|
+
/**
|
|
208
|
+
* Get the current user from an Authorization header.
|
|
209
|
+
* This is the helper for implementing `/api/auth/me` endpoints.
|
|
210
|
+
*
|
|
211
|
+
* - Does NOT call the IDP
|
|
212
|
+
* - Validates JWT signature using cached JWKS
|
|
213
|
+
* - Returns the decoded JWT payload
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```ts
|
|
217
|
+
* const validator = createJwtValidator({ authVitalHost: process.env.AV_HOST });
|
|
218
|
+
*
|
|
219
|
+
* // GET /api/auth/me
|
|
220
|
+
* app.get('/api/auth/me', async (req, res) => {
|
|
221
|
+
* const result = await validator.getCurrentUser(req.headers.authorization);
|
|
222
|
+
*
|
|
223
|
+
* if (!result.authenticated) {
|
|
224
|
+
* return res.status(401).json({ error: result.error });
|
|
225
|
+
* }
|
|
226
|
+
*
|
|
227
|
+
* res.json(result.user);
|
|
228
|
+
* });
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
getCurrentUser(authorizationHeader: string | null | undefined): Promise<GetCurrentUserResult$1>;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Create a JWT validator instance
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```ts
|
|
238
|
+
* const validator = createJwtValidator({
|
|
239
|
+
* authVitalHost: 'https://auth.example.com',
|
|
240
|
+
* audience: 'my-client-id', // optional but recommended
|
|
241
|
+
* });
|
|
242
|
+
*
|
|
243
|
+
* const result = await validator.validateToken(token);
|
|
244
|
+
* if (result.valid) {
|
|
245
|
+
* console.log('User:', result.payload.sub);
|
|
246
|
+
* }
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
declare function createJwtValidator(config: JwtValidatorConfig): JwtValidator;
|
|
250
|
+
interface GetCurrentUserResult$1 {
|
|
251
|
+
/** Whether the request is authenticated with a valid token */
|
|
252
|
+
authenticated: boolean;
|
|
253
|
+
/** The decoded JWT payload (user data) if authenticated */
|
|
254
|
+
user: JwtPayload | null;
|
|
255
|
+
/** Error message if authentication failed */
|
|
256
|
+
error?: string;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Extract and validate the current user from an Authorization header.
|
|
260
|
+
* This is the helper for implementing `/api/auth/me` endpoints.
|
|
261
|
+
*
|
|
262
|
+
* - Does NOT call the IDP
|
|
263
|
+
* - Validates JWT signature using cached JWKS (fetches if not cached)
|
|
264
|
+
* - Returns the decoded JWT payload
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```ts
|
|
268
|
+
* import { getCurrentUser, createJwtValidator } from '@authvital/sdk/server';
|
|
269
|
+
*
|
|
270
|
+
* // Create a validator (typically once at startup)
|
|
271
|
+
* const validator = createJwtValidator({
|
|
272
|
+
* authVitalHost: process.env.AV_HOST,
|
|
273
|
+
* });
|
|
274
|
+
*
|
|
275
|
+
* // GET /api/auth/me
|
|
276
|
+
* app.get('/api/auth/me', async (req, res) => {
|
|
277
|
+
* const result = await getCurrentUser(req.headers.authorization, validator);
|
|
278
|
+
*
|
|
279
|
+
* if (!result.authenticated) {
|
|
280
|
+
* return res.status(401).json({ error: result.error });
|
|
281
|
+
* }
|
|
282
|
+
*
|
|
283
|
+
* // Return the decoded JWT claims (no IDP call!)
|
|
284
|
+
* res.json(result.user);
|
|
285
|
+
* });
|
|
286
|
+
* ```
|
|
287
|
+
*
|
|
288
|
+
* @example Next.js API Route
|
|
289
|
+
* ```ts
|
|
290
|
+
* import { getCurrentUser, createJwtValidator } from '@authvital/sdk/server';
|
|
291
|
+
*
|
|
292
|
+
* const validator = createJwtValidator({ authVitalHost: process.env.AV_HOST });
|
|
293
|
+
*
|
|
294
|
+
* export async function GET(request: Request) {
|
|
295
|
+
* const result = await getCurrentUser(
|
|
296
|
+
* request.headers.get('authorization'),
|
|
297
|
+
* validator
|
|
298
|
+
* );
|
|
299
|
+
*
|
|
300
|
+
* if (!result.authenticated) {
|
|
301
|
+
* return Response.json({ error: result.error }, { status: 401 });
|
|
302
|
+
* }
|
|
303
|
+
*
|
|
304
|
+
* return Response.json(result.user);
|
|
305
|
+
* }
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
308
|
+
declare function getCurrentUser(authorizationHeader: string | null | undefined, validator: JwtValidator): Promise<GetCurrentUserResult$1>;
|
|
309
|
+
/**
|
|
310
|
+
* Convenience function that creates a validator and gets the current user in one call.
|
|
311
|
+
* Use this for simple cases; for better performance, create the validator once and reuse it.
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```ts
|
|
315
|
+
* import { getCurrentUserFromConfig } from '@authvital/sdk/server';
|
|
316
|
+
*
|
|
317
|
+
* // GET /api/auth/me (simple one-liner)
|
|
318
|
+
* app.get('/api/auth/me', async (req, res) => {
|
|
319
|
+
* const result = await getCurrentUserFromConfig(
|
|
320
|
+
* req.headers.authorization,
|
|
321
|
+
* { authVitalHost: process.env.AV_HOST }
|
|
322
|
+
* );
|
|
323
|
+
*
|
|
324
|
+
* if (!result.authenticated) {
|
|
325
|
+
* return res.status(401).json({ error: result.error });
|
|
326
|
+
* }
|
|
327
|
+
*
|
|
328
|
+
* res.json(result.user);
|
|
329
|
+
* });
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
declare function getCurrentUserFromConfig(authorizationHeader: string | null | undefined, config: JwtValidatorConfig): Promise<GetCurrentUserResult$1>;
|
|
333
|
+
/**
|
|
334
|
+
* Express/Connect middleware factory for JWT validation
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* ```ts
|
|
338
|
+
* import { createJwtMiddleware } from '@authvital/sdk/server';
|
|
339
|
+
*
|
|
340
|
+
* const requireAuth = createJwtMiddleware({
|
|
341
|
+
* authVitalHost: process.env.AV_HOST,
|
|
342
|
+
* });
|
|
343
|
+
*
|
|
344
|
+
* app.get('/api/protected', requireAuth, (req, res) => {
|
|
345
|
+
* console.log('User:', req.user);
|
|
346
|
+
* res.json({ message: 'Hello!' });
|
|
347
|
+
* });
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
declare function createJwtMiddleware(config: JwtValidatorConfig): (req: any, res: any, next: any) => Promise<any>;
|
|
351
|
+
/**
|
|
352
|
+
* Create options for passport-jwt Strategy
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* ```ts
|
|
356
|
+
* import { Strategy as JwtStrategy } from 'passport-jwt';
|
|
357
|
+
* import { createPassportJwtOptions } from '@authvital/sdk/server';
|
|
358
|
+
*
|
|
359
|
+
* const options = await createPassportJwtOptions({
|
|
360
|
+
* authVitalHost: process.env.AV_HOST,
|
|
361
|
+
* });
|
|
362
|
+
*
|
|
363
|
+
* passport.use(new JwtStrategy(options, (payload, done) => {
|
|
364
|
+
* // payload is the decoded JWT
|
|
365
|
+
* done(null, payload);
|
|
366
|
+
* }));
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
declare function createPassportJwtOptions(config: JwtValidatorConfig): Promise<{
|
|
370
|
+
jwtFromRequest: (req: any) => string | null;
|
|
371
|
+
secretOrKeyProvider: (req: any, rawJwt: any, done: any) => void;
|
|
372
|
+
issuer: string;
|
|
373
|
+
audience?: string;
|
|
374
|
+
algorithms: string[];
|
|
375
|
+
}>;
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* @authvital/sdk - Base Client
|
|
379
|
+
*
|
|
380
|
+
* Shared HTTP utilities, token management, and JWT validation for the AuthVital SDK.
|
|
381
|
+
* All namespaces extend from this base to access authenticated API calls.
|
|
382
|
+
*/
|
|
383
|
+
|
|
384
|
+
interface AuthVitalConfig {
|
|
385
|
+
/** AuthVital IDP URL (e.g., "https://auth.example.com") */
|
|
386
|
+
authVitalHost: string;
|
|
387
|
+
/** OAuth client_id for your application */
|
|
388
|
+
clientId: string;
|
|
389
|
+
/** OAuth client_secret for your application */
|
|
390
|
+
clientSecret: string;
|
|
391
|
+
/** JWKS cache TTL in seconds (default: 3600 = 1 hour) */
|
|
392
|
+
jwksCacheTtl?: number;
|
|
393
|
+
/** Expected audience for JWT validation (defaults to clientId) */
|
|
394
|
+
audience?: string;
|
|
395
|
+
}
|
|
396
|
+
interface GetCurrentUserResult {
|
|
397
|
+
/** Whether the request is authenticated with a valid token */
|
|
398
|
+
authenticated: boolean;
|
|
399
|
+
/** The decoded JWT payload (user data) if authenticated */
|
|
400
|
+
user: JwtPayload | null;
|
|
401
|
+
/** Error message if authentication failed */
|
|
402
|
+
error?: string;
|
|
403
|
+
}
|
|
404
|
+
interface ValidatedClaims {
|
|
405
|
+
/** User ID (sub claim) */
|
|
406
|
+
sub: string;
|
|
407
|
+
/** Tenant ID (tenant_id claim) - only present if token is tenant-scoped */
|
|
408
|
+
tenantId: string;
|
|
409
|
+
/** Tenant subdomain/slug (tenant_subdomain claim) */
|
|
410
|
+
tenantSubdomain?: string;
|
|
411
|
+
/** User's email (if email scope was requested) */
|
|
412
|
+
email?: string;
|
|
413
|
+
/** User's tenant roles (tenant_roles claim) */
|
|
414
|
+
tenant_roles?: string[];
|
|
415
|
+
/** Full JWT payload */
|
|
416
|
+
payload: JwtPayload;
|
|
417
|
+
}
|
|
418
|
+
type RequestLike = Request | {
|
|
419
|
+
headers: {
|
|
420
|
+
authorization?: string;
|
|
421
|
+
Authorization?: string;
|
|
422
|
+
};
|
|
423
|
+
} | {
|
|
424
|
+
headers: Headers;
|
|
425
|
+
} | {
|
|
426
|
+
headers: {
|
|
427
|
+
get: (name: string) => string | null;
|
|
428
|
+
};
|
|
429
|
+
};
|
|
430
|
+
/**
|
|
431
|
+
* Extract Authorization header from various request types
|
|
432
|
+
*/
|
|
433
|
+
declare function extractAuthorizationHeader(request: RequestLike): string | null;
|
|
434
|
+
/**
|
|
435
|
+
* Append client_id query parameter to a URI
|
|
436
|
+
* Handles URIs that already have query params
|
|
437
|
+
*/
|
|
438
|
+
declare function appendClientIdToUri(uri: string | null, clientId: string): string | null;
|
|
439
|
+
/**
|
|
440
|
+
* Base client with shared HTTP utilities and token management.
|
|
441
|
+
*
|
|
442
|
+
* Provides:
|
|
443
|
+
* - M2M token management (client_credentials flow)
|
|
444
|
+
* - Authenticated requests (forwarding user JWTs)
|
|
445
|
+
* - JWT validation and claims extraction
|
|
446
|
+
*/
|
|
447
|
+
declare class BaseClient {
|
|
448
|
+
readonly config: AuthVitalConfig;
|
|
449
|
+
readonly jwtValidator: JwtValidator;
|
|
450
|
+
private accessToken;
|
|
451
|
+
private tokenExpiresAt;
|
|
452
|
+
constructor(config: AuthVitalConfig);
|
|
453
|
+
/**
|
|
454
|
+
* Validate JWT from an incoming request and return the decoded user.
|
|
455
|
+
*
|
|
456
|
+
* - Extracts Authorization header from the request
|
|
457
|
+
* - Validates JWT signature using cached JWKS (public endpoint, no auth needed)
|
|
458
|
+
* - Returns decoded JWT payload
|
|
459
|
+
* - Does NOT call the IDP
|
|
460
|
+
*
|
|
461
|
+
* @example
|
|
462
|
+
* ```ts
|
|
463
|
+
* // Express
|
|
464
|
+
* app.get('/api/auth/me', async (req, res) => {
|
|
465
|
+
* const { authenticated, user, error } = await authvital.getCurrentUser(req);
|
|
466
|
+
* if (!authenticated) return res.status(401).json({ error });
|
|
467
|
+
* res.json(user);
|
|
468
|
+
* });
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
471
|
+
getCurrentUser(request: RequestLike): Promise<GetCurrentUserResult>;
|
|
472
|
+
/**
|
|
473
|
+
* Validate the JWT from an incoming request and extract key claims.
|
|
474
|
+
*
|
|
475
|
+
* This is the recommended way to get user/tenant context for API calls.
|
|
476
|
+
* Throws an error if the token is invalid or missing required claims.
|
|
477
|
+
*
|
|
478
|
+
* @param request - The incoming HTTP request (Express, Next.js, Fetch API, etc.)
|
|
479
|
+
* @returns Validated claims including sub (userId) and tenantId
|
|
480
|
+
* @throws Error if token is invalid or missing tenant_id claim
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* ```ts
|
|
484
|
+
* // Express
|
|
485
|
+
* app.get('/api/members', async (req, res) => {
|
|
486
|
+
* const claims = await authvital.validateRequest(req);
|
|
487
|
+
* const members = await authvital.memberships.listForApplication(claims);
|
|
488
|
+
* res.json(members);
|
|
489
|
+
* });
|
|
490
|
+
* ```
|
|
491
|
+
*/
|
|
492
|
+
validateRequest(request: RequestLike): Promise<ValidatedClaims>;
|
|
493
|
+
/**
|
|
494
|
+
* Get M2M access token (client_credentials flow)
|
|
495
|
+
*
|
|
496
|
+
* Returns cached token if still valid, otherwise fetches a new one.
|
|
497
|
+
*/
|
|
498
|
+
getAccessToken(): Promise<string>;
|
|
499
|
+
/**
|
|
500
|
+
* Make an M2M authenticated request
|
|
501
|
+
*
|
|
502
|
+
* Uses client_credentials token for machine-to-machine calls.
|
|
503
|
+
*/
|
|
504
|
+
request<T>(method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body?: unknown, isRetry?: boolean): Promise<T>;
|
|
505
|
+
/**
|
|
506
|
+
* Make an authenticated request using the JWT from the original request
|
|
507
|
+
*
|
|
508
|
+
* This version forwards the user's JWT token instead of using the
|
|
509
|
+
* M2M (client_credentials) token. Used for endpoints that require
|
|
510
|
+
* user context and validate tenant permissions.
|
|
511
|
+
*
|
|
512
|
+
* @param originalRequest - The incoming request with JWT
|
|
513
|
+
* @param method - HTTP method
|
|
514
|
+
* @param path - API path
|
|
515
|
+
* @param body - Request body (for POST/PUT)
|
|
516
|
+
* @returns Parsed response
|
|
517
|
+
* @throws Error if request fails or JWT is required
|
|
518
|
+
*/
|
|
519
|
+
authenticatedRequest<T>(originalRequest: RequestLike, method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body?: unknown): Promise<T>;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* @authvital/sdk - Sessions Namespace (Token Ghosting)
|
|
524
|
+
*
|
|
525
|
+
* Manage user sessions: list, revoke specific sessions, or logout everywhere.
|
|
526
|
+
*/
|
|
527
|
+
|
|
528
|
+
interface SessionInfo {
|
|
529
|
+
id: string;
|
|
530
|
+
createdAt: string;
|
|
531
|
+
expiresAt: string;
|
|
532
|
+
userAgent: string | null;
|
|
533
|
+
ipAddress: string | null;
|
|
534
|
+
tenant: string | null;
|
|
535
|
+
}
|
|
536
|
+
interface SessionsListResponse {
|
|
537
|
+
sessions: SessionInfo[];
|
|
538
|
+
count: number;
|
|
539
|
+
}
|
|
540
|
+
interface SessionRevokeResponse {
|
|
541
|
+
success: boolean;
|
|
542
|
+
message: string;
|
|
543
|
+
}
|
|
544
|
+
interface LogoutAllResponse {
|
|
545
|
+
success: boolean;
|
|
546
|
+
message: string;
|
|
547
|
+
count: number;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Creates the sessions namespace with all session management methods.
|
|
551
|
+
*
|
|
552
|
+
* @param client - The base client instance for making authenticated requests
|
|
553
|
+
* @returns Object containing all session methods
|
|
554
|
+
*/
|
|
555
|
+
declare function createSessionsNamespace(client: BaseClient): {
|
|
556
|
+
/**
|
|
557
|
+
* Get all active sessions for the authenticated user
|
|
558
|
+
*
|
|
559
|
+
* Returns a list of active sessions with metadata (device info, location, etc.).
|
|
560
|
+
* Useful for building "manage sessions" UI.
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```ts
|
|
564
|
+
* app.get('/api/sessions', async (req, res) => {
|
|
565
|
+
* const { sessions, count } = await authvital.sessions.list(req);
|
|
566
|
+
* res.json(sessions);
|
|
567
|
+
* });
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
list: (request: RequestLike, options?: {
|
|
571
|
+
applicationId?: string;
|
|
572
|
+
}) => Promise<SessionsListResponse>;
|
|
573
|
+
/**
|
|
574
|
+
* Revoke a specific session by ID
|
|
575
|
+
*
|
|
576
|
+
* Call this from "manage sessions" UI to logout a specific device.
|
|
577
|
+
* User can only revoke their own sessions.
|
|
578
|
+
*
|
|
579
|
+
* @example
|
|
580
|
+
* ```ts
|
|
581
|
+
* app.post('/api/sessions/:id/revoke', async (req, res) => {
|
|
582
|
+
* const result = await authvital.sessions.revoke(req, req.params.id);
|
|
583
|
+
* res.json(result);
|
|
584
|
+
* });
|
|
585
|
+
* ```
|
|
586
|
+
*/
|
|
587
|
+
revoke: (request: RequestLike, sessionId: string) => Promise<SessionRevokeResponse>;
|
|
588
|
+
/**
|
|
589
|
+
* Revoke ALL sessions for the authenticated user
|
|
590
|
+
*
|
|
591
|
+
* Call this when user clicks "logout everywhere".
|
|
592
|
+
* Revokes all active sessions, forcing re-authentication on all devices.
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* ```ts
|
|
596
|
+
* app.post('/api/logout-all', async (req, res) => {
|
|
597
|
+
* const result = await authvital.sessions.revokeAll(req);
|
|
598
|
+
* res.json({ message: `Logged out of ${result.count} devices` });
|
|
599
|
+
* });
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
revokeAll: (request: RequestLike, options?: {
|
|
603
|
+
applicationId?: string;
|
|
604
|
+
}) => Promise<LogoutAllResponse>;
|
|
605
|
+
/**
|
|
606
|
+
* Logout current session
|
|
607
|
+
*
|
|
608
|
+
* Revokes the session associated with the current refresh token.
|
|
609
|
+
* Call this for normal logout.
|
|
610
|
+
*
|
|
611
|
+
* Note: For browser apps, prefer redirecting to /oauth/logout which
|
|
612
|
+
* handles cookie clearing automatically.
|
|
613
|
+
*
|
|
614
|
+
* @example
|
|
615
|
+
* ```ts
|
|
616
|
+
* app.post('/api/logout', async (req, res) => {
|
|
617
|
+
* // Get refresh token from cookie or body
|
|
618
|
+
* const refreshToken = req.cookies.refresh_token || req.body.refresh_token;
|
|
619
|
+
* const result = await authvital.sessions.logout(refreshToken);
|
|
620
|
+
* res.clearCookie('refresh_token');
|
|
621
|
+
* res.json(result);
|
|
622
|
+
* });
|
|
623
|
+
* ```
|
|
624
|
+
*/
|
|
625
|
+
logout: (refreshToken: string) => Promise<SessionRevokeResponse>;
|
|
626
|
+
};
|
|
627
|
+
type SessionsNamespace = ReturnType<typeof createSessionsNamespace>;
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* @authvital/sdk - Server-Side Types
|
|
631
|
+
*
|
|
632
|
+
* Clean, well-organized type definitions for the AuthVital server SDK.
|
|
633
|
+
*
|
|
634
|
+
* ╔════════════════════════════════════════════════════════════════════════════╗
|
|
635
|
+
* ║ IMPORTANT: CANONICAL TYPE SYNCHRONIZATION ║
|
|
636
|
+
* ╠════════════════════════════════════════════════════════════════════════════╣
|
|
637
|
+
* ║ The following types are COPIES of canonical definitions: ║
|
|
638
|
+
* ║ ║
|
|
639
|
+
* ║ - SubscriptionSummary ║
|
|
640
|
+
* ║ - SubscriptionStatusType ║
|
|
641
|
+
* ║ - MemberWithLicenses ║
|
|
642
|
+
* ║ - MembershipStatusType ║
|
|
643
|
+
* ║ - AvailableLicenseType ║
|
|
644
|
+
* ║ - TenantLicenseOverview ║
|
|
645
|
+
* ║ ║
|
|
646
|
+
* ║ SOURCE OF TRUTH: backend/src/common/types/licensing.types.ts ║
|
|
647
|
+
* ║ ║
|
|
648
|
+
* ║ When updating these types, ALWAYS update the canonical source first, ║
|
|
649
|
+
* ║ then sync changes here to maintain consistency. ║
|
|
650
|
+
* ╚════════════════════════════════════════════════════════════════════════════╝
|
|
651
|
+
*/
|
|
652
|
+
/**
|
|
653
|
+
* Subscription status values.
|
|
654
|
+
* @sync backend/src/common/types/licensing.types.ts
|
|
655
|
+
*/
|
|
656
|
+
type SubscriptionStatusType = 'ACTIVE' | 'TRIALING' | 'PAST_DUE' | 'CANCELED' | 'EXPIRED';
|
|
657
|
+
/**
|
|
658
|
+
* Membership status values.
|
|
659
|
+
* @sync backend/src/common/types/licensing.types.ts
|
|
660
|
+
*/
|
|
661
|
+
type MembershipStatusType = 'ACTIVE' | 'INVITED' | 'SUSPENDED';
|
|
662
|
+
/**
|
|
663
|
+
* Summary of a tenant's subscription (license inventory).
|
|
664
|
+
* @sync backend/src/common/types/licensing.types.ts
|
|
665
|
+
*/
|
|
666
|
+
interface SubscriptionSummary {
|
|
667
|
+
id: string;
|
|
668
|
+
applicationId: string;
|
|
669
|
+
applicationName: string;
|
|
670
|
+
licenseTypeId: string;
|
|
671
|
+
licenseTypeName: string;
|
|
672
|
+
licenseTypeSlug: string;
|
|
673
|
+
quantityPurchased: number;
|
|
674
|
+
quantityAssigned: number;
|
|
675
|
+
quantityAvailable: number;
|
|
676
|
+
status: SubscriptionStatusType;
|
|
677
|
+
currentPeriodEnd: string;
|
|
678
|
+
features: Record<string, boolean>;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* A tenant member with their license assignments.
|
|
682
|
+
* @sync backend/src/common/types/licensing.types.ts
|
|
683
|
+
*/
|
|
684
|
+
interface MemberWithLicenses {
|
|
685
|
+
user: {
|
|
686
|
+
id: string;
|
|
687
|
+
email: string | null;
|
|
688
|
+
givenName: string | null;
|
|
689
|
+
familyName: string | null;
|
|
690
|
+
};
|
|
691
|
+
membership: {
|
|
692
|
+
id: string;
|
|
693
|
+
status: MembershipStatusType;
|
|
694
|
+
};
|
|
695
|
+
licenses: Array<{
|
|
696
|
+
id: string;
|
|
697
|
+
applicationId: string;
|
|
698
|
+
applicationName: string;
|
|
699
|
+
licenseTypeId: string;
|
|
700
|
+
licenseTypeName: string;
|
|
701
|
+
licenseTypeSlug: string;
|
|
702
|
+
assignedAt: string;
|
|
703
|
+
}>;
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* A license type available for provisioning.
|
|
707
|
+
* @sync backend/src/common/types/licensing.types.ts
|
|
708
|
+
*/
|
|
709
|
+
interface AvailableLicenseType {
|
|
710
|
+
id: string;
|
|
711
|
+
name: string;
|
|
712
|
+
slug: string;
|
|
713
|
+
description: string | null;
|
|
714
|
+
applicationId: string;
|
|
715
|
+
applicationName: string;
|
|
716
|
+
features: Record<string, boolean>;
|
|
717
|
+
displayOrder: number;
|
|
718
|
+
hasSubscription: boolean;
|
|
719
|
+
existingSubscription?: {
|
|
720
|
+
id: string;
|
|
721
|
+
quantityPurchased: number;
|
|
722
|
+
quantityAssigned: number;
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Full license overview for a tenant.
|
|
727
|
+
* @sync backend/src/common/types/licensing.types.ts
|
|
728
|
+
*/
|
|
729
|
+
interface TenantLicenseOverview {
|
|
730
|
+
tenantId: string;
|
|
731
|
+
subscriptions: SubscriptionSummary[];
|
|
732
|
+
totalSeatsOwned: number;
|
|
733
|
+
totalSeatsAssigned: number;
|
|
734
|
+
}
|
|
735
|
+
interface AuthVitalClientConfig {
|
|
736
|
+
/** AuthVital server URL (e.g., https://auth.yourapp.com) */
|
|
737
|
+
authVitalHost: string;
|
|
738
|
+
/** OAuth client_id for your application */
|
|
739
|
+
clientId: string;
|
|
740
|
+
/** OAuth client_secret for your application */
|
|
741
|
+
clientSecret: string;
|
|
742
|
+
/** Optional: Scopes to request (default: 'system:admin') */
|
|
743
|
+
scope?: string;
|
|
744
|
+
}
|
|
745
|
+
interface OAuthFlowConfig {
|
|
746
|
+
/** AuthVital server URL */
|
|
747
|
+
authVitalHost: string;
|
|
748
|
+
/** OAuth client_id */
|
|
749
|
+
clientId: string;
|
|
750
|
+
/** OAuth client_secret (optional for public clients) */
|
|
751
|
+
clientSecret?: string;
|
|
752
|
+
/** OAuth redirect URI */
|
|
753
|
+
redirectUri: string;
|
|
754
|
+
/** OAuth scopes */
|
|
755
|
+
scope?: string;
|
|
756
|
+
}
|
|
757
|
+
interface TokenResponse {
|
|
758
|
+
access_token: string;
|
|
759
|
+
token_type: string;
|
|
760
|
+
expires_in: number;
|
|
761
|
+
refresh_token?: string;
|
|
762
|
+
id_token?: string;
|
|
763
|
+
scope?: string;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* License information included in JWT
|
|
767
|
+
*/
|
|
768
|
+
interface JwtLicenseInfo {
|
|
769
|
+
/** License type slug (e.g., "pro", "enterprise") */
|
|
770
|
+
type: string;
|
|
771
|
+
/** License type display name */
|
|
772
|
+
name: string;
|
|
773
|
+
/** Enabled feature keys */
|
|
774
|
+
features: string[];
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Enhanced JWT payload with OIDC standard claims
|
|
778
|
+
*
|
|
779
|
+
* This represents the decoded JWT token contents from AuthVital.
|
|
780
|
+
* Claims are included based on requested OAuth scopes.
|
|
781
|
+
*/
|
|
782
|
+
interface EnhancedJwtPayload {
|
|
783
|
+
/** Subject (user ID) */
|
|
784
|
+
sub: string;
|
|
785
|
+
/** Audience (client ID) */
|
|
786
|
+
aud: string | string[];
|
|
787
|
+
/** Issuer (AuthVital URL) */
|
|
788
|
+
iss: string;
|
|
789
|
+
/** Issued at (unix timestamp) */
|
|
790
|
+
iat: number;
|
|
791
|
+
/** Expiration (unix timestamp) */
|
|
792
|
+
exp: number;
|
|
793
|
+
/** Unique handle - OIDC: preferred_username */
|
|
794
|
+
preferred_username?: string;
|
|
795
|
+
/** Full display name - OIDC: name */
|
|
796
|
+
name?: string;
|
|
797
|
+
/** First name - OIDC: given_name */
|
|
798
|
+
given_name?: string;
|
|
799
|
+
/** Last name - OIDC: family_name */
|
|
800
|
+
family_name?: string;
|
|
801
|
+
/** Middle name(s) - OIDC: middle_name */
|
|
802
|
+
middle_name?: string;
|
|
803
|
+
/** Casual name - OIDC: nickname */
|
|
804
|
+
nickname?: string;
|
|
805
|
+
/** Profile picture URL - OIDC: picture */
|
|
806
|
+
picture?: string;
|
|
807
|
+
/** Personal URL - OIDC: website */
|
|
808
|
+
website?: string;
|
|
809
|
+
/** Gender identity - OIDC: gender */
|
|
810
|
+
gender?: string;
|
|
811
|
+
/** Birth date (YYYY-MM-DD) - OIDC: birthdate */
|
|
812
|
+
birthdate?: string;
|
|
813
|
+
/** IANA timezone - OIDC: zoneinfo */
|
|
814
|
+
zoneinfo?: string;
|
|
815
|
+
/** Language/region - OIDC: locale */
|
|
816
|
+
locale?: string;
|
|
817
|
+
/** Last profile update (unix timestamp) - OIDC: updated_at */
|
|
818
|
+
updated_at?: number;
|
|
819
|
+
/** Email address - OIDC: email */
|
|
820
|
+
email?: string;
|
|
821
|
+
/** Whether email is verified - OIDC: email_verified */
|
|
822
|
+
email_verified?: boolean;
|
|
823
|
+
/** Phone number (E.164) - OIDC: phone_number */
|
|
824
|
+
phone_number?: string;
|
|
825
|
+
/** Whether phone is verified - OIDC: phone_number_verified */
|
|
826
|
+
phone_number_verified?: boolean;
|
|
827
|
+
/** Current tenant ID (when token is tenant-scoped) */
|
|
828
|
+
tenant_id?: string;
|
|
829
|
+
/** Tenant subdomain */
|
|
830
|
+
tenant_subdomain?: string;
|
|
831
|
+
/** Tenant-level roles (from TenantRole) */
|
|
832
|
+
tenant_roles?: string[];
|
|
833
|
+
/** Tenant-level permissions */
|
|
834
|
+
tenant_permissions?: string[];
|
|
835
|
+
/** Application-specific roles (from Role) */
|
|
836
|
+
app_roles?: string[];
|
|
837
|
+
/** Application-specific permissions */
|
|
838
|
+
app_permissions?: string[];
|
|
839
|
+
/** Groups the user belongs to in the current tenant */
|
|
840
|
+
groups?: string[];
|
|
841
|
+
/** License info for current app */
|
|
842
|
+
license?: JwtLicenseInfo;
|
|
843
|
+
/** Granted scopes */
|
|
844
|
+
scope?: string;
|
|
845
|
+
[key: string]: unknown;
|
|
846
|
+
}
|
|
847
|
+
interface InvitationResponse {
|
|
848
|
+
/** The user's ID (sub claim in JWT) */
|
|
849
|
+
sub: string;
|
|
850
|
+
/** When the invitation expires */
|
|
851
|
+
expiresAt: string;
|
|
852
|
+
}
|
|
853
|
+
interface PendingInvitation {
|
|
854
|
+
id: string;
|
|
855
|
+
email: string;
|
|
856
|
+
role: string;
|
|
857
|
+
expiresAt: string;
|
|
858
|
+
createdAt: string;
|
|
859
|
+
invitedBy: {
|
|
860
|
+
id: string;
|
|
861
|
+
email: string | null;
|
|
862
|
+
givenName: string | null;
|
|
863
|
+
familyName: string | null;
|
|
864
|
+
} | null;
|
|
865
|
+
}
|
|
866
|
+
interface PendingInvitationsResponse {
|
|
867
|
+
tenantId: string;
|
|
868
|
+
tenantName: string;
|
|
869
|
+
invitations: PendingInvitation[];
|
|
870
|
+
totalCount: number;
|
|
871
|
+
}
|
|
872
|
+
interface SendInvitationParams {
|
|
873
|
+
email: string;
|
|
874
|
+
/** User's first name (used if creating new user) */
|
|
875
|
+
givenName?: string;
|
|
876
|
+
/** User's last name (used if creating new user) */
|
|
877
|
+
familyName?: string;
|
|
878
|
+
/**
|
|
879
|
+
* Tenant role ID to assign when invitation is accepted.
|
|
880
|
+
* Required. Get available role IDs from `authvital.memberships.getTenantRoles()`.
|
|
881
|
+
*/
|
|
882
|
+
roleId: string;
|
|
883
|
+
expiresInDays?: number;
|
|
884
|
+
/** Application clientId - determines redirect URL after acceptance */
|
|
885
|
+
clientId?: string;
|
|
886
|
+
}
|
|
887
|
+
interface ResendInvitationParams {
|
|
888
|
+
invitationId: string;
|
|
889
|
+
expiresInDays?: number;
|
|
890
|
+
}
|
|
891
|
+
interface RevokeInvitationResponse {
|
|
892
|
+
success: boolean;
|
|
893
|
+
message: string;
|
|
894
|
+
}
|
|
895
|
+
interface MembershipUser {
|
|
896
|
+
id: string;
|
|
897
|
+
email: string | null;
|
|
898
|
+
givenName: string | null;
|
|
899
|
+
familyName: string | null;
|
|
900
|
+
}
|
|
901
|
+
interface MembershipRole {
|
|
902
|
+
id: string;
|
|
903
|
+
name: string;
|
|
904
|
+
slug: string;
|
|
905
|
+
applicationId: string;
|
|
906
|
+
applicationName: string;
|
|
907
|
+
}
|
|
908
|
+
interface TenantMembership {
|
|
909
|
+
id: string;
|
|
910
|
+
status: string;
|
|
911
|
+
joinedAt: string | null;
|
|
912
|
+
createdAt: string;
|
|
913
|
+
user: MembershipUser;
|
|
914
|
+
roles: MembershipRole[];
|
|
915
|
+
}
|
|
916
|
+
interface TenantMembershipsResponse {
|
|
917
|
+
tenantId: string;
|
|
918
|
+
tenantName: string;
|
|
919
|
+
tenantSlug: string;
|
|
920
|
+
initiateLoginUri: string | null;
|
|
921
|
+
memberships: TenantMembership[];
|
|
922
|
+
totalCount: number;
|
|
923
|
+
}
|
|
924
|
+
interface ApplicationMembership {
|
|
925
|
+
id: string;
|
|
926
|
+
status: string;
|
|
927
|
+
joinedAt: string | null;
|
|
928
|
+
createdAt: string;
|
|
929
|
+
user: MembershipUser;
|
|
930
|
+
tenant: {
|
|
931
|
+
id: string;
|
|
932
|
+
name: string;
|
|
933
|
+
slug: string;
|
|
934
|
+
initiateLoginUri: string | null;
|
|
935
|
+
};
|
|
936
|
+
roles: MembershipRole[];
|
|
937
|
+
}
|
|
938
|
+
interface ApplicationMembershipsResponse {
|
|
939
|
+
applicationId: string;
|
|
940
|
+
applicationName: string;
|
|
941
|
+
clientId: string;
|
|
942
|
+
memberships: ApplicationMembership[];
|
|
943
|
+
totalCount: number;
|
|
944
|
+
}
|
|
945
|
+
interface ValidateMembershipResponse {
|
|
946
|
+
isMember: boolean;
|
|
947
|
+
membership: {
|
|
948
|
+
id: string;
|
|
949
|
+
status: string;
|
|
950
|
+
joinedAt: string | null;
|
|
951
|
+
} | null;
|
|
952
|
+
}
|
|
953
|
+
interface UserTenantMembership {
|
|
954
|
+
id: string;
|
|
955
|
+
status: string;
|
|
956
|
+
joinedAt: string | null;
|
|
957
|
+
createdAt: string;
|
|
958
|
+
tenant: {
|
|
959
|
+
id: string;
|
|
960
|
+
name: string;
|
|
961
|
+
slug: string;
|
|
962
|
+
initiateLoginUri: string | null;
|
|
963
|
+
};
|
|
964
|
+
roles: MembershipRole[];
|
|
965
|
+
}
|
|
966
|
+
interface UserTenantsResponse {
|
|
967
|
+
userId: string;
|
|
968
|
+
memberships: UserTenantMembership[];
|
|
969
|
+
totalCount: number;
|
|
970
|
+
}
|
|
971
|
+
interface TenantRoleDefinition {
|
|
972
|
+
id: string;
|
|
973
|
+
name: string;
|
|
974
|
+
slug: string;
|
|
975
|
+
description: string | null;
|
|
976
|
+
isSystem: boolean;
|
|
977
|
+
permissions: string[];
|
|
978
|
+
}
|
|
979
|
+
interface TenantRolesResponse {
|
|
980
|
+
roles: TenantRoleDefinition[];
|
|
981
|
+
}
|
|
982
|
+
interface ApplicationRoleDefinition {
|
|
983
|
+
id: string;
|
|
984
|
+
name: string;
|
|
985
|
+
slug: string;
|
|
986
|
+
description: string | null;
|
|
987
|
+
isSystem: boolean;
|
|
988
|
+
permissions: string[];
|
|
989
|
+
}
|
|
990
|
+
interface ApplicationRolesResponse {
|
|
991
|
+
applicationId: string;
|
|
992
|
+
applicationName: string;
|
|
993
|
+
clientId: string;
|
|
994
|
+
roles: ApplicationRoleDefinition[];
|
|
995
|
+
}
|
|
996
|
+
interface SetMemberRoleResponse {
|
|
997
|
+
success: boolean;
|
|
998
|
+
message: string;
|
|
999
|
+
role: {
|
|
1000
|
+
id: string;
|
|
1001
|
+
name: string;
|
|
1002
|
+
slug: string;
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
interface CheckPermissionParams {
|
|
1006
|
+
userId: string;
|
|
1007
|
+
tenantId: string;
|
|
1008
|
+
permission: string;
|
|
1009
|
+
}
|
|
1010
|
+
interface CheckPermissionResult {
|
|
1011
|
+
allowed: boolean;
|
|
1012
|
+
reason?: string;
|
|
1013
|
+
}
|
|
1014
|
+
interface CheckPermissionsParams {
|
|
1015
|
+
userId: string;
|
|
1016
|
+
tenantId: string;
|
|
1017
|
+
permissions: string[];
|
|
1018
|
+
}
|
|
1019
|
+
interface CheckPermissionsResult {
|
|
1020
|
+
results: Record<string, boolean>;
|
|
1021
|
+
allAllowed: boolean;
|
|
1022
|
+
}
|
|
1023
|
+
interface UserPermissions {
|
|
1024
|
+
permissions: string[];
|
|
1025
|
+
roles: Array<{
|
|
1026
|
+
id: string;
|
|
1027
|
+
name: string;
|
|
1028
|
+
slug: string;
|
|
1029
|
+
}>;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Result of checking a quota
|
|
1033
|
+
*/
|
|
1034
|
+
interface QuotaCheckResult {
|
|
1035
|
+
/** Whether the action is allowed */
|
|
1036
|
+
allowed: boolean;
|
|
1037
|
+
/** Current usage count */
|
|
1038
|
+
currentUsage?: number;
|
|
1039
|
+
/** The limit */
|
|
1040
|
+
limit?: number;
|
|
1041
|
+
/** Reason if not allowed */
|
|
1042
|
+
reason?: string;
|
|
1043
|
+
/** Whether this would trigger overage billing */
|
|
1044
|
+
wouldTriggerOverage?: boolean;
|
|
1045
|
+
/** The overage price ID if applicable */
|
|
1046
|
+
overagePriceId?: string | null;
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Result of checking a feature flag
|
|
1050
|
+
*/
|
|
1051
|
+
interface FeatureCheckResult {
|
|
1052
|
+
/** Whether the feature is enabled */
|
|
1053
|
+
hasAccess: boolean;
|
|
1054
|
+
/** The license type slug */
|
|
1055
|
+
licenseType: string | null;
|
|
1056
|
+
/** Reason if not enabled */
|
|
1057
|
+
reason?: string;
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* User's license assignment info
|
|
1061
|
+
*/
|
|
1062
|
+
interface UserLicenseAssignment {
|
|
1063
|
+
id: string;
|
|
1064
|
+
userId: string;
|
|
1065
|
+
applicationId: string;
|
|
1066
|
+
applicationName: string;
|
|
1067
|
+
licenseTypeId: string;
|
|
1068
|
+
licenseTypeName: string;
|
|
1069
|
+
licenseTypeSlug: string;
|
|
1070
|
+
features: Record<string, boolean>;
|
|
1071
|
+
assignedAt: string;
|
|
1072
|
+
assignedById?: string;
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Parameters for granting a license (M2M)
|
|
1076
|
+
*/
|
|
1077
|
+
interface GrantLicenseParams {
|
|
1078
|
+
tenantId: string;
|
|
1079
|
+
userId: string;
|
|
1080
|
+
applicationId: string;
|
|
1081
|
+
licenseTypeId: string;
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Parameters for revoking a license (M2M)
|
|
1085
|
+
*/
|
|
1086
|
+
interface RevokeLicenseParams {
|
|
1087
|
+
tenantId: string;
|
|
1088
|
+
userId: string;
|
|
1089
|
+
applicationId: string;
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Parameters for changing license type (M2M)
|
|
1093
|
+
*/
|
|
1094
|
+
interface ChangeLicenseTypeParams {
|
|
1095
|
+
tenantId: string;
|
|
1096
|
+
userId: string;
|
|
1097
|
+
applicationId: string;
|
|
1098
|
+
newLicenseTypeId: string;
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Bulk grant license result
|
|
1102
|
+
*/
|
|
1103
|
+
interface BulkGrantLicenseResult {
|
|
1104
|
+
userId: string;
|
|
1105
|
+
applicationId: string;
|
|
1106
|
+
success: boolean;
|
|
1107
|
+
error?: string;
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Bulk revoke license result
|
|
1111
|
+
*/
|
|
1112
|
+
interface BulkRevokeLicenseResult {
|
|
1113
|
+
revokedCount: number;
|
|
1114
|
+
failures: Array<{
|
|
1115
|
+
userId: string;
|
|
1116
|
+
applicationId: string;
|
|
1117
|
+
error: string;
|
|
1118
|
+
}>;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Main AuthVital SDK client.
|
|
1123
|
+
*
|
|
1124
|
+
* Extends BaseClient with namespaced APIs for:
|
|
1125
|
+
* - Invitations (send, list, resend, revoke)
|
|
1126
|
+
* - Memberships (list, validate, roles)
|
|
1127
|
+
* - Permissions (check, list)
|
|
1128
|
+
* - Entitlements (quotas, features)
|
|
1129
|
+
* - Licenses (grant, revoke, check)
|
|
1130
|
+
* - Sessions (list, revoke, logout)
|
|
1131
|
+
*/
|
|
1132
|
+
declare class AuthVital extends BaseClient {
|
|
1133
|
+
readonly invitations: {
|
|
1134
|
+
send: (request: RequestLike, params: Omit<SendInvitationParams, "tenantId">) => Promise<InvitationResponse>;
|
|
1135
|
+
listPending: (request: RequestLike) => Promise<PendingInvitationsResponse>;
|
|
1136
|
+
resend: (request: RequestLike, params: ResendInvitationParams) => Promise<{
|
|
1137
|
+
expiresAt: string;
|
|
1138
|
+
}>;
|
|
1139
|
+
revoke: (request: RequestLike, invitationId: string) => Promise<RevokeInvitationResponse>;
|
|
1140
|
+
};
|
|
1141
|
+
readonly memberships: {
|
|
1142
|
+
listForTenant: (request: RequestLike, options?: {
|
|
1143
|
+
status?: "ACTIVE" | "INVITED" | "SUSPENDED";
|
|
1144
|
+
includeRoles?: boolean;
|
|
1145
|
+
appendClientId?: boolean;
|
|
1146
|
+
}) => Promise<TenantMembershipsResponse>;
|
|
1147
|
+
listForApplication: (request: RequestLike, options?: {
|
|
1148
|
+
status?: "ACTIVE" | "INVITED" | "SUSPENDED";
|
|
1149
|
+
appendClientId?: boolean;
|
|
1150
|
+
}) => Promise<ApplicationMembershipsResponse>;
|
|
1151
|
+
validate: (request: RequestLike) => Promise<ValidateMembershipResponse>;
|
|
1152
|
+
listTenantsForUser: (request: RequestLike, options?: {
|
|
1153
|
+
status?: "ACTIVE" | "INVITED" | "SUSPENDED";
|
|
1154
|
+
includeRoles?: boolean;
|
|
1155
|
+
appendClientId?: boolean;
|
|
1156
|
+
}) => Promise<UserTenantsResponse>;
|
|
1157
|
+
getTenantRoles: () => Promise<TenantRolesResponse>;
|
|
1158
|
+
getApplicationRoles: () => Promise<ApplicationRolesResponse>;
|
|
1159
|
+
setMemberRole: (request: RequestLike, membershipId: string, roleSlug: string) => Promise<SetMemberRoleResponse>;
|
|
1160
|
+
};
|
|
1161
|
+
readonly permissions: {
|
|
1162
|
+
check: (request: RequestLike, permission: string) => Promise<CheckPermissionResult>;
|
|
1163
|
+
checkMany: (request: RequestLike, permissions: string[]) => Promise<CheckPermissionsResult>;
|
|
1164
|
+
list: (request: RequestLike) => Promise<UserPermissions>;
|
|
1165
|
+
};
|
|
1166
|
+
readonly entitlements: {
|
|
1167
|
+
canPerform: (request: RequestLike, featureKey: string, _options?: {
|
|
1168
|
+
appScope?: string;
|
|
1169
|
+
incrementCount?: number;
|
|
1170
|
+
}) => Promise<QuotaCheckResult>;
|
|
1171
|
+
decrementUsage: (request: RequestLike, featureKey: string, options?: {
|
|
1172
|
+
appScope?: string;
|
|
1173
|
+
by?: number;
|
|
1174
|
+
}) => Promise<{
|
|
1175
|
+
newUsage: number;
|
|
1176
|
+
}>;
|
|
1177
|
+
};
|
|
1178
|
+
readonly licenses: {
|
|
1179
|
+
getTenantOverview: (tenantId: string) => Promise<TenantLicenseOverview>;
|
|
1180
|
+
getUserLicenses: (tenantId: string, userId: string) => Promise<UserLicenseAssignment[]>;
|
|
1181
|
+
getTenantSubscriptions: (tenantId: string) => Promise<SubscriptionSummary[]>;
|
|
1182
|
+
getMembersWithLicenses: (tenantId: string) => Promise<MemberWithLicenses[]>;
|
|
1183
|
+
getAvailableLicenseTypes: (tenantId: string) => Promise<AvailableLicenseType[]>;
|
|
1184
|
+
grantToUser: (params: GrantLicenseParams) => Promise<UserLicenseAssignment>;
|
|
1185
|
+
revokeFromUser: (params: RevokeLicenseParams) => Promise<void>;
|
|
1186
|
+
changeUserType: (params: ChangeLicenseTypeParams) => Promise<UserLicenseAssignment>;
|
|
1187
|
+
grantBulk: (assignments: GrantLicenseParams[]) => Promise<BulkGrantLicenseResult[]>;
|
|
1188
|
+
revokeBulk: (revocations: RevokeLicenseParams[]) => Promise<BulkRevokeLicenseResult>;
|
|
1189
|
+
grant: (request: RequestLike, options: {
|
|
1190
|
+
userId?: string;
|
|
1191
|
+
applicationId: string;
|
|
1192
|
+
licenseTypeId: string;
|
|
1193
|
+
}) => Promise<LicenseGrantResponse>;
|
|
1194
|
+
revoke: (request: RequestLike, options: {
|
|
1195
|
+
userId?: string;
|
|
1196
|
+
applicationId: string;
|
|
1197
|
+
}) => Promise<LicenseRevokeResponse>;
|
|
1198
|
+
changeType: (request: RequestLike, options: {
|
|
1199
|
+
userId?: string;
|
|
1200
|
+
applicationId: string;
|
|
1201
|
+
newLicenseTypeId: string;
|
|
1202
|
+
}) => Promise<LicenseRevokeResponse>;
|
|
1203
|
+
listForUser: (request: RequestLike, userId?: string) => Promise<UserLicenseListItem[]>;
|
|
1204
|
+
check: (request: RequestLike, userId: string | undefined, applicationId: string) => Promise<LicenseCheckResponse>;
|
|
1205
|
+
hasFeature: (request: RequestLike, userId: string | undefined, applicationId: string, featureKey: string) => Promise<LicenseFeatureResponse>;
|
|
1206
|
+
getAppLicensedUsers: (request: RequestLike, applicationId: string) => Promise<LicensedUser[]>;
|
|
1207
|
+
countLicensedUsers: (request: RequestLike, applicationId: string) => Promise<{
|
|
1208
|
+
count: number;
|
|
1209
|
+
}>;
|
|
1210
|
+
getUserLicenseType: (request: RequestLike, userId: string | undefined, applicationId: string) => Promise<string | null>;
|
|
1211
|
+
getHolders: (request: RequestLike, applicationId: string) => Promise<LicenseHolder[]>;
|
|
1212
|
+
getAuditLog: (request: RequestLike, options?: {
|
|
1213
|
+
userId?: string;
|
|
1214
|
+
applicationId?: string;
|
|
1215
|
+
limit?: number;
|
|
1216
|
+
offset?: number;
|
|
1217
|
+
}) => Promise<LicenseAuditLogResponse>;
|
|
1218
|
+
getUsageOverview: (request: RequestLike) => Promise<UsageOverviewResponse>;
|
|
1219
|
+
getUsageTrends: (request: RequestLike, days?: number) => Promise<UsageTrendEntry[]>;
|
|
1220
|
+
};
|
|
1221
|
+
readonly sessions: {
|
|
1222
|
+
list: (request: RequestLike, options?: {
|
|
1223
|
+
applicationId?: string;
|
|
1224
|
+
}) => Promise<SessionsListResponse>;
|
|
1225
|
+
revoke: (request: RequestLike, sessionId: string) => Promise<SessionRevokeResponse>;
|
|
1226
|
+
revokeAll: (request: RequestLike, options?: {
|
|
1227
|
+
applicationId?: string;
|
|
1228
|
+
}) => Promise<LogoutAllResponse>;
|
|
1229
|
+
logout: (refreshToken: string) => Promise<SessionRevokeResponse>;
|
|
1230
|
+
};
|
|
1231
|
+
/**
|
|
1232
|
+
* Check tenant permission from JWT (no API call)
|
|
1233
|
+
* Returns true if user has the specified tenant permission.
|
|
1234
|
+
*
|
|
1235
|
+
* Reads from the `tenant_permissions` claim in the JWT.
|
|
1236
|
+
* Wildcards are supported (e.g., `licenses:*` matches `licenses:manage`).
|
|
1237
|
+
*
|
|
1238
|
+
* @example
|
|
1239
|
+
* ```ts
|
|
1240
|
+
* if (await authvital.hasTenantPermission(req, 'licenses:manage')) {
|
|
1241
|
+
* // User can manage licenses - show admin UI
|
|
1242
|
+
* }
|
|
1243
|
+
* ```
|
|
1244
|
+
*/
|
|
1245
|
+
hasTenantPermission(request: RequestLike, permission: string): Promise<boolean>;
|
|
1246
|
+
/**
|
|
1247
|
+
* Check app permission from JWT (no API call)
|
|
1248
|
+
* Returns true if user has the specified app permission.
|
|
1249
|
+
*
|
|
1250
|
+
* Reads from the `app_permissions` claim in the JWT.
|
|
1251
|
+
*
|
|
1252
|
+
* @example
|
|
1253
|
+
* ```ts
|
|
1254
|
+
* if (await authvital.hasAppPermission(req, 'projects:create')) {
|
|
1255
|
+
* // User can create projects
|
|
1256
|
+
* }
|
|
1257
|
+
* ```
|
|
1258
|
+
*/
|
|
1259
|
+
hasAppPermission(request: RequestLike, permission: string): Promise<boolean>;
|
|
1260
|
+
/**
|
|
1261
|
+
* Check feature from JWT license claim (no API call)
|
|
1262
|
+
* Returns true if the feature is enabled in the user's license.
|
|
1263
|
+
*
|
|
1264
|
+
* Reads from the `license.features` array in the JWT.
|
|
1265
|
+
*
|
|
1266
|
+
* @example
|
|
1267
|
+
* ```ts
|
|
1268
|
+
* if (await authvital.hasFeatureFromJwt(req, 'sso')) {
|
|
1269
|
+
* // User's tenant has SSO enabled
|
|
1270
|
+
* }
|
|
1271
|
+
* ```
|
|
1272
|
+
*/
|
|
1273
|
+
hasFeatureFromJwt(request: RequestLike, featureKey: string): Promise<boolean>;
|
|
1274
|
+
/**
|
|
1275
|
+
* Get license type from JWT (no API call)
|
|
1276
|
+
* Returns the license type slug (e.g., 'pro', 'enterprise') or null.
|
|
1277
|
+
*
|
|
1278
|
+
* Reads from the `license.type` claim in the JWT.
|
|
1279
|
+
*
|
|
1280
|
+
* @example
|
|
1281
|
+
* ```ts
|
|
1282
|
+
* const licenseType = await authvital.getLicenseTypeFromJwt(req);
|
|
1283
|
+
* if (licenseType === 'enterprise') {
|
|
1284
|
+
* // Show enterprise features
|
|
1285
|
+
* }
|
|
1286
|
+
* ```
|
|
1287
|
+
*/
|
|
1288
|
+
getLicenseTypeFromJwt(request: RequestLike): Promise<string | null>;
|
|
1289
|
+
/**
|
|
1290
|
+
* Get all tenant permissions from JWT (no API call)
|
|
1291
|
+
* Returns the array of tenant permissions granted to the user.
|
|
1292
|
+
*
|
|
1293
|
+
* @example
|
|
1294
|
+
* ```ts
|
|
1295
|
+
* const permissions = await authvital.getTenantPermissions(req);
|
|
1296
|
+
* console.log(permissions); // ['licenses:view', 'members:invite', ...]
|
|
1297
|
+
* ```
|
|
1298
|
+
*/
|
|
1299
|
+
getTenantPermissions(request: RequestLike): Promise<string[]>;
|
|
1300
|
+
/**
|
|
1301
|
+
* Get all app permissions from JWT (no API call)
|
|
1302
|
+
* Returns the array of app permissions granted to the user.
|
|
1303
|
+
*
|
|
1304
|
+
* @example
|
|
1305
|
+
* ```ts
|
|
1306
|
+
* const permissions = await authvital.getAppPermissions(req);
|
|
1307
|
+
* console.log(permissions); // ['projects:create', 'datasets:read', ...]
|
|
1308
|
+
* ```
|
|
1309
|
+
*/
|
|
1310
|
+
getAppPermissions(request: RequestLike): Promise<string[]>;
|
|
1311
|
+
/**
|
|
1312
|
+
* Get all tenant roles from JWT (no API call)
|
|
1313
|
+
* Returns the array of tenant role slugs for the user.
|
|
1314
|
+
*
|
|
1315
|
+
* @example
|
|
1316
|
+
* ```ts
|
|
1317
|
+
* const roles = await authvital.getTenantRoles(req);
|
|
1318
|
+
* console.log(roles); // ['owner', 'admin']
|
|
1319
|
+
* ```
|
|
1320
|
+
*/
|
|
1321
|
+
getTenantRoles(request: RequestLike): Promise<string[]>;
|
|
1322
|
+
/**
|
|
1323
|
+
* Get all app roles from JWT (no API call)
|
|
1324
|
+
* Returns the array of app role slugs for the user.
|
|
1325
|
+
*
|
|
1326
|
+
* @example
|
|
1327
|
+
* ```ts
|
|
1328
|
+
* const roles = await authvital.getAppRoles(req);
|
|
1329
|
+
* console.log(roles); // ['editor', 'viewer']
|
|
1330
|
+
* ```
|
|
1331
|
+
*/
|
|
1332
|
+
getAppRoles(request: RequestLike): Promise<string[]>;
|
|
1333
|
+
/**
|
|
1334
|
+
* Get URL for tenant members management page
|
|
1335
|
+
* Extracts tenantId from the request JWT
|
|
1336
|
+
*/
|
|
1337
|
+
getMembersUrl(req: RequestLike): Promise<string>;
|
|
1338
|
+
/**
|
|
1339
|
+
* Get URL for tenant applications management page
|
|
1340
|
+
* Extracts tenantId from the request JWT
|
|
1341
|
+
*/
|
|
1342
|
+
getApplicationsUrl(req: RequestLike): Promise<string>;
|
|
1343
|
+
/**
|
|
1344
|
+
* Get URL for tenant settings page
|
|
1345
|
+
* Extracts tenantId from the request JWT
|
|
1346
|
+
*/
|
|
1347
|
+
getSettingsUrl(req: RequestLike): Promise<string>;
|
|
1348
|
+
/**
|
|
1349
|
+
* Get URL for tenant overview page
|
|
1350
|
+
* Extracts tenantId from the request JWT
|
|
1351
|
+
*/
|
|
1352
|
+
getOverviewUrl(req: RequestLike): Promise<string>;
|
|
1353
|
+
/**
|
|
1354
|
+
* Get URL for user account settings page
|
|
1355
|
+
* (Does not require tenantId)
|
|
1356
|
+
*/
|
|
1357
|
+
getAccountSettingsUrl(): string;
|
|
1358
|
+
/**
|
|
1359
|
+
* Get all management URLs at once
|
|
1360
|
+
* Extracts tenantId from the request JWT
|
|
1361
|
+
*
|
|
1362
|
+
* @example
|
|
1363
|
+
* ```typescript
|
|
1364
|
+
* const urls = await authvital.getManagementUrls(req);
|
|
1365
|
+
* res.json({ urls });
|
|
1366
|
+
* // {
|
|
1367
|
+
* // overview: 'https://auth.example.com/tenant/abc/overview',
|
|
1368
|
+
* // members: 'https://auth.example.com/tenant/abc/members',
|
|
1369
|
+
* // applications: 'https://auth.example.com/tenant/abc/applications',
|
|
1370
|
+
* // settings: 'https://auth.example.com/tenant/abc/settings',
|
|
1371
|
+
* // accountSettings: 'https://auth.example.com/account/settings',
|
|
1372
|
+
* // }
|
|
1373
|
+
* ```
|
|
1374
|
+
*/
|
|
1375
|
+
getManagementUrls(req: RequestLike): Promise<{
|
|
1376
|
+
overview: string;
|
|
1377
|
+
members: string;
|
|
1378
|
+
applications: string;
|
|
1379
|
+
settings: string;
|
|
1380
|
+
accountSettings: string;
|
|
1381
|
+
}>;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Create an AuthVital client instance.
|
|
1385
|
+
*
|
|
1386
|
+
* Configure once at startup, use everywhere.
|
|
1387
|
+
*
|
|
1388
|
+
* @example
|
|
1389
|
+
* ```ts
|
|
1390
|
+
* // lib/authvital.ts
|
|
1391
|
+
* import { createAuthVital } from '@authvital/sdk/server';
|
|
1392
|
+
*
|
|
1393
|
+
* export const authvital = createAuthVital({
|
|
1394
|
+
* authVitalHost: process.env.AV_HOST!,
|
|
1395
|
+
* clientId: process.env.AV_CLIENT_ID!,
|
|
1396
|
+
* clientSecret: process.env.AV_CLIENT_SECRET!,
|
|
1397
|
+
* });
|
|
1398
|
+
*
|
|
1399
|
+
* // Then use it anywhere:
|
|
1400
|
+
* import { authvital } from '@/lib/authvital';
|
|
1401
|
+
*
|
|
1402
|
+
* // Validate JWT from request (uses cached JWKS, no IDP auth needed)
|
|
1403
|
+
* const { user } = await authvital.getCurrentUser(request);
|
|
1404
|
+
*
|
|
1405
|
+
* // M2M calls (uses client_credentials automatically)
|
|
1406
|
+
* const members = await authvital.memberships.listForTenant('tenant-123');
|
|
1407
|
+
* ```
|
|
1408
|
+
*/
|
|
1409
|
+
declare function createAuthVital(config: AuthVitalConfig): AuthVital;
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* @authvital/sdk - Invitations Namespace
|
|
1413
|
+
*
|
|
1414
|
+
* Manage tenant invitations: send, list pending, resend, and revoke.
|
|
1415
|
+
*/
|
|
1416
|
+
|
|
1417
|
+
/**
|
|
1418
|
+
* Creates the invitations namespace with all invitation-related methods.
|
|
1419
|
+
*
|
|
1420
|
+
* @param client - The base client instance for making authenticated requests
|
|
1421
|
+
* @returns Object containing all invitation methods
|
|
1422
|
+
*/
|
|
1423
|
+
declare function createInvitationsNamespace(client: BaseClient): {
|
|
1424
|
+
/**
|
|
1425
|
+
* Send an invitation to join a tenant
|
|
1426
|
+
*
|
|
1427
|
+
* Automatically validates JWT and uses tenantId from it.
|
|
1428
|
+
* If a user with this email already exists, uses that user.
|
|
1429
|
+
* If not, creates a new user with the provided details.
|
|
1430
|
+
* Returns only the user's sub (ID) and invitation expiry for security.
|
|
1431
|
+
*
|
|
1432
|
+
* @example
|
|
1433
|
+
* ```ts
|
|
1434
|
+
* // First, get the role ID from available roles
|
|
1435
|
+
* const { roles } = await authvital.memberships.getTenantRoles();
|
|
1436
|
+
* const adminRole = roles.find(r => r.slug === 'admin');
|
|
1437
|
+
*
|
|
1438
|
+
* const { sub, expiresAt } = await authvital.invitations.send(request, {
|
|
1439
|
+
* email: 'newuser@example.com',
|
|
1440
|
+
* givenName: 'John',
|
|
1441
|
+
* familyName: 'Doe',
|
|
1442
|
+
* roleId: adminRole?.id, // Use role ID, not slug
|
|
1443
|
+
* });
|
|
1444
|
+
* // sub = user's ID (can be used in your app's database)
|
|
1445
|
+
* // expiresAt = when the invitation expires
|
|
1446
|
+
* ```
|
|
1447
|
+
*/
|
|
1448
|
+
send: (request: RequestLike, params: Omit<SendInvitationParams, "tenantId">) => Promise<InvitationResponse>;
|
|
1449
|
+
/**
|
|
1450
|
+
* Get all pending invitations for a tenant
|
|
1451
|
+
*
|
|
1452
|
+
* Automatically validates JWT and uses tenantId from it.
|
|
1453
|
+
*
|
|
1454
|
+
* @example
|
|
1455
|
+
* ```ts
|
|
1456
|
+
* const { invitations, totalCount } = await authvital.invitations.listPending(request);
|
|
1457
|
+
* ```
|
|
1458
|
+
*/
|
|
1459
|
+
listPending: (request: RequestLike) => Promise<PendingInvitationsResponse>;
|
|
1460
|
+
/**
|
|
1461
|
+
* Resend an invitation (generates new token, extends expiry)
|
|
1462
|
+
*
|
|
1463
|
+
* Automatically validates JWT and uses tenantId from it.
|
|
1464
|
+
* Returns the new expiration date
|
|
1465
|
+
*
|
|
1466
|
+
* @example
|
|
1467
|
+
* ```ts
|
|
1468
|
+
* const { expiresAt } = await authvital.invitations.resend(request, {
|
|
1469
|
+
* invitationId: 'inv-123',
|
|
1470
|
+
* expiresInDays: 7,
|
|
1471
|
+
* });
|
|
1472
|
+
* ```
|
|
1473
|
+
*/
|
|
1474
|
+
resend: (request: RequestLike, params: ResendInvitationParams) => Promise<{
|
|
1475
|
+
expiresAt: string;
|
|
1476
|
+
}>;
|
|
1477
|
+
/**
|
|
1478
|
+
* Revoke an invitation
|
|
1479
|
+
*
|
|
1480
|
+
* @example
|
|
1481
|
+
* ```ts
|
|
1482
|
+
* await authvital.invitations.revoke(request, 'inv-123');
|
|
1483
|
+
* ```
|
|
1484
|
+
*/
|
|
1485
|
+
revoke: (request: RequestLike, invitationId: string) => Promise<RevokeInvitationResponse>;
|
|
1486
|
+
};
|
|
1487
|
+
type InvitationsNamespace = ReturnType<typeof createInvitationsNamespace>;
|
|
1488
|
+
|
|
1489
|
+
/**
|
|
1490
|
+
* @authvital/sdk - Memberships Namespace
|
|
1491
|
+
*
|
|
1492
|
+
* Manage tenant and application memberships, roles, and user access.
|
|
1493
|
+
*/
|
|
1494
|
+
|
|
1495
|
+
/**
|
|
1496
|
+
* Creates the memberships namespace with all membership-related methods.
|
|
1497
|
+
*
|
|
1498
|
+
* @param client - The base client instance for making authenticated requests
|
|
1499
|
+
* @returns Object containing all membership methods
|
|
1500
|
+
*/
|
|
1501
|
+
declare function createMembershipsNamespace(client: BaseClient): {
|
|
1502
|
+
/**
|
|
1503
|
+
* Get all memberships for the authenticated user's tenant
|
|
1504
|
+
*
|
|
1505
|
+
* Automatically validates JWT and uses tenantId from it.
|
|
1506
|
+
*
|
|
1507
|
+
* @param request - The incoming HTTP request
|
|
1508
|
+
* @param options - Optional filters and configuration
|
|
1509
|
+
*
|
|
1510
|
+
* @example
|
|
1511
|
+
* ```ts
|
|
1512
|
+
* app.get('/api/team', async (req, res) => {
|
|
1513
|
+
* const members = await authvital.memberships.listForTenant(req, {
|
|
1514
|
+
* status: 'ACTIVE',
|
|
1515
|
+
* });
|
|
1516
|
+
* res.json(members);
|
|
1517
|
+
* });
|
|
1518
|
+
* ```
|
|
1519
|
+
*/
|
|
1520
|
+
listForTenant: (request: RequestLike, options?: {
|
|
1521
|
+
status?: "ACTIVE" | "INVITED" | "SUSPENDED";
|
|
1522
|
+
includeRoles?: boolean;
|
|
1523
|
+
/** Append client_id to initiateLoginUri using SDK's configured clientId */
|
|
1524
|
+
appendClientId?: boolean;
|
|
1525
|
+
}) => Promise<TenantMembershipsResponse>;
|
|
1526
|
+
/**
|
|
1527
|
+
* Get all memberships for your application in the authenticated user's tenant
|
|
1528
|
+
*
|
|
1529
|
+
* Automatically uses clientId from SDK config and tenantId from the validated JWT.
|
|
1530
|
+
*
|
|
1531
|
+
* @param request - The incoming HTTP request
|
|
1532
|
+
* @param options - Optional filters and configuration
|
|
1533
|
+
*
|
|
1534
|
+
* @example
|
|
1535
|
+
* ```ts
|
|
1536
|
+
* app.get('/api/members', async (req, res) => {
|
|
1537
|
+
* const { memberships } = await authvital.memberships.listForApplication(req);
|
|
1538
|
+
* res.json(memberships);
|
|
1539
|
+
* });
|
|
1540
|
+
* ```
|
|
1541
|
+
*/
|
|
1542
|
+
listForApplication: (request: RequestLike, options?: {
|
|
1543
|
+
status?: "ACTIVE" | "INVITED" | "SUSPENDED";
|
|
1544
|
+
/** Append client_id to each tenant's initiateLoginUri using SDK's configured clientId */
|
|
1545
|
+
appendClientId?: boolean;
|
|
1546
|
+
}) => Promise<ApplicationMembershipsResponse>;
|
|
1547
|
+
/**
|
|
1548
|
+
* Validate that the authenticated user is a member of their tenant
|
|
1549
|
+
*
|
|
1550
|
+
* @param request - The incoming HTTP request
|
|
1551
|
+
*/
|
|
1552
|
+
validate: (request: RequestLike) => Promise<ValidateMembershipResponse>;
|
|
1553
|
+
/**
|
|
1554
|
+
* Get all tenants for the authenticated user
|
|
1555
|
+
*
|
|
1556
|
+
* Returns all tenants the user is a member of, with optional role information.
|
|
1557
|
+
*
|
|
1558
|
+
* @param request - The incoming HTTP request
|
|
1559
|
+
* @param options - Optional filters and configuration
|
|
1560
|
+
*
|
|
1561
|
+
* @example
|
|
1562
|
+
* ```ts
|
|
1563
|
+
* app.get('/api/my-tenants', async (req, res) => {
|
|
1564
|
+
* const result = await authvital.memberships.listTenantsForUser(req, {
|
|
1565
|
+
* status: 'ACTIVE',
|
|
1566
|
+
* appendClientId: true,
|
|
1567
|
+
* });
|
|
1568
|
+
* res.json(result.memberships);
|
|
1569
|
+
* });
|
|
1570
|
+
* ```
|
|
1571
|
+
*/
|
|
1572
|
+
listTenantsForUser: (request: RequestLike, options?: {
|
|
1573
|
+
status?: "ACTIVE" | "INVITED" | "SUSPENDED";
|
|
1574
|
+
includeRoles?: boolean;
|
|
1575
|
+
/** Append client_id to each tenant's initiateLoginUri using SDK's configured clientId */
|
|
1576
|
+
appendClientId?: boolean;
|
|
1577
|
+
}) => Promise<UserTenantsResponse>;
|
|
1578
|
+
/**
|
|
1579
|
+
* Get all available tenant roles (IDP-level)
|
|
1580
|
+
*
|
|
1581
|
+
* Returns the role definitions (owner, admin, member, etc.) that can be
|
|
1582
|
+
* assigned to memberships. These are instance-wide, not tenant-specific.
|
|
1583
|
+
* Use this to populate a role picker dropdown.
|
|
1584
|
+
*
|
|
1585
|
+
* @example
|
|
1586
|
+
* ```ts
|
|
1587
|
+
* app.get('/api/roles', async (req, res) => {
|
|
1588
|
+
* const { roles } = await authvital.memberships.getTenantRoles();
|
|
1589
|
+
* res.json(roles);
|
|
1590
|
+
* // [{ slug: 'owner', name: 'Owner', ... }, { slug: 'admin', ... }, ...]
|
|
1591
|
+
* });
|
|
1592
|
+
* ```
|
|
1593
|
+
*/
|
|
1594
|
+
getTenantRoles: () => Promise<TenantRolesResponse>;
|
|
1595
|
+
/**
|
|
1596
|
+
* Get all roles for your application
|
|
1597
|
+
*
|
|
1598
|
+
* Returns the role definitions (admin, editor, viewer, etc.) specific to
|
|
1599
|
+
* your application. Uses the clientId from SDK config automatically.
|
|
1600
|
+
* Use this to populate a role picker for invite flows or role assignment.
|
|
1601
|
+
*
|
|
1602
|
+
* NOTE: These are APPLICATION-specific roles, different from tenant roles
|
|
1603
|
+
* (owner/admin/member). Application roles are used for fine-grained
|
|
1604
|
+
* permissions within your app.
|
|
1605
|
+
*
|
|
1606
|
+
* @example
|
|
1607
|
+
* ```ts
|
|
1608
|
+
* app.get('/api/app-roles', async (req, res) => {
|
|
1609
|
+
* const { roles } = await authvital.memberships.getApplicationRoles();
|
|
1610
|
+
* res.json(roles);
|
|
1611
|
+
* // [{ slug: 'admin', name: 'Admin', permissions: [...] }, ...]
|
|
1612
|
+
* });
|
|
1613
|
+
*
|
|
1614
|
+
* // Use in invite flow:
|
|
1615
|
+
* const { roles } = await authvital.memberships.getApplicationRoles();
|
|
1616
|
+
* const editorRole = roles.find(r => r.slug === 'editor');
|
|
1617
|
+
* await authvital.invitations.send(request, {
|
|
1618
|
+
* email: 'user@example.com',
|
|
1619
|
+
* roleId: editorRole?.id,
|
|
1620
|
+
* });
|
|
1621
|
+
* ```
|
|
1622
|
+
*/
|
|
1623
|
+
getApplicationRoles: () => Promise<ApplicationRolesResponse>;
|
|
1624
|
+
/**
|
|
1625
|
+
* Set a member's tenant role (replaces any existing roles)
|
|
1626
|
+
*
|
|
1627
|
+
* Performs a pre-flight check using the caller's JWT to catch obvious
|
|
1628
|
+
* permission violations before calling the IDP. The IDP then does the
|
|
1629
|
+
* full authoritative check (e.g., admin can't demote an owner).
|
|
1630
|
+
*
|
|
1631
|
+
* Role hierarchy: owner > admin > member
|
|
1632
|
+
* - Owners can change anyone's role
|
|
1633
|
+
* - Admins can change admins and members, but cannot touch owners or promote to owner
|
|
1634
|
+
* - Members cannot change roles
|
|
1635
|
+
*
|
|
1636
|
+
* @param request - The incoming HTTP request (used to read caller's JWT)
|
|
1637
|
+
* @param membershipId - The membership to update
|
|
1638
|
+
* @param roleSlug - The role slug to set (e.g., 'admin', 'member', 'owner')
|
|
1639
|
+
*
|
|
1640
|
+
* @example
|
|
1641
|
+
* ```ts
|
|
1642
|
+
* app.put('/api/team/:membershipId/role', async (req, res) => {
|
|
1643
|
+
* const result = await authvital.memberships.setMemberRole(
|
|
1644
|
+
* req,
|
|
1645
|
+
* req.params.membershipId,
|
|
1646
|
+
* req.body.role, // e.g., 'admin'
|
|
1647
|
+
* );
|
|
1648
|
+
* res.json(result.role); // { id, name, slug }
|
|
1649
|
+
* });
|
|
1650
|
+
* ```
|
|
1651
|
+
*/
|
|
1652
|
+
setMemberRole: (request: RequestLike, membershipId: string, roleSlug: string) => Promise<SetMemberRoleResponse>;
|
|
1653
|
+
};
|
|
1654
|
+
type MembershipsNamespace = ReturnType<typeof createMembershipsNamespace>;
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* @authvital/sdk - Permissions Namespace
|
|
1658
|
+
*
|
|
1659
|
+
* Check and list user permissions for tenant-scoped operations.
|
|
1660
|
+
*/
|
|
1661
|
+
|
|
1662
|
+
/**
|
|
1663
|
+
* Creates the permissions namespace with all permission-related methods.
|
|
1664
|
+
*
|
|
1665
|
+
* @param client - The base client instance for making authenticated requests
|
|
1666
|
+
* @returns Object containing all permission methods
|
|
1667
|
+
*/
|
|
1668
|
+
declare function createPermissionsNamespace(client: BaseClient): {
|
|
1669
|
+
/**
|
|
1670
|
+
* Check if the authenticated user has a specific permission
|
|
1671
|
+
*
|
|
1672
|
+
* @param request - The incoming HTTP request
|
|
1673
|
+
* @param permission - The permission to check (e.g., 'users:write')
|
|
1674
|
+
*
|
|
1675
|
+
* @example
|
|
1676
|
+
* ```ts
|
|
1677
|
+
* app.post('/api/users', async (req, res) => {
|
|
1678
|
+
* const { allowed } = await authvital.permissions.check(req, 'users:write');
|
|
1679
|
+
* if (!allowed) return res.status(403).json({ error: 'Forbidden' });
|
|
1680
|
+
* // ... create user
|
|
1681
|
+
* });
|
|
1682
|
+
* ```
|
|
1683
|
+
*/
|
|
1684
|
+
check: (request: RequestLike, permission: string) => Promise<CheckPermissionResult>;
|
|
1685
|
+
/**
|
|
1686
|
+
* Check multiple permissions at once for the authenticated user
|
|
1687
|
+
*
|
|
1688
|
+
* @param request - The incoming HTTP request
|
|
1689
|
+
* @param permissions - Array of permissions to check
|
|
1690
|
+
*
|
|
1691
|
+
* @example
|
|
1692
|
+
* ```ts
|
|
1693
|
+
* const { results } = await authvital.permissions.checkMany(req, ['users:read', 'users:write']);
|
|
1694
|
+
* // results = { 'users:read': true, 'users:write': false }
|
|
1695
|
+
* ```
|
|
1696
|
+
*/
|
|
1697
|
+
checkMany: (request: RequestLike, permissions: string[]) => Promise<CheckPermissionsResult>;
|
|
1698
|
+
/**
|
|
1699
|
+
* Get all permissions for the authenticated user
|
|
1700
|
+
*
|
|
1701
|
+
* @param request - The incoming HTTP request
|
|
1702
|
+
*
|
|
1703
|
+
* @example
|
|
1704
|
+
* ```ts
|
|
1705
|
+
* app.get('/api/my-permissions', async (req, res) => {
|
|
1706
|
+
* const perms = await authvital.permissions.list(req);
|
|
1707
|
+
* res.json(perms);
|
|
1708
|
+
* });
|
|
1709
|
+
* ```
|
|
1710
|
+
*/
|
|
1711
|
+
list: (request: RequestLike) => Promise<UserPermissions>;
|
|
1712
|
+
};
|
|
1713
|
+
type PermissionsNamespace = ReturnType<typeof createPermissionsNamespace>;
|
|
1714
|
+
|
|
1715
|
+
/**
|
|
1716
|
+
* @authvital/sdk - Entitlements Namespace (The Gatekeeper 🛡️)
|
|
1717
|
+
*
|
|
1718
|
+
* Check quotas, features, and entitlements for tenant-scoped operations.
|
|
1719
|
+
*/
|
|
1720
|
+
|
|
1721
|
+
/**
|
|
1722
|
+
* Creates the entitlements namespace with all entitlement-related methods.
|
|
1723
|
+
*
|
|
1724
|
+
* @param client - The base client instance for making authenticated requests
|
|
1725
|
+
* @returns Object containing all entitlement methods
|
|
1726
|
+
*/
|
|
1727
|
+
declare function createEntitlementsNamespace(client: BaseClient): {
|
|
1728
|
+
/**
|
|
1729
|
+
* Check if a quota-based action is allowed
|
|
1730
|
+
*
|
|
1731
|
+
* This is the main "gatekeeper" function. Use it before any quota-consuming action.
|
|
1732
|
+
*
|
|
1733
|
+
* @param request - The incoming HTTP request
|
|
1734
|
+
* @param featureKey - The quota to check (e.g., 'seats', 'projects')
|
|
1735
|
+
* @param options - Optional: appScope and incrementCount
|
|
1736
|
+
*
|
|
1737
|
+
* @example
|
|
1738
|
+
* ```ts
|
|
1739
|
+
* // Before adding a team member
|
|
1740
|
+
* const check = await authvital.entitlements.canPerform(req, 'seats');
|
|
1741
|
+
* if (!check.allowed) {
|
|
1742
|
+
* return res.status(403).json({ error: check.reason });
|
|
1743
|
+
* }
|
|
1744
|
+
* // Add the member...
|
|
1745
|
+
* await authvital.entitlements.incrementUsage(req, 'seats');
|
|
1746
|
+
* ```
|
|
1747
|
+
*/
|
|
1748
|
+
canPerform: (request: RequestLike, featureKey: string, _options?: {
|
|
1749
|
+
appScope?: string;
|
|
1750
|
+
incrementCount?: number;
|
|
1751
|
+
}) => Promise<QuotaCheckResult>;
|
|
1752
|
+
/**
|
|
1753
|
+
* Decrement usage for a quota (call after removing resource)
|
|
1754
|
+
*
|
|
1755
|
+
* @param request - The incoming HTTP request
|
|
1756
|
+
* @param featureKey - The quota to decrement (e.g., 'seats')
|
|
1757
|
+
* @param options - Optional: appScope and amount to decrement by
|
|
1758
|
+
*
|
|
1759
|
+
* @example
|
|
1760
|
+
* ```ts
|
|
1761
|
+
* // After removing a team member
|
|
1762
|
+
* await authvital.entitlements.decrementUsage(req, 'seats');
|
|
1763
|
+
* ```
|
|
1764
|
+
*/
|
|
1765
|
+
decrementUsage: (request: RequestLike, featureKey: string, options?: {
|
|
1766
|
+
appScope?: string;
|
|
1767
|
+
by?: number;
|
|
1768
|
+
}) => Promise<{
|
|
1769
|
+
newUsage: number;
|
|
1770
|
+
}>;
|
|
1771
|
+
};
|
|
1772
|
+
type EntitlementsNamespace = ReturnType<typeof createEntitlementsNamespace>;
|
|
1773
|
+
|
|
1774
|
+
/**
|
|
1775
|
+
* @authvital/sdk - License Types
|
|
1776
|
+
*
|
|
1777
|
+
* Shared types for the licenses namespace.
|
|
1778
|
+
*/
|
|
1779
|
+
interface LicenseGrantResponse {
|
|
1780
|
+
assignmentId: string;
|
|
1781
|
+
message: string;
|
|
1782
|
+
}
|
|
1783
|
+
interface LicenseRevokeResponse {
|
|
1784
|
+
message: string;
|
|
1785
|
+
}
|
|
1786
|
+
interface LicenseCheckResponse {
|
|
1787
|
+
hasLicense: boolean;
|
|
1788
|
+
licenseType: string | null;
|
|
1789
|
+
features?: string[];
|
|
1790
|
+
}
|
|
1791
|
+
interface LicenseFeatureResponse {
|
|
1792
|
+
hasFeature: boolean;
|
|
1793
|
+
}
|
|
1794
|
+
interface LicensedUser {
|
|
1795
|
+
id: string;
|
|
1796
|
+
userId: string;
|
|
1797
|
+
email: string;
|
|
1798
|
+
licenseType: string;
|
|
1799
|
+
}
|
|
1800
|
+
interface LicenseHolder {
|
|
1801
|
+
userId: string;
|
|
1802
|
+
userEmail: string;
|
|
1803
|
+
userName?: string;
|
|
1804
|
+
licenseTypeId: string;
|
|
1805
|
+
licenseTypeName: string;
|
|
1806
|
+
assignedAt: string;
|
|
1807
|
+
}
|
|
1808
|
+
interface LicenseAuditLogEntry {
|
|
1809
|
+
id: string;
|
|
1810
|
+
userId: string;
|
|
1811
|
+
userEmail: string;
|
|
1812
|
+
userName?: string;
|
|
1813
|
+
applicationId: string;
|
|
1814
|
+
applicationName: string;
|
|
1815
|
+
licenseTypeId: string;
|
|
1816
|
+
licenseTypeName: string;
|
|
1817
|
+
action: 'GRANTED' | 'REVOKED' | 'CHANGED';
|
|
1818
|
+
previousLicenseTypeName?: string;
|
|
1819
|
+
performedBy: string;
|
|
1820
|
+
performedAt?: string;
|
|
1821
|
+
reason?: string;
|
|
1822
|
+
}
|
|
1823
|
+
interface LicenseAuditLogResponse {
|
|
1824
|
+
entries: LicenseAuditLogEntry[];
|
|
1825
|
+
total: number;
|
|
1826
|
+
limit: number;
|
|
1827
|
+
offset: number;
|
|
1828
|
+
}
|
|
1829
|
+
interface UsageOverviewResponse {
|
|
1830
|
+
tenantId: string;
|
|
1831
|
+
applications: Array<{
|
|
1832
|
+
applicationId: string;
|
|
1833
|
+
applicationName: string;
|
|
1834
|
+
licenseTypeName: string;
|
|
1835
|
+
totalSeats: number;
|
|
1836
|
+
seatsAssigned: number;
|
|
1837
|
+
seatsAvailable: number;
|
|
1838
|
+
utilizationPercentage: number;
|
|
1839
|
+
}>;
|
|
1840
|
+
totalSeatsAcrossAllApps: number;
|
|
1841
|
+
totalSeatsAssigned: number;
|
|
1842
|
+
overallUtilization: number;
|
|
1843
|
+
hasOverage: boolean;
|
|
1844
|
+
overageApplications: string[];
|
|
1845
|
+
}
|
|
1846
|
+
interface UsageTrendEntry {
|
|
1847
|
+
date: string;
|
|
1848
|
+
seatsAssigned: number;
|
|
1849
|
+
seatsAvailable: number;
|
|
1850
|
+
utilizationPercentage: number;
|
|
1851
|
+
}
|
|
1852
|
+
interface UserLicenseListItem {
|
|
1853
|
+
id: string;
|
|
1854
|
+
licenseTypeId: string;
|
|
1855
|
+
licenseTypeName: string;
|
|
1856
|
+
licenseTypeSlug: string;
|
|
1857
|
+
applicationId: string;
|
|
1858
|
+
applicationName: string;
|
|
1859
|
+
assignedAt: string;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
/**
|
|
1863
|
+
* Creates the licenses namespace with all license-related methods.
|
|
1864
|
+
*
|
|
1865
|
+
* Combines:
|
|
1866
|
+
* - User-scoped operations (JWT auth): grant, revoke, check, etc.
|
|
1867
|
+
* - Admin operations (M2M): tenant overview, bulk operations, etc.
|
|
1868
|
+
*
|
|
1869
|
+
* @param client - The base client instance for making authenticated requests
|
|
1870
|
+
* @returns Object containing all license methods
|
|
1871
|
+
*/
|
|
1872
|
+
declare function createLicensesNamespace(client: BaseClient): {
|
|
1873
|
+
getTenantOverview: (tenantId: string) => Promise<TenantLicenseOverview>;
|
|
1874
|
+
getUserLicenses: (tenantId: string, userId: string) => Promise<UserLicenseAssignment[]>;
|
|
1875
|
+
getTenantSubscriptions: (tenantId: string) => Promise<SubscriptionSummary[]>;
|
|
1876
|
+
getMembersWithLicenses: (tenantId: string) => Promise<MemberWithLicenses[]>;
|
|
1877
|
+
getAvailableLicenseTypes: (tenantId: string) => Promise<AvailableLicenseType[]>;
|
|
1878
|
+
grantToUser: (params: GrantLicenseParams) => Promise<UserLicenseAssignment>;
|
|
1879
|
+
revokeFromUser: (params: RevokeLicenseParams) => Promise<void>;
|
|
1880
|
+
changeUserType: (params: ChangeLicenseTypeParams) => Promise<UserLicenseAssignment>;
|
|
1881
|
+
grantBulk: (assignments: GrantLicenseParams[]) => Promise<BulkGrantLicenseResult[]>;
|
|
1882
|
+
revokeBulk: (revocations: RevokeLicenseParams[]) => Promise<BulkRevokeLicenseResult>;
|
|
1883
|
+
grant: (request: RequestLike, options: {
|
|
1884
|
+
userId?: string;
|
|
1885
|
+
applicationId: string;
|
|
1886
|
+
licenseTypeId: string;
|
|
1887
|
+
}) => Promise<LicenseGrantResponse>;
|
|
1888
|
+
revoke: (request: RequestLike, options: {
|
|
1889
|
+
userId?: string;
|
|
1890
|
+
applicationId: string;
|
|
1891
|
+
}) => Promise<LicenseRevokeResponse>;
|
|
1892
|
+
changeType: (request: RequestLike, options: {
|
|
1893
|
+
userId?: string;
|
|
1894
|
+
applicationId: string;
|
|
1895
|
+
newLicenseTypeId: string;
|
|
1896
|
+
}) => Promise<LicenseRevokeResponse>;
|
|
1897
|
+
listForUser: (request: RequestLike, userId?: string) => Promise<UserLicenseListItem[]>;
|
|
1898
|
+
check: (request: RequestLike, userId: string | undefined, applicationId: string) => Promise<LicenseCheckResponse>;
|
|
1899
|
+
hasFeature: (request: RequestLike, userId: string | undefined, applicationId: string, featureKey: string) => Promise<LicenseFeatureResponse>;
|
|
1900
|
+
getAppLicensedUsers: (request: RequestLike, applicationId: string) => Promise<LicensedUser[]>;
|
|
1901
|
+
countLicensedUsers: (request: RequestLike, applicationId: string) => Promise<{
|
|
1902
|
+
count: number;
|
|
1903
|
+
}>;
|
|
1904
|
+
getUserLicenseType: (request: RequestLike, userId: string | undefined, applicationId: string) => Promise<string | null>;
|
|
1905
|
+
getHolders: (request: RequestLike, applicationId: string) => Promise<LicenseHolder[]>;
|
|
1906
|
+
getAuditLog: (request: RequestLike, options?: {
|
|
1907
|
+
userId?: string;
|
|
1908
|
+
applicationId?: string;
|
|
1909
|
+
limit?: number;
|
|
1910
|
+
offset?: number;
|
|
1911
|
+
}) => Promise<LicenseAuditLogResponse>;
|
|
1912
|
+
getUsageOverview: (request: RequestLike) => Promise<UsageOverviewResponse>;
|
|
1913
|
+
getUsageTrends: (request: RequestLike, days?: number) => Promise<UsageTrendEntry[]>;
|
|
1914
|
+
};
|
|
1915
|
+
type LicensesNamespace = ReturnType<typeof createLicensesNamespace>;
|
|
1916
|
+
|
|
1917
|
+
/**
|
|
1918
|
+
* @authvital/sdk - Server-Side OAuth Flow
|
|
1919
|
+
*
|
|
1920
|
+
* Utilities for implementing OAuth 2.0 Authorization Code Flow with PKCE
|
|
1921
|
+
* on your backend. Use this when you need to handle OAuth callbacks server-side.
|
|
1922
|
+
*
|
|
1923
|
+
* @example
|
|
1924
|
+
* ```ts
|
|
1925
|
+
* import { OAuthFlow } from '@authvital/sdk/server';
|
|
1926
|
+
*
|
|
1927
|
+
* const oauth = new OAuthFlow({
|
|
1928
|
+
* authVitalHost: process.env.AV_HOST,
|
|
1929
|
+
* clientId: process.env.AV_CLIENT_ID,
|
|
1930
|
+
* clientSecret: process.env.AV_CLIENT_SECRET,
|
|
1931
|
+
* redirectUri: 'https://myapp.com/api/auth/callback',
|
|
1932
|
+
* });
|
|
1933
|
+
*
|
|
1934
|
+
* // GET /api/auth/login
|
|
1935
|
+
* app.get('/api/auth/login', (req, res) => {
|
|
1936
|
+
* const { authorizeUrl, state, codeVerifier } = oauth.startFlow();
|
|
1937
|
+
* req.session.oauthState = state;
|
|
1938
|
+
* req.session.codeVerifier = codeVerifier;
|
|
1939
|
+
* res.redirect(authorizeUrl);
|
|
1940
|
+
* });
|
|
1941
|
+
*
|
|
1942
|
+
* // GET /api/auth/callback
|
|
1943
|
+
* app.get('/api/auth/callback', async (req, res) => {
|
|
1944
|
+
* const tokens = await oauth.handleCallback(
|
|
1945
|
+
* req.query.code,
|
|
1946
|
+
* req.query.state,
|
|
1947
|
+
* req.session.oauthState,
|
|
1948
|
+
* req.session.codeVerifier
|
|
1949
|
+
* );
|
|
1950
|
+
* req.session.accessToken = tokens.access_token;
|
|
1951
|
+
* res.redirect('/dashboard');
|
|
1952
|
+
* });
|
|
1953
|
+
* ```
|
|
1954
|
+
*/
|
|
1955
|
+
|
|
1956
|
+
/**
|
|
1957
|
+
* Generate a cryptographically secure code verifier
|
|
1958
|
+
*/
|
|
1959
|
+
declare function generateCodeVerifier(): string;
|
|
1960
|
+
/**
|
|
1961
|
+
* Generate code challenge from verifier (S256 method)
|
|
1962
|
+
*/
|
|
1963
|
+
declare function generateCodeChallenge(verifier: string): string;
|
|
1964
|
+
/**
|
|
1965
|
+
* Generate both PKCE values at once
|
|
1966
|
+
*/
|
|
1967
|
+
declare function generatePKCE(): {
|
|
1968
|
+
codeVerifier: string;
|
|
1969
|
+
codeChallenge: string;
|
|
1970
|
+
};
|
|
1971
|
+
/**
|
|
1972
|
+
* Payload structure for OAuth state parameter
|
|
1973
|
+
* Contains CSRF nonce and optional app-specific state
|
|
1974
|
+
*/
|
|
1975
|
+
interface StatePayload {
|
|
1976
|
+
csrf: string;
|
|
1977
|
+
appState?: string;
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Generate a secure random state for CSRF protection
|
|
1981
|
+
*/
|
|
1982
|
+
declare function generateState(): string;
|
|
1983
|
+
/**
|
|
1984
|
+
* Encode state payload as base64url JSON
|
|
1985
|
+
* Used to pass both CSRF nonce and app-specific state through OAuth flow
|
|
1986
|
+
*
|
|
1987
|
+
* @param csrf - CSRF nonce for security
|
|
1988
|
+
* @param appState - Optional app-specific state (e.g., return URL)
|
|
1989
|
+
*/
|
|
1990
|
+
declare function encodeState(csrf: string, appState?: string): string;
|
|
1991
|
+
/**
|
|
1992
|
+
* Decode state payload from base64url JSON
|
|
1993
|
+
* Returns null if decoding fails (invalid format)
|
|
1994
|
+
*
|
|
1995
|
+
* @param state - The encoded state string from OAuth callback
|
|
1996
|
+
*/
|
|
1997
|
+
declare function decodeState(state: string): StatePayload | null;
|
|
1998
|
+
/**
|
|
1999
|
+
* Encode state with embedded verifier (for stateless mode)
|
|
2000
|
+
* Format: {csrf}:{base64url_verifier}
|
|
2001
|
+
*
|
|
2002
|
+
* Use this if you don't want to store state server-side.
|
|
2003
|
+
*/
|
|
2004
|
+
declare function encodeStateWithVerifier(csrf: string, codeVerifier: string): string;
|
|
2005
|
+
/**
|
|
2006
|
+
* Decode state to extract CSRF and verifier
|
|
2007
|
+
*/
|
|
2008
|
+
declare function decodeStateWithVerifier(state: string): {
|
|
2009
|
+
csrf: string;
|
|
2010
|
+
codeVerifier: string;
|
|
2011
|
+
} | null;
|
|
2012
|
+
interface AuthorizeUrlParams {
|
|
2013
|
+
authVitalHost: string;
|
|
2014
|
+
clientId: string;
|
|
2015
|
+
redirectUri: string;
|
|
2016
|
+
state: string;
|
|
2017
|
+
codeChallenge: string;
|
|
2018
|
+
scope?: string;
|
|
2019
|
+
nonce?: string;
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* Build the OAuth authorize URL
|
|
2023
|
+
*/
|
|
2024
|
+
declare function buildAuthorizeUrl(params: AuthorizeUrlParams): string;
|
|
2025
|
+
interface TokenExchangeParams {
|
|
2026
|
+
authVitalHost: string;
|
|
2027
|
+
clientId: string;
|
|
2028
|
+
clientSecret?: string;
|
|
2029
|
+
code: string;
|
|
2030
|
+
codeVerifier: string;
|
|
2031
|
+
redirectUri: string;
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Exchange authorization code for tokens (SERVER-TO-SERVER)
|
|
2035
|
+
*/
|
|
2036
|
+
declare function exchangeCodeForTokens(params: TokenExchangeParams): Promise<TokenResponse>;
|
|
2037
|
+
interface RefreshTokenParams {
|
|
2038
|
+
authVitalHost: string;
|
|
2039
|
+
clientId: string;
|
|
2040
|
+
clientSecret?: string;
|
|
2041
|
+
refreshToken: string;
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Refresh access token using refresh token (SERVER-TO-SERVER)
|
|
2045
|
+
*/
|
|
2046
|
+
declare function refreshAccessToken(params: RefreshTokenParams): Promise<TokenResponse>;
|
|
2047
|
+
/**
|
|
2048
|
+
* Decode JWT payload (without verification - for extracting claims only)
|
|
2049
|
+
*/
|
|
2050
|
+
declare function decodeJwt<T = Record<string, unknown>>(token: string): T | null;
|
|
2051
|
+
/**
|
|
2052
|
+
* Helper class for managing the OAuth flow server-side
|
|
2053
|
+
*/
|
|
2054
|
+
declare class OAuthFlow {
|
|
2055
|
+
private config;
|
|
2056
|
+
constructor(config: OAuthFlowConfig);
|
|
2057
|
+
/**
|
|
2058
|
+
* Start the OAuth flow - generates PKCE, state, and authorize URL
|
|
2059
|
+
*
|
|
2060
|
+
* @param options.appState - Optional app-specific state to pass through OAuth (e.g., return URL)
|
|
2061
|
+
*/
|
|
2062
|
+
startFlow(options?: {
|
|
2063
|
+
appState?: string;
|
|
2064
|
+
}): {
|
|
2065
|
+
authorizeUrl: string;
|
|
2066
|
+
state: string;
|
|
2067
|
+
codeVerifier: string;
|
|
2068
|
+
codeChallenge: string;
|
|
2069
|
+
};
|
|
2070
|
+
/**
|
|
2071
|
+
* Handle the OAuth callback - verify state and exchange code for tokens
|
|
2072
|
+
*
|
|
2073
|
+
* @param code - Authorization code from callback
|
|
2074
|
+
* @param receivedState - State parameter from callback URL
|
|
2075
|
+
* @param expectedState - State that was stored when starting the flow
|
|
2076
|
+
* @param codeVerifier - PKCE code verifier that was stored when starting the flow
|
|
2077
|
+
* @returns Token response with optional appState that was passed through
|
|
2078
|
+
* @throws Error if state doesn't match (CSRF) or token exchange fails
|
|
2079
|
+
*/
|
|
2080
|
+
handleCallback(code: string, receivedState: string, expectedState: string, codeVerifier: string): Promise<TokenResponse & {
|
|
2081
|
+
appState?: string;
|
|
2082
|
+
}>;
|
|
2083
|
+
/**
|
|
2084
|
+
* Refresh tokens using refresh token
|
|
2085
|
+
*/
|
|
2086
|
+
refreshTokens(refreshToken: string): Promise<TokenResponse>;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
/**
|
|
2090
|
+
* @authvital/sdk - Server-Side URL Builders
|
|
2091
|
+
*
|
|
2092
|
+
* Simple URL builders for landing pages, emails, and other non-OAuth contexts.
|
|
2093
|
+
* Use these when you just need a link to the AuthVital login/signup page
|
|
2094
|
+
* WITHOUT the full PKCE ceremony.
|
|
2095
|
+
*
|
|
2096
|
+
* For full OAuth flows with PKCE, use OAuthFlow from './oauth-flow' instead.
|
|
2097
|
+
*
|
|
2098
|
+
* @example
|
|
2099
|
+
* ```ts
|
|
2100
|
+
* import { getSignupUrl, getLoginUrl } from '@authvital/sdk/server';
|
|
2101
|
+
*
|
|
2102
|
+
* // For a "Get Started" button on your landing page
|
|
2103
|
+
* const signupLink = getSignupUrl({
|
|
2104
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2105
|
+
* clientId: 'my-app',
|
|
2106
|
+
* redirectUri: 'https://app.myapp.com/dashboard',
|
|
2107
|
+
* });
|
|
2108
|
+
*
|
|
2109
|
+
* // For an email CTA
|
|
2110
|
+
* const loginLink = getLoginUrl({
|
|
2111
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2112
|
+
* clientId: 'my-app',
|
|
2113
|
+
* });
|
|
2114
|
+
* ```
|
|
2115
|
+
*/
|
|
2116
|
+
interface AuthUrlOptions {
|
|
2117
|
+
/** AuthVital server URL (e.g., https://auth.myapp.com) */
|
|
2118
|
+
authVitalHost: string;
|
|
2119
|
+
/** OAuth client_id for your application */
|
|
2120
|
+
clientId: string;
|
|
2121
|
+
/** Optional: Where to redirect after auth completes */
|
|
2122
|
+
redirectUri?: string;
|
|
2123
|
+
}
|
|
2124
|
+
interface SignupUrlOptions extends AuthUrlOptions {
|
|
2125
|
+
/** Optional: Pre-fill the email field */
|
|
2126
|
+
email?: string;
|
|
2127
|
+
/** Optional: Invitation token (for accepting team invites) */
|
|
2128
|
+
inviteToken?: string;
|
|
2129
|
+
}
|
|
2130
|
+
interface LoginUrlOptions extends AuthUrlOptions {
|
|
2131
|
+
/** Optional: Pre-fill the email field */
|
|
2132
|
+
email?: string;
|
|
2133
|
+
/** Optional: Hint for which tenant to log into */
|
|
2134
|
+
tenantHint?: string;
|
|
2135
|
+
}
|
|
2136
|
+
/**
|
|
2137
|
+
* Build a signup URL for landing pages, emails, etc.
|
|
2138
|
+
*
|
|
2139
|
+
* This is a simple redirect - NOT a full OAuth flow.
|
|
2140
|
+
* AuthVital will handle the OAuth redirect internally after signup.
|
|
2141
|
+
*
|
|
2142
|
+
* @example
|
|
2143
|
+
* ```ts
|
|
2144
|
+
* // Basic signup link
|
|
2145
|
+
* const url = getSignupUrl({
|
|
2146
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2147
|
+
* clientId: 'my-app',
|
|
2148
|
+
* });
|
|
2149
|
+
*
|
|
2150
|
+
* // With redirect after signup
|
|
2151
|
+
* const url = getSignupUrl({
|
|
2152
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2153
|
+
* clientId: 'my-app',
|
|
2154
|
+
* redirectUri: 'https://app.myapp.com/onboarding',
|
|
2155
|
+
* });
|
|
2156
|
+
*
|
|
2157
|
+
* // With pre-filled email (e.g., from waitlist)
|
|
2158
|
+
* const url = getSignupUrl({
|
|
2159
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2160
|
+
* clientId: 'my-app',
|
|
2161
|
+
* email: 'user@example.com',
|
|
2162
|
+
* });
|
|
2163
|
+
*
|
|
2164
|
+
* // For team invite acceptance
|
|
2165
|
+
* const url = getSignupUrl({
|
|
2166
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2167
|
+
* clientId: 'my-app',
|
|
2168
|
+
* inviteToken: 'abc123',
|
|
2169
|
+
* });
|
|
2170
|
+
* ```
|
|
2171
|
+
*/
|
|
2172
|
+
declare function getSignupUrl(options: SignupUrlOptions): string;
|
|
2173
|
+
/**
|
|
2174
|
+
* Build a login URL for landing pages, emails, etc.
|
|
2175
|
+
*
|
|
2176
|
+
* This is a simple redirect - NOT a full OAuth flow.
|
|
2177
|
+
* AuthVital will handle the OAuth redirect internally after login.
|
|
2178
|
+
*
|
|
2179
|
+
* @example
|
|
2180
|
+
* ```ts
|
|
2181
|
+
* // Basic login link
|
|
2182
|
+
* const url = getLoginUrl({
|
|
2183
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2184
|
+
* clientId: 'my-app',
|
|
2185
|
+
* });
|
|
2186
|
+
*
|
|
2187
|
+
* // With redirect after login
|
|
2188
|
+
* const url = getLoginUrl({
|
|
2189
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2190
|
+
* clientId: 'my-app',
|
|
2191
|
+
* redirectUri: 'https://app.myapp.com/dashboard',
|
|
2192
|
+
* });
|
|
2193
|
+
*
|
|
2194
|
+
* // With tenant hint (for multi-tenant apps)
|
|
2195
|
+
* const url = getLoginUrl({
|
|
2196
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2197
|
+
* clientId: 'my-app',
|
|
2198
|
+
* tenantHint: 'acme-corp',
|
|
2199
|
+
* });
|
|
2200
|
+
* ```
|
|
2201
|
+
*/
|
|
2202
|
+
declare function getLoginUrl(options: LoginUrlOptions): string;
|
|
2203
|
+
/**
|
|
2204
|
+
* Build a password reset URL.
|
|
2205
|
+
*
|
|
2206
|
+
* @example
|
|
2207
|
+
* ```ts
|
|
2208
|
+
* const url = getPasswordResetUrl({
|
|
2209
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2210
|
+
* clientId: 'my-app',
|
|
2211
|
+
* email: 'user@example.com',
|
|
2212
|
+
* });
|
|
2213
|
+
* ```
|
|
2214
|
+
*/
|
|
2215
|
+
declare function getPasswordResetUrl(options: AuthUrlOptions & {
|
|
2216
|
+
email?: string;
|
|
2217
|
+
}): string;
|
|
2218
|
+
/**
|
|
2219
|
+
* Build an invite acceptance URL.
|
|
2220
|
+
*
|
|
2221
|
+
* Use this when sending team invitation emails.
|
|
2222
|
+
*
|
|
2223
|
+
* @example
|
|
2224
|
+
* ```ts
|
|
2225
|
+
* const url = getInviteAcceptUrl({
|
|
2226
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2227
|
+
* clientId: 'my-app',
|
|
2228
|
+
* inviteToken: 'abc123xyz',
|
|
2229
|
+
* });
|
|
2230
|
+
* ```
|
|
2231
|
+
*/
|
|
2232
|
+
declare function getInviteAcceptUrl(options: AuthUrlOptions & {
|
|
2233
|
+
inviteToken: string;
|
|
2234
|
+
}): string;
|
|
2235
|
+
interface LogoutUrlOptions {
|
|
2236
|
+
/** AuthVital server URL (e.g., https://auth.myapp.com) */
|
|
2237
|
+
authVitalHost: string;
|
|
2238
|
+
/** Optional: Where to redirect after logout completes. If not provided, shows IDP's logged-out page. */
|
|
2239
|
+
postLogoutRedirectUri?: string;
|
|
2240
|
+
}
|
|
2241
|
+
/**
|
|
2242
|
+
* Build a logout URL that clears the IDP session.
|
|
2243
|
+
*
|
|
2244
|
+
* Use this when logging out users - it will clear both your app's
|
|
2245
|
+
* cookies AND the IDP session.
|
|
2246
|
+
*
|
|
2247
|
+
* @example
|
|
2248
|
+
* ```ts
|
|
2249
|
+
* // In your logout endpoint - show IDP's logged out page:
|
|
2250
|
+
* const logoutUrl = getLogoutUrl({
|
|
2251
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2252
|
+
* });
|
|
2253
|
+
* res.redirect(logoutUrl);
|
|
2254
|
+
*
|
|
2255
|
+
* // Or redirect back to your app after logout:
|
|
2256
|
+
* const logoutUrl = getLogoutUrl({
|
|
2257
|
+
* authVitalHost: 'https://auth.myapp.com',
|
|
2258
|
+
* postLogoutRedirectUri: 'https://myapp.com/',
|
|
2259
|
+
* });
|
|
2260
|
+
* ```
|
|
2261
|
+
*/
|
|
2262
|
+
declare function getLogoutUrl(options: LogoutUrlOptions): string;
|
|
2263
|
+
/**
|
|
2264
|
+
* Get URL for user account settings page (standalone version)
|
|
2265
|
+
*
|
|
2266
|
+
* RECOMMENDED: Use authvital.getAccountSettingsUrl() instead for consistency.
|
|
2267
|
+
* This standalone function is for cases where you don't have an AuthVital client.
|
|
2268
|
+
*
|
|
2269
|
+
* @example
|
|
2270
|
+
* ```typescript
|
|
2271
|
+
* const url = getAccountSettingsUrl(authVitalHost);
|
|
2272
|
+
* // Returns: https://auth.example.com/account/settings
|
|
2273
|
+
* ```
|
|
2274
|
+
*/
|
|
2275
|
+
declare function getAccountSettingsUrl(authVitalHost: string): string;
|
|
2276
|
+
|
|
2277
|
+
/**
|
|
2278
|
+
* @authvital/sdk - Prisma Schema Snippets
|
|
2279
|
+
*
|
|
2280
|
+
* Copy these model definitions into your schema.prisma file.
|
|
2281
|
+
* The sync handler expects these exact field names.
|
|
2282
|
+
*/
|
|
2283
|
+
declare const IDENTITY_SCHEMA = "\n// =============================================================================\n// AUTHVITAL IDENTITY (synced from IDP)\n// =============================================================================\n// Copy this into your schema.prisma and customize as needed.\n// The sync handler only touches these base fields.\n\nmodel Identity {\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // CORE IDENTITY (synced from AuthVital - OIDC Standard Claims)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n id String @id // AuthVital subject ID (sub claim)\n username String? @unique // Unique handle (@janesmith)\n displayName String? @map(\"display_name\") // Full name (OIDC: name)\n givenName String? @map(\"given_name\") // First name\n familyName String? @map(\"family_name\") // Last name\n middleName String? @map(\"middle_name\") // Middle name(s)\n nickname String? // Casual name\n pictureUrl String? @map(\"picture_url\") // Profile picture URL\n website String? // Personal URL\n gender String? // Gender identity\n birthdate String? // YYYY-MM-DD\n zoneinfo String? // IANA timezone\n locale String? // Language (e.g., en-US)\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // EMAIL SCOPE (OIDC Standard)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n email String? @unique\n emailVerified Boolean @default(false) @map(\"email_verified\")\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // PHONE SCOPE (OIDC Standard)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n phone String? @unique\n phoneVerified Boolean @default(false) @map(\"phone_verified\")\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // TENANT CONTEXT (for multi-tenant apps)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n tenantId String? @map(\"tenant_id\") // Current tenant ID\n appRole String? @map(\"app_role\") // Role slug (e.g., \"admin\")\n groups String[] @default([]) // Group slugs in tenant\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // STATUS\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n isActive Boolean @default(true) @map(\"is_active\") // IDP-level: can user log in at all?\n hasAppAccess Boolean @default(true) @map(\"has_app_access\") // App-level: does user have access to THIS app?\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // SYNC METADATA\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n syncedAt DateTime @default(now()) @map(\"synced_at\")\n createdAt DateTime @default(now()) @map(\"created_at\")\n updatedAt DateTime @updatedAt @map(\"updated_at\")\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // RELATIONS\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n sessions IdentitySession[]\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // ADD YOUR APP-SPECIFIC RELATIONS BELOW\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // profile UserProfile?\n // posts Post[]\n \n @@index([tenantId])\n @@index([email])\n @@index([username])\n @@map(\"av_identities\")\n}\n";
|
|
2284
|
+
declare const IDENTITY_SESSION_SCHEMA = "\n// =============================================================================\n// AUTHVITAL IDENTITY SESSION (optional - for session management)\n// =============================================================================\n\nmodel IdentitySession {\n id String @id @default(cuid())\n identityId String @map(\"identity_id\")\n identity Identity @relation(fields: [identityId], references: [id], onDelete: Cascade)\n \n authSessionId String? @map(\"auth_session_id\") // AuthVital session ID\n deviceInfo String? @map(\"device_info\") // Parsed device info\n ipAddress String? @map(\"ip_address\")\n userAgent String? @map(\"user_agent\")\n \n createdAt DateTime @default(now()) @map(\"created_at\")\n lastActiveAt DateTime @default(now()) @map(\"last_active_at\")\n expiresAt DateTime @map(\"expires_at\")\n revokedAt DateTime? @map(\"revoked_at\")\n \n @@index([identityId])\n @@index([authSessionId])\n @@map(\"av_identity_sessions\")\n}\n";
|
|
2285
|
+
/**
|
|
2286
|
+
* Combined schema snippet - both models together
|
|
2287
|
+
*/
|
|
2288
|
+
declare const FULL_SCHEMA = "\n// =============================================================================\n// AUTHVITAL IDENTITY (synced from IDP)\n// =============================================================================\n// Copy this into your schema.prisma and customize as needed.\n// The sync handler only touches these base fields.\n\nmodel Identity {\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // CORE IDENTITY (synced from AuthVital - OIDC Standard Claims)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n id String @id // AuthVital subject ID (sub claim)\n username String? @unique // Unique handle (@janesmith)\n displayName String? @map(\"display_name\") // Full name (OIDC: name)\n givenName String? @map(\"given_name\") // First name\n familyName String? @map(\"family_name\") // Last name\n middleName String? @map(\"middle_name\") // Middle name(s)\n nickname String? // Casual name\n pictureUrl String? @map(\"picture_url\") // Profile picture URL\n website String? // Personal URL\n gender String? // Gender identity\n birthdate String? // YYYY-MM-DD\n zoneinfo String? // IANA timezone\n locale String? // Language (e.g., en-US)\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // EMAIL SCOPE (OIDC Standard)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n email String? @unique\n emailVerified Boolean @default(false) @map(\"email_verified\")\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // PHONE SCOPE (OIDC Standard)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n phone String? @unique\n phoneVerified Boolean @default(false) @map(\"phone_verified\")\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // TENANT CONTEXT (for multi-tenant apps)\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n tenantId String? @map(\"tenant_id\") // Current tenant ID\n appRole String? @map(\"app_role\") // Role slug (e.g., \"admin\")\n groups String[] @default([]) // Group slugs in tenant\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // STATUS\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n isActive Boolean @default(true) @map(\"is_active\") // IDP-level: can user log in at all?\n hasAppAccess Boolean @default(true) @map(\"has_app_access\") // App-level: does user have access to THIS app?\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // SYNC METADATA\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n syncedAt DateTime @default(now()) @map(\"synced_at\")\n createdAt DateTime @default(now()) @map(\"created_at\")\n updatedAt DateTime @updatedAt @map(\"updated_at\")\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // RELATIONS\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n sessions IdentitySession[]\n \n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // ADD YOUR APP-SPECIFIC RELATIONS BELOW\n // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // profile UserProfile?\n // posts Post[]\n \n @@index([tenantId])\n @@index([email])\n @@index([username])\n @@map(\"av_identities\")\n}\n\n\n\n// =============================================================================\n// AUTHVITAL IDENTITY SESSION (optional - for session management)\n// =============================================================================\n\nmodel IdentitySession {\n id String @id @default(cuid())\n identityId String @map(\"identity_id\")\n identity Identity @relation(fields: [identityId], references: [id], onDelete: Cascade)\n \n authSessionId String? @map(\"auth_session_id\") // AuthVital session ID\n deviceInfo String? @map(\"device_info\") // Parsed device info\n ipAddress String? @map(\"ip_address\")\n userAgent String? @map(\"user_agent\")\n \n createdAt DateTime @default(now()) @map(\"created_at\")\n lastActiveAt DateTime @default(now()) @map(\"last_active_at\")\n expiresAt DateTime @map(\"expires_at\")\n revokedAt DateTime? @map(\"revoked_at\")\n \n @@index([identityId])\n @@index([authSessionId])\n @@map(\"av_identity_sessions\")\n}\n";
|
|
2289
|
+
/**
|
|
2290
|
+
* Print the schema to console (for easy copy-paste)
|
|
2291
|
+
*/
|
|
2292
|
+
declare function printSchema(): void;
|
|
2293
|
+
|
|
2294
|
+
/**
|
|
2295
|
+
* @authvital/sdk - Identity Sync Types
|
|
2296
|
+
*
|
|
2297
|
+
* TypeScript types matching the Prisma schema snippets.
|
|
2298
|
+
* Use these for type-safe operations with synced identities.
|
|
2299
|
+
*
|
|
2300
|
+
* Follows OIDC Standard Claims:
|
|
2301
|
+
* - Profile Scope: name, given_name, family_name, etc.
|
|
2302
|
+
* - Email Scope: email, email_verified
|
|
2303
|
+
* - Phone Scope: phone_number, phone_number_verified
|
|
2304
|
+
*/
|
|
2305
|
+
/**
|
|
2306
|
+
* Base identity fields synced from AuthVital (OIDC-compliant)
|
|
2307
|
+
*/
|
|
2308
|
+
interface IdentityBase {
|
|
2309
|
+
/** AuthVital subject ID (sub claim in JWT) */
|
|
2310
|
+
id: string;
|
|
2311
|
+
/** Unique handle (e.g., @janesmith) - OIDC: preferred_username */
|
|
2312
|
+
username: string | null;
|
|
2313
|
+
/** Full display name - OIDC: name */
|
|
2314
|
+
displayName: string | null;
|
|
2315
|
+
/** First name - OIDC: given_name */
|
|
2316
|
+
givenName: string | null;
|
|
2317
|
+
/** Last name - OIDC: family_name */
|
|
2318
|
+
familyName: string | null;
|
|
2319
|
+
/** Middle name(s) - OIDC: middle_name */
|
|
2320
|
+
middleName: string | null;
|
|
2321
|
+
/** Casual name - OIDC: nickname */
|
|
2322
|
+
nickname: string | null;
|
|
2323
|
+
/** Profile picture URL - OIDC: picture */
|
|
2324
|
+
pictureUrl: string | null;
|
|
2325
|
+
/** Personal/professional URL - OIDC: website */
|
|
2326
|
+
website: string | null;
|
|
2327
|
+
/** Gender identity - OIDC: gender */
|
|
2328
|
+
gender: string | null;
|
|
2329
|
+
/** Birth date in YYYY-MM-DD format - OIDC: birthdate */
|
|
2330
|
+
birthdate: string | null;
|
|
2331
|
+
/** IANA timezone (e.g., America/New_York) - OIDC: zoneinfo */
|
|
2332
|
+
zoneinfo: string | null;
|
|
2333
|
+
/** Language/region (e.g., en-US) - OIDC: locale */
|
|
2334
|
+
locale: string | null;
|
|
2335
|
+
/** Identity's email address - OIDC: email */
|
|
2336
|
+
email: string | null;
|
|
2337
|
+
/** Whether email is verified - OIDC: email_verified */
|
|
2338
|
+
emailVerified: boolean;
|
|
2339
|
+
/** Phone number in E.164 format - OIDC: phone_number */
|
|
2340
|
+
phone: string | null;
|
|
2341
|
+
/** Whether phone is verified - OIDC: phone_number_verified */
|
|
2342
|
+
phoneVerified: boolean;
|
|
2343
|
+
/** Current tenant ID (for multi-tenant apps) */
|
|
2344
|
+
tenantId: string | null;
|
|
2345
|
+
/** Role slug from app_roles claim */
|
|
2346
|
+
appRole: string | null;
|
|
2347
|
+
/** Group slugs in current tenant */
|
|
2348
|
+
groups: string[];
|
|
2349
|
+
/**
|
|
2350
|
+
* Whether the identity is active at the IDP level.
|
|
2351
|
+
* When false, user cannot log into ANY app - all sessions invalidated.
|
|
2352
|
+
* Set via subject.deactivated event.
|
|
2353
|
+
*/
|
|
2354
|
+
isActive: boolean;
|
|
2355
|
+
/**
|
|
2356
|
+
* Whether the identity has access to THIS specific app.
|
|
2357
|
+
* When false, user can still log into other apps, just not this one.
|
|
2358
|
+
* Set via app_access.granted/revoked events.
|
|
2359
|
+
*/
|
|
2360
|
+
hasAppAccess: boolean;
|
|
2361
|
+
/** Last sync timestamp */
|
|
2362
|
+
syncedAt: Date;
|
|
2363
|
+
/** When the identity was first synced */
|
|
2364
|
+
createdAt: Date;
|
|
2365
|
+
/** Last update timestamp - OIDC: updated_at (as unix timestamp in JWT) */
|
|
2366
|
+
updatedAt: Date;
|
|
2367
|
+
}
|
|
2368
|
+
/**
|
|
2369
|
+
* Identity for creation (id required, others optional)
|
|
2370
|
+
*/
|
|
2371
|
+
interface IdentityCreate {
|
|
2372
|
+
id: string;
|
|
2373
|
+
username?: string | null;
|
|
2374
|
+
displayName?: string | null;
|
|
2375
|
+
givenName?: string | null;
|
|
2376
|
+
familyName?: string | null;
|
|
2377
|
+
middleName?: string | null;
|
|
2378
|
+
nickname?: string | null;
|
|
2379
|
+
pictureUrl?: string | null;
|
|
2380
|
+
website?: string | null;
|
|
2381
|
+
gender?: string | null;
|
|
2382
|
+
birthdate?: string | null;
|
|
2383
|
+
zoneinfo?: string | null;
|
|
2384
|
+
locale?: string | null;
|
|
2385
|
+
email?: string | null;
|
|
2386
|
+
emailVerified?: boolean;
|
|
2387
|
+
phone?: string | null;
|
|
2388
|
+
phoneVerified?: boolean;
|
|
2389
|
+
tenantId?: string | null;
|
|
2390
|
+
appRole?: string | null;
|
|
2391
|
+
groups?: string[];
|
|
2392
|
+
isActive?: boolean;
|
|
2393
|
+
hasAppAccess?: boolean;
|
|
2394
|
+
}
|
|
2395
|
+
/**
|
|
2396
|
+
* Identity for updates (all fields optional)
|
|
2397
|
+
*/
|
|
2398
|
+
interface IdentityUpdate {
|
|
2399
|
+
username?: string | null;
|
|
2400
|
+
displayName?: string | null;
|
|
2401
|
+
givenName?: string | null;
|
|
2402
|
+
familyName?: string | null;
|
|
2403
|
+
middleName?: string | null;
|
|
2404
|
+
nickname?: string | null;
|
|
2405
|
+
pictureUrl?: string | null;
|
|
2406
|
+
website?: string | null;
|
|
2407
|
+
gender?: string | null;
|
|
2408
|
+
birthdate?: string | null;
|
|
2409
|
+
zoneinfo?: string | null;
|
|
2410
|
+
locale?: string | null;
|
|
2411
|
+
email?: string | null;
|
|
2412
|
+
emailVerified?: boolean;
|
|
2413
|
+
phone?: string | null;
|
|
2414
|
+
phoneVerified?: boolean;
|
|
2415
|
+
tenantId?: string | null;
|
|
2416
|
+
appRole?: string | null;
|
|
2417
|
+
groups?: string[];
|
|
2418
|
+
isActive?: boolean;
|
|
2419
|
+
hasAppAccess?: boolean;
|
|
2420
|
+
syncedAt?: Date;
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Identity session fields
|
|
2424
|
+
*/
|
|
2425
|
+
interface IdentitySessionBase {
|
|
2426
|
+
id: string;
|
|
2427
|
+
identityId: string;
|
|
2428
|
+
/** AuthVital's session ID (for linking) */
|
|
2429
|
+
authSessionId: string | null;
|
|
2430
|
+
/** Parsed device info (e.g., "Chrome on macOS") */
|
|
2431
|
+
deviceInfo: string | null;
|
|
2432
|
+
/** Client IP address */
|
|
2433
|
+
ipAddress: string | null;
|
|
2434
|
+
/** Raw user agent string */
|
|
2435
|
+
userAgent: string | null;
|
|
2436
|
+
/** When the session was created */
|
|
2437
|
+
createdAt: Date;
|
|
2438
|
+
/** Last activity timestamp */
|
|
2439
|
+
lastActiveAt: Date;
|
|
2440
|
+
/** When the session expires */
|
|
2441
|
+
expiresAt: Date;
|
|
2442
|
+
/** When the session was revoked (null if active) */
|
|
2443
|
+
revokedAt: Date | null;
|
|
2444
|
+
}
|
|
2445
|
+
/**
|
|
2446
|
+
* Session for creation
|
|
2447
|
+
*/
|
|
2448
|
+
interface IdentitySessionCreate {
|
|
2449
|
+
identityId: string;
|
|
2450
|
+
authSessionId?: string | null;
|
|
2451
|
+
deviceInfo?: string | null;
|
|
2452
|
+
ipAddress?: string | null;
|
|
2453
|
+
userAgent?: string | null;
|
|
2454
|
+
expiresAt: Date;
|
|
2455
|
+
}
|
|
2456
|
+
/**
|
|
2457
|
+
* Session for updates
|
|
2458
|
+
*/
|
|
2459
|
+
interface IdentitySessionUpdate {
|
|
2460
|
+
lastActiveAt?: Date;
|
|
2461
|
+
revokedAt?: Date | null;
|
|
2462
|
+
deviceInfo?: string | null;
|
|
2463
|
+
}
|
|
2464
|
+
/**
|
|
2465
|
+
* Options for session cleanup
|
|
2466
|
+
*/
|
|
2467
|
+
interface SessionCleanupOptions {
|
|
2468
|
+
/** Delete sessions expired more than X days ago (default: 30) */
|
|
2469
|
+
expiredOlderThanDays?: number;
|
|
2470
|
+
/** Also delete revoked sessions (default: false - keeps for audit) */
|
|
2471
|
+
deleteRevoked?: boolean;
|
|
2472
|
+
/** Dry run - log what would be deleted without deleting (default: false) */
|
|
2473
|
+
dryRun?: boolean;
|
|
2474
|
+
}
|
|
2475
|
+
/**
|
|
2476
|
+
* Result of cleanup operation
|
|
2477
|
+
*/
|
|
2478
|
+
interface SessionCleanupResult {
|
|
2479
|
+
/** Number of sessions deleted (or would be deleted in dry run) */
|
|
2480
|
+
deletedCount: number;
|
|
2481
|
+
/** Whether this was a dry run */
|
|
2482
|
+
dryRun: boolean;
|
|
2483
|
+
}
|
|
2484
|
+
/**
|
|
2485
|
+
* Minimal Prisma client interface for the sync handler
|
|
2486
|
+
*
|
|
2487
|
+
* This allows the sync handler to work with any Prisma client
|
|
2488
|
+
* that has the expected identity and identitySession models.
|
|
2489
|
+
*/
|
|
2490
|
+
interface PrismaClientLike {
|
|
2491
|
+
identity: {
|
|
2492
|
+
upsert: (args: {
|
|
2493
|
+
where: {
|
|
2494
|
+
id: string;
|
|
2495
|
+
};
|
|
2496
|
+
create: IdentityCreate;
|
|
2497
|
+
update: IdentityUpdate;
|
|
2498
|
+
}) => Promise<unknown>;
|
|
2499
|
+
update: (args: {
|
|
2500
|
+
where: {
|
|
2501
|
+
id: string;
|
|
2502
|
+
};
|
|
2503
|
+
data: IdentityUpdate;
|
|
2504
|
+
}) => Promise<unknown>;
|
|
2505
|
+
delete: (args: {
|
|
2506
|
+
where: {
|
|
2507
|
+
id: string;
|
|
2508
|
+
};
|
|
2509
|
+
}) => Promise<unknown>;
|
|
2510
|
+
};
|
|
2511
|
+
identitySession: {
|
|
2512
|
+
deleteMany: (args: {
|
|
2513
|
+
where: {
|
|
2514
|
+
OR?: Array<Record<string, unknown>>;
|
|
2515
|
+
expiresAt?: {
|
|
2516
|
+
lt: Date;
|
|
2517
|
+
};
|
|
2518
|
+
revokedAt?: {
|
|
2519
|
+
not: null;
|
|
2520
|
+
} | {
|
|
2521
|
+
lt: Date;
|
|
2522
|
+
};
|
|
2523
|
+
};
|
|
2524
|
+
}) => Promise<{
|
|
2525
|
+
count: number;
|
|
2526
|
+
}>;
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
/**
|
|
2530
|
+
* Function that resolves a Prisma client for a given tenant
|
|
2531
|
+
* Used for tenant-isolated database architectures
|
|
2532
|
+
*/
|
|
2533
|
+
type PrismaClientResolver = (tenantId: string) => PrismaClientLike | Promise<PrismaClientLike>;
|
|
2534
|
+
/**
|
|
2535
|
+
* Either a direct Prisma client (shared DB) or a resolver function (tenant-isolated DBs)
|
|
2536
|
+
*/
|
|
2537
|
+
type PrismaClientOrResolver = PrismaClientLike | PrismaClientResolver;
|
|
2538
|
+
|
|
2539
|
+
/**
|
|
2540
|
+
* @authvital/sdk - Identity Sync Handler
|
|
2541
|
+
*
|
|
2542
|
+
* Pre-built webhook handler that syncs AuthVital identities to your local database.
|
|
2543
|
+
* Supports all OIDC standard claims.
|
|
2544
|
+
*
|
|
2545
|
+
* @example Single database (shared or single-tenant)
|
|
2546
|
+
* ```typescript
|
|
2547
|
+
* import { IdentitySyncHandler, WebhookRouter } from '@authvital/sdk/server';
|
|
2548
|
+
* import { prisma } from './prisma';
|
|
2549
|
+
*
|
|
2550
|
+
* const handler = new IdentitySyncHandler(prisma);
|
|
2551
|
+
* const router = new WebhookRouter({ handler });
|
|
2552
|
+
* ```
|
|
2553
|
+
*
|
|
2554
|
+
* @example Tenant-isolated databases
|
|
2555
|
+
* ```typescript
|
|
2556
|
+
* import { IdentitySyncHandler, WebhookRouter } from '@authvital/sdk/server';
|
|
2557
|
+
* import { getTenantPrisma } from './db';
|
|
2558
|
+
*
|
|
2559
|
+
* // Pass a resolver function that returns the correct client per tenant
|
|
2560
|
+
* const handler = new IdentitySyncHandler((tenantId) => getTenantPrisma(tenantId));
|
|
2561
|
+
* const router = new WebhookRouter({ handler });
|
|
2562
|
+
* ```
|
|
2563
|
+
*/
|
|
2564
|
+
|
|
2565
|
+
/**
|
|
2566
|
+
* Pre-built webhook handler for syncing identities from AuthVital
|
|
2567
|
+
*
|
|
2568
|
+
* Supports two modes:
|
|
2569
|
+
* 1. **Shared DB**: Pass a single Prisma client
|
|
2570
|
+
* 2. **Tenant-isolated DBs**: Pass a resolver function that returns the client per tenant
|
|
2571
|
+
*
|
|
2572
|
+
* Handles:
|
|
2573
|
+
* - subject.created → Creates identity in local DB
|
|
2574
|
+
* - subject.updated → Updates identity in local DB
|
|
2575
|
+
* - subject.deleted → Deletes identity from local DB
|
|
2576
|
+
* - subject.deactivated → Marks identity as inactive
|
|
2577
|
+
* - member.joined → Updates identity's tenant context
|
|
2578
|
+
* - member.left → Clears identity's tenant context
|
|
2579
|
+
* - member.role_changed → Updates identity's tenant role
|
|
2580
|
+
* - app_access.granted → Updates identity's app role
|
|
2581
|
+
* - app_access.revoked → Clears identity's app role
|
|
2582
|
+
* - app_access.role_changed → Updates identity's app role
|
|
2583
|
+
*/
|
|
2584
|
+
declare class IdentitySyncHandler extends AuthVitalEventHandler {
|
|
2585
|
+
private readonly prismaOrResolver;
|
|
2586
|
+
private readonly isResolver;
|
|
2587
|
+
/**
|
|
2588
|
+
* Create a new identity sync handler
|
|
2589
|
+
*
|
|
2590
|
+
* @param prismaOrResolver - Either a Prisma client (shared DB) or a resolver function (tenant-isolated DBs)
|
|
2591
|
+
*
|
|
2592
|
+
* @example Shared database
|
|
2593
|
+
* ```typescript
|
|
2594
|
+
* new IdentitySyncHandler(prisma)
|
|
2595
|
+
* ```
|
|
2596
|
+
*
|
|
2597
|
+
* @example Tenant-isolated databases
|
|
2598
|
+
* ```typescript
|
|
2599
|
+
* new IdentitySyncHandler((tenantId) => getTenantPrisma(tenantId))
|
|
2600
|
+
* ```
|
|
2601
|
+
*/
|
|
2602
|
+
constructor(prismaOrResolver: PrismaClientOrResolver);
|
|
2603
|
+
/**
|
|
2604
|
+
* Get the Prisma client for the given tenant
|
|
2605
|
+
* - Shared DB mode: Returns the single client (tenantId ignored)
|
|
2606
|
+
* - Tenant-isolated mode: Calls resolver with tenantId
|
|
2607
|
+
*/
|
|
2608
|
+
private getClient;
|
|
2609
|
+
onSubjectCreated(event: SubjectCreatedEvent): Promise<void>;
|
|
2610
|
+
onSubjectUpdated(event: SubjectUpdatedEvent): Promise<void>;
|
|
2611
|
+
onSubjectDeleted(event: SubjectDeletedEvent): Promise<void>;
|
|
2612
|
+
onSubjectDeactivated(event: SubjectDeactivatedEvent): Promise<void>;
|
|
2613
|
+
onMemberJoined(event: MemberJoinedEvent): Promise<void>;
|
|
2614
|
+
onMemberLeft(event: MemberLeftEvent): Promise<void>;
|
|
2615
|
+
onMemberRoleChanged(event: MemberRoleChangedEvent): Promise<void>;
|
|
2616
|
+
onAppAccessGranted(event: AppAccessGrantedEvent): Promise<void>;
|
|
2617
|
+
onAppAccessRevoked(event: AppAccessRevokedEvent): Promise<void>;
|
|
2618
|
+
onAppAccessRoleChanged(event: AppAccessRoleChangedEvent): Promise<void>;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
/**
|
|
2622
|
+
* @authvital/sdk - Session Cleanup Utility
|
|
2623
|
+
*
|
|
2624
|
+
* Optional utility for cleaning up expired/revoked sessions.
|
|
2625
|
+
* Call this on a schedule (cron job) or manually.
|
|
2626
|
+
*
|
|
2627
|
+
* @example
|
|
2628
|
+
* ```typescript
|
|
2629
|
+
* import { cleanupSessions } from '@authvital/sdk/server';
|
|
2630
|
+
* import { prisma } from './prisma';
|
|
2631
|
+
*
|
|
2632
|
+
* // Run daily via cron
|
|
2633
|
+
* await cleanupSessions(prisma, {
|
|
2634
|
+
* expiredOlderThanDays: 30,
|
|
2635
|
+
* deleteRevoked: false, // Keep revoked for audit trail
|
|
2636
|
+
* });
|
|
2637
|
+
* ```
|
|
2638
|
+
*/
|
|
2639
|
+
|
|
2640
|
+
/**
|
|
2641
|
+
* Clean up expired and optionally revoked sessions
|
|
2642
|
+
*
|
|
2643
|
+
* @param prisma - Prisma client instance
|
|
2644
|
+
* @param options - Cleanup configuration
|
|
2645
|
+
* @returns Result with count of deleted sessions
|
|
2646
|
+
*/
|
|
2647
|
+
declare function cleanupSessions(prisma: PrismaClientLike, options?: SessionCleanupOptions): Promise<SessionCleanupResult>;
|
|
2648
|
+
/**
|
|
2649
|
+
* Get raw SQL for session cleanup (for use with pg_cron or similar)
|
|
2650
|
+
*
|
|
2651
|
+
* @param options - Cleanup configuration
|
|
2652
|
+
* @returns SQL statement string
|
|
2653
|
+
*/
|
|
2654
|
+
declare function getCleanupSQL(options?: SessionCleanupOptions): string;
|
|
2655
|
+
|
|
2656
|
+
export { AppAccessGrantedEvent, AppAccessRevokedEvent, AppAccessRoleChangedEvent, type ApplicationMembership, type ApplicationMembershipsResponse, type ApplicationRoleDefinition, type ApplicationRolesResponse, type AuthUrlOptions, AuthVital, type AuthVitalClientConfig, type AuthVitalConfig, AuthVitalEventHandler, type AuthorizeUrlParams, type AvailableLicenseType, BaseClient, type CheckPermissionParams, type CheckPermissionResult, type CheckPermissionsParams, type CheckPermissionsResult, type EnhancedJwtPayload, type EntitlementsNamespace, FULL_SCHEMA, type FeatureCheckResult, type GetCurrentUserResult, IDENTITY_SCHEMA, IDENTITY_SESSION_SCHEMA, type IdentityBase, type IdentityCreate, type IdentitySessionBase, type IdentitySessionCreate, type IdentitySessionUpdate, IdentitySyncHandler, type IdentityUpdate, type InvitationResponse, type InvitationsNamespace, type Jwks, type JwksKey, type JwtHeader, type JwtLicenseInfo, type JwtPayload, JwtValidator, type JwtValidatorConfig, type LicenseAuditLogEntry, type LicenseAuditLogResponse, type LicenseCheckResponse, type LicenseFeatureResponse, type LicenseGrantResponse, type LicenseHolder, type LicenseRevokeResponse, type LicensedUser, type LicensesNamespace, type LoginUrlOptions, type LogoutUrlOptions, MemberJoinedEvent, MemberLeftEvent, MemberRoleChangedEvent, type MemberWithLicenses, type MembershipRole, type MembershipStatusType, type MembershipUser, type MembershipsNamespace, OAuthFlow, type OAuthFlowConfig, type PendingInvitation, type PendingInvitationsResponse, type PermissionsNamespace, type PrismaClientLike, type PrismaClientOrResolver, type PrismaClientResolver, type RefreshTokenParams, type RequestLike, type ResendInvitationParams, type RevokeInvitationResponse, type SendInvitationParams, type SessionCleanupOptions, type SessionCleanupResult, type SessionsNamespace, type SetMemberRoleResponse, type SignupUrlOptions, type StatePayload, SubjectCreatedEvent, SubjectDeactivatedEvent, SubjectDeletedEvent, SubjectUpdatedEvent, type SubscriptionStatusType, type SubscriptionSummary, type TenantLicenseOverview, type TenantMembership, type TenantMembershipsResponse, type TenantRoleDefinition, type TenantRolesResponse, type TokenExchangeParams, type TokenResponse, type UsageOverviewResponse, type UsageTrendEntry, type UserLicenseListItem, type UserPermissions, type UserTenantMembership, type UserTenantsResponse, type ValidateMembershipResponse, type ValidateTokenResult, type ValidatedClaims, appendClientIdToUri, buildAuthorizeUrl, cleanupSessions, createAuthVital, createEntitlementsNamespace, createInvitationsNamespace, createJwtMiddleware, createJwtValidator, createLicensesNamespace, createMembershipsNamespace, createPassportJwtOptions, createPermissionsNamespace, createSessionsNamespace, decodeJwt, decodeState, decodeStateWithVerifier, encodeState, encodeStateWithVerifier, exchangeCodeForTokens, extractAuthorizationHeader, generateCodeChallenge, generateCodeVerifier, generatePKCE, generateState, getAccountSettingsUrl, getCleanupSQL, getCurrentUser, getCurrentUserFromConfig, getInviteAcceptUrl, getLoginUrl, getLogoutUrl, getPasswordResetUrl, getSignupUrl, printSchema, refreshAccessToken };
|