hazo_auth 5.1.11 → 5.1.13
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 +206 -2
- package/SETUP_CHECKLIST.md +202 -1
- package/cli-src/lib/auth/auth_cache.ts +24 -1
- package/cli-src/lib/auth/auth_types.ts +146 -0
- package/cli-src/lib/auth/hazo_get_auth.server.ts +60 -8
- package/cli-src/lib/auth/hazo_get_tenant_auth.server.ts +267 -0
- package/cli-src/lib/auth/index.ts +20 -0
- package/cli-src/lib/cookies_config.server.ts +1 -0
- package/dist/lib/auth/auth_cache.d.ts +11 -2
- package/dist/lib/auth/auth_cache.d.ts.map +1 -1
- package/dist/lib/auth/auth_cache.js +20 -1
- package/dist/lib/auth/auth_types.d.ts +113 -0
- package/dist/lib/auth/auth_types.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.js +43 -0
- package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_auth.server.js +47 -8
- package/dist/lib/auth/hazo_get_tenant_auth.server.d.ts +64 -0
- package/dist/lib/auth/hazo_get_tenant_auth.server.d.ts.map +1 -0
- package/dist/lib/auth/hazo_get_tenant_auth.server.js +203 -0
- package/dist/lib/auth/index.d.ts +3 -0
- package/dist/lib/auth/index.d.ts.map +1 -1
- package/dist/lib/auth/index.js +3 -0
- package/dist/lib/cookies_config.server.d.ts +1 -0
- package/dist/lib/cookies_config.server.d.ts.map +1 -1
- package/dist/lib/cookies_config.server.js +1 -0
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
HazoAuthUser,
|
|
11
11
|
HazoAuthOptions,
|
|
12
12
|
ScopeAccessInfo,
|
|
13
|
+
ScopeDetails,
|
|
13
14
|
} from "./auth_types";
|
|
14
15
|
import { PermissionError, ScopeAccessError } from "./auth_types.js";
|
|
15
16
|
import { get_auth_cache } from "./auth_cache.js";
|
|
@@ -72,11 +73,6 @@ function get_client_ip(request: NextRequest): string {
|
|
|
72
73
|
return "unknown";
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
/**
|
|
76
|
-
* Fetches user data and permissions from database
|
|
77
|
-
* @param user_id - User ID
|
|
78
|
-
* @returns Object with user, permissions, and role_ids
|
|
79
|
-
*/
|
|
80
76
|
/**
|
|
81
77
|
* CRUD service options for hazo_user_scopes table
|
|
82
78
|
* This table uses a composite primary key (user_id, scope_id) and no 'id' column
|
|
@@ -86,10 +82,63 @@ const USER_SCOPES_CRUD_OPTIONS = {
|
|
|
86
82
|
autoId: false as const,
|
|
87
83
|
};
|
|
88
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Fetches full scope details for user's scope assignments
|
|
87
|
+
* Joins hazo_user_scopes with hazo_scopes to get name, slug, branding
|
|
88
|
+
* @param user_id - User ID
|
|
89
|
+
* @returns Array of scope details with branding information
|
|
90
|
+
*/
|
|
91
|
+
async function fetch_user_scope_details(user_id: string): Promise<ScopeDetails[]> {
|
|
92
|
+
const hazoConnect = get_hazo_connect_instance();
|
|
93
|
+
const user_scopes_service = createCrudService(
|
|
94
|
+
hazoConnect,
|
|
95
|
+
"hazo_user_scopes",
|
|
96
|
+
USER_SCOPES_CRUD_OPTIONS,
|
|
97
|
+
);
|
|
98
|
+
const scopes_service = createCrudService(hazoConnect, "hazo_scopes");
|
|
99
|
+
|
|
100
|
+
const user_scope_assignments = await user_scopes_service.findBy({ user_id });
|
|
101
|
+
if (!Array.isArray(user_scope_assignments) || user_scope_assignments.length === 0) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const scope_details: ScopeDetails[] = [];
|
|
106
|
+
for (const assignment of user_scope_assignments) {
|
|
107
|
+
const scope_id = assignment.scope_id as string;
|
|
108
|
+
const role_id = assignment.role_id as string;
|
|
109
|
+
|
|
110
|
+
const scopes = await scopes_service.findBy({ id: scope_id });
|
|
111
|
+
if (Array.isArray(scopes) && scopes.length > 0) {
|
|
112
|
+
const scope = scopes[0];
|
|
113
|
+
scope_details.push({
|
|
114
|
+
id: scope_id,
|
|
115
|
+
name: scope.name as string,
|
|
116
|
+
slug: (scope.slug as string) || null,
|
|
117
|
+
level: scope.level as string,
|
|
118
|
+
parent_id: scope.parent_id as string | null,
|
|
119
|
+
role_id,
|
|
120
|
+
logo_url: (scope.logo_url as string) || null,
|
|
121
|
+
primary_color: (scope.primary_color as string) || null,
|
|
122
|
+
secondary_color: (scope.secondary_color as string) || null,
|
|
123
|
+
tagline: (scope.tagline as string) || null,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return scope_details;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Fetches user data and permissions from database
|
|
133
|
+
* @param user_id - User ID
|
|
134
|
+
* @returns Object with user, permissions, role_ids, and scopes
|
|
135
|
+
*/
|
|
136
|
+
|
|
89
137
|
async function fetch_user_data_from_db(user_id: string): Promise<{
|
|
90
138
|
user: HazoAuthUser;
|
|
91
139
|
permissions: string[];
|
|
92
140
|
role_ids: string[];
|
|
141
|
+
scopes: ScopeDetails[];
|
|
93
142
|
}> {
|
|
94
143
|
const hazoConnect = get_hazo_connect_instance();
|
|
95
144
|
const users_service = createCrudService(hazoConnect, "hazo_users");
|
|
@@ -186,7 +235,10 @@ async function fetch_user_data_from_db(user_id: string): Promise<{
|
|
|
186
235
|
|
|
187
236
|
const permissions = Array.from(permissions_set);
|
|
188
237
|
|
|
189
|
-
|
|
238
|
+
// v5.2: Fetch full scope details for caching
|
|
239
|
+
const scopes = await fetch_user_scope_details(user_id);
|
|
240
|
+
|
|
241
|
+
return { user, permissions, role_ids, scopes };
|
|
190
242
|
}
|
|
191
243
|
|
|
192
244
|
/**
|
|
@@ -430,8 +482,8 @@ export async function hazo_get_auth(
|
|
|
430
482
|
permissions = user_data.permissions;
|
|
431
483
|
role_ids = user_data.role_ids;
|
|
432
484
|
|
|
433
|
-
// Update cache
|
|
434
|
-
cache.set(user_id, user, permissions, role_ids);
|
|
485
|
+
// Update cache (v5.2: includes scope details for tenant auth)
|
|
486
|
+
cache.set(user_id, user, permissions, role_ids, user_data.scopes);
|
|
435
487
|
} catch (error) {
|
|
436
488
|
const error_message =
|
|
437
489
|
error instanceof Error ? error.message : "Unknown error";
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// file_description: tenant-aware authentication function that extracts scope from request headers/cookies
|
|
2
|
+
// section: imports
|
|
3
|
+
import { NextRequest } from "next/server";
|
|
4
|
+
import { hazo_get_auth } from "./hazo_get_auth.server.js";
|
|
5
|
+
import { get_auth_cache } from "./auth_cache.js";
|
|
6
|
+
import { get_scope_by_id } from "../services/scope_service.js";
|
|
7
|
+
import { get_hazo_connect_instance } from "../hazo_connect_instance.server.js";
|
|
8
|
+
import { get_cookie_name } from "../cookies_config.server.js";
|
|
9
|
+
import { get_auth_utility_config } from "../auth_utility_config.server.js";
|
|
10
|
+
import type {
|
|
11
|
+
TenantAuthOptions,
|
|
12
|
+
TenantAuthResult,
|
|
13
|
+
RequiredTenantAuthResult,
|
|
14
|
+
TenantOrganization,
|
|
15
|
+
ScopeDetails,
|
|
16
|
+
} from "./auth_types";
|
|
17
|
+
import {
|
|
18
|
+
AuthenticationRequiredError,
|
|
19
|
+
TenantRequiredError,
|
|
20
|
+
TenantAccessDeniedError,
|
|
21
|
+
} from "./auth_types.js";
|
|
22
|
+
|
|
23
|
+
// section: constants
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default header name for scope ID
|
|
27
|
+
*/
|
|
28
|
+
const DEFAULT_SCOPE_HEADER = "X-Hazo-Scope-Id";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Base cookie name for scope ID (will have prefix applied)
|
|
32
|
+
*/
|
|
33
|
+
const BASE_SCOPE_COOKIE = "hazo_auth_scope_id";
|
|
34
|
+
|
|
35
|
+
// section: helpers
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Extracts scope ID from request headers or cookies
|
|
39
|
+
* Priority: Header > Cookie
|
|
40
|
+
* @param request - NextRequest object
|
|
41
|
+
* @param options - TenantAuthOptions for customization
|
|
42
|
+
* @returns Scope ID if found, undefined otherwise
|
|
43
|
+
*/
|
|
44
|
+
export function extract_scope_id_from_request(
|
|
45
|
+
request: NextRequest,
|
|
46
|
+
options: TenantAuthOptions,
|
|
47
|
+
): string | undefined {
|
|
48
|
+
// Check header first
|
|
49
|
+
const header_name = options.scope_header_name || DEFAULT_SCOPE_HEADER;
|
|
50
|
+
const header_value = request.headers.get(header_name);
|
|
51
|
+
if (header_value) {
|
|
52
|
+
return header_value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check cookie (with prefix)
|
|
56
|
+
const cookie_name = options.scope_cookie_name || get_cookie_name(BASE_SCOPE_COOKIE);
|
|
57
|
+
const cookie_value = request.cookies.get(cookie_name)?.value;
|
|
58
|
+
return cookie_value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Builds TenantOrganization from scope details and access info
|
|
63
|
+
* @param scope_details - Full scope details from cache
|
|
64
|
+
* @param is_super_admin - Whether user is accessing as super admin
|
|
65
|
+
* @returns TenantOrganization object
|
|
66
|
+
*/
|
|
67
|
+
function build_tenant_organization(
|
|
68
|
+
scope_details: ScopeDetails,
|
|
69
|
+
is_super_admin: boolean,
|
|
70
|
+
): TenantOrganization {
|
|
71
|
+
return {
|
|
72
|
+
id: scope_details.id,
|
|
73
|
+
name: scope_details.name,
|
|
74
|
+
slug: scope_details.slug,
|
|
75
|
+
level: scope_details.level,
|
|
76
|
+
role_id: scope_details.role_id,
|
|
77
|
+
is_super_admin,
|
|
78
|
+
branding:
|
|
79
|
+
scope_details.logo_url || scope_details.primary_color
|
|
80
|
+
? {
|
|
81
|
+
logo_url: scope_details.logo_url,
|
|
82
|
+
primary_color: scope_details.primary_color,
|
|
83
|
+
secondary_color: scope_details.secondary_color,
|
|
84
|
+
tagline: scope_details.tagline,
|
|
85
|
+
}
|
|
86
|
+
: undefined,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// section: main_functions
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Tenant-aware authentication function
|
|
94
|
+
*
|
|
95
|
+
* Extracts tenant/scope context from request headers or cookies,
|
|
96
|
+
* validates access, and returns enriched result with organization info.
|
|
97
|
+
*
|
|
98
|
+
* Header priority: X-Hazo-Scope-Id > Cookie
|
|
99
|
+
*
|
|
100
|
+
* @param request - NextRequest object
|
|
101
|
+
* @param options - TenantAuthOptions for customization
|
|
102
|
+
* @returns TenantAuthResult with user, permissions, organization, and user_scopes
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const auth = await hazo_get_tenant_auth(request);
|
|
107
|
+
* if (auth.authenticated && auth.organization) {
|
|
108
|
+
* // Access tenant-specific data
|
|
109
|
+
* const data = await getData(auth.organization.id);
|
|
110
|
+
* }
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export async function hazo_get_tenant_auth(
|
|
114
|
+
request: NextRequest,
|
|
115
|
+
options: TenantAuthOptions = {},
|
|
116
|
+
): Promise<TenantAuthResult> {
|
|
117
|
+
// Extract scope_id from request
|
|
118
|
+
const scope_id = extract_scope_id_from_request(request, options);
|
|
119
|
+
|
|
120
|
+
// Build hazo_get_auth options, passing through scope_id if present
|
|
121
|
+
const auth_options = {
|
|
122
|
+
...options,
|
|
123
|
+
scope_id: scope_id || options.scope_id, // Allow explicit override via options
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Call base hazo_get_auth
|
|
127
|
+
const auth_result = await hazo_get_auth(request, auth_options);
|
|
128
|
+
|
|
129
|
+
// Handle unauthenticated case
|
|
130
|
+
if (!auth_result.authenticated) {
|
|
131
|
+
return {
|
|
132
|
+
authenticated: false,
|
|
133
|
+
user: null,
|
|
134
|
+
permissions: [],
|
|
135
|
+
permission_ok: false,
|
|
136
|
+
organization: null,
|
|
137
|
+
organization_id: null,
|
|
138
|
+
user_scopes: [],
|
|
139
|
+
scope_ok: false,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Get user's scopes from cache (already populated by hazo_get_auth)
|
|
144
|
+
const config = get_auth_utility_config();
|
|
145
|
+
const cache = get_auth_cache(
|
|
146
|
+
config.cache_max_users,
|
|
147
|
+
config.cache_ttl_minutes,
|
|
148
|
+
config.cache_max_age_minutes,
|
|
149
|
+
);
|
|
150
|
+
const cached = cache.get(auth_result.user.id);
|
|
151
|
+
|
|
152
|
+
// User scopes from cache or empty array
|
|
153
|
+
const user_scopes: ScopeDetails[] = cached?.scopes || [];
|
|
154
|
+
|
|
155
|
+
// Build organization info if scope access was successful
|
|
156
|
+
let organization: TenantOrganization | null = null;
|
|
157
|
+
|
|
158
|
+
if (scope_id && auth_result.scope_ok && auth_result.scope_access_via) {
|
|
159
|
+
// Find the scope in user's scopes that matches the access_via scope
|
|
160
|
+
const access_scope = user_scopes.find(
|
|
161
|
+
(s) => s.id === auth_result.scope_access_via?.scope_id,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
if (access_scope) {
|
|
165
|
+
organization = build_tenant_organization(
|
|
166
|
+
access_scope,
|
|
167
|
+
auth_result.scope_access_via.is_super_admin || false,
|
|
168
|
+
);
|
|
169
|
+
} else if (auth_result.scope_access_via.is_super_admin) {
|
|
170
|
+
// Super admin accessing scope they're not assigned to - fetch scope details
|
|
171
|
+
const hazoConnect = get_hazo_connect_instance();
|
|
172
|
+
const scope_result = await get_scope_by_id(hazoConnect, scope_id);
|
|
173
|
+
if (scope_result.success && scope_result.scope) {
|
|
174
|
+
organization = {
|
|
175
|
+
id: scope_result.scope.id,
|
|
176
|
+
name: scope_result.scope.name,
|
|
177
|
+
slug: null, // Could fetch from scope if slug column exists
|
|
178
|
+
level: scope_result.scope.level,
|
|
179
|
+
role_id: "", // Super admin doesn't have a role in the scope
|
|
180
|
+
is_super_admin: true,
|
|
181
|
+
branding: scope_result.scope.logo_url
|
|
182
|
+
? {
|
|
183
|
+
logo_url: scope_result.scope.logo_url,
|
|
184
|
+
primary_color: scope_result.scope.primary_color,
|
|
185
|
+
secondary_color: scope_result.scope.secondary_color,
|
|
186
|
+
tagline: scope_result.scope.tagline,
|
|
187
|
+
}
|
|
188
|
+
: undefined,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
authenticated: true,
|
|
196
|
+
user: auth_result.user,
|
|
197
|
+
permissions: auth_result.permissions,
|
|
198
|
+
permission_ok: auth_result.permission_ok,
|
|
199
|
+
missing_permissions: auth_result.missing_permissions,
|
|
200
|
+
organization,
|
|
201
|
+
organization_id: organization?.id || null,
|
|
202
|
+
user_scopes,
|
|
203
|
+
scope_ok: auth_result.scope_ok,
|
|
204
|
+
scope_access_via: auth_result.scope_access_via,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Strict tenant authentication helper
|
|
210
|
+
*
|
|
211
|
+
* Wraps hazo_get_tenant_auth and throws appropriate errors:
|
|
212
|
+
* - AuthenticationRequiredError (401) if not authenticated
|
|
213
|
+
* - TenantRequiredError (403) if no tenant context in request
|
|
214
|
+
* - TenantAccessDeniedError (403) if user lacks access to requested tenant
|
|
215
|
+
*
|
|
216
|
+
* @param request - NextRequest object
|
|
217
|
+
* @param options - TenantAuthOptions for customization
|
|
218
|
+
* @returns RequiredTenantAuthResult with guaranteed non-null organization
|
|
219
|
+
* @throws AuthenticationRequiredError, TenantRequiredError, TenantAccessDeniedError
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```typescript
|
|
223
|
+
* try {
|
|
224
|
+
* const auth = await require_tenant_auth(request);
|
|
225
|
+
* // auth.organization is guaranteed non-null here
|
|
226
|
+
* const data = await getData(auth.organization.id);
|
|
227
|
+
* } catch (error) {
|
|
228
|
+
* if (error instanceof HazoAuthError) {
|
|
229
|
+
* return NextResponse.json(
|
|
230
|
+
* { error: error.message, code: error.code },
|
|
231
|
+
* { status: error.status_code }
|
|
232
|
+
* );
|
|
233
|
+
* }
|
|
234
|
+
* throw error;
|
|
235
|
+
* }
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
export async function require_tenant_auth(
|
|
239
|
+
request: NextRequest,
|
|
240
|
+
options: TenantAuthOptions = {},
|
|
241
|
+
): Promise<RequiredTenantAuthResult> {
|
|
242
|
+
const result = await hazo_get_tenant_auth(request, options);
|
|
243
|
+
|
|
244
|
+
// Check authentication
|
|
245
|
+
if (!result.authenticated) {
|
|
246
|
+
throw new AuthenticationRequiredError();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Extract scope_id from request for error messages
|
|
250
|
+
const scope_id = extract_scope_id_from_request(request, options);
|
|
251
|
+
|
|
252
|
+
// Check if scope was requested but access denied
|
|
253
|
+
if (scope_id && !result.scope_ok) {
|
|
254
|
+
throw new TenantAccessDeniedError(scope_id, result.user_scopes);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Check if organization context is required but missing
|
|
258
|
+
if (!result.organization) {
|
|
259
|
+
throw new TenantRequiredError(
|
|
260
|
+
"No organization context provided. Include X-Hazo-Scope-Id header or scope cookie.",
|
|
261
|
+
result.user_scopes,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Type assertion: at this point we know organization is non-null
|
|
266
|
+
return result as RequiredTenantAuthResult;
|
|
267
|
+
}
|
|
@@ -11,6 +11,26 @@ export {
|
|
|
11
11
|
} from "./auth_utils.server.js";
|
|
12
12
|
export type { AuthResult, AuthUser } from "./auth_utils.server";
|
|
13
13
|
|
|
14
|
+
// section: tenant_auth_exports
|
|
15
|
+
export {
|
|
16
|
+
hazo_get_tenant_auth,
|
|
17
|
+
require_tenant_auth,
|
|
18
|
+
extract_scope_id_from_request,
|
|
19
|
+
} from "./hazo_get_tenant_auth.server.js";
|
|
20
|
+
export type {
|
|
21
|
+
ScopeDetails,
|
|
22
|
+
TenantOrganization,
|
|
23
|
+
TenantAuthOptions,
|
|
24
|
+
TenantAuthResult,
|
|
25
|
+
RequiredTenantAuthResult,
|
|
26
|
+
} from "./auth_types";
|
|
27
|
+
export {
|
|
28
|
+
HazoAuthError,
|
|
29
|
+
AuthenticationRequiredError,
|
|
30
|
+
TenantRequiredError,
|
|
31
|
+
TenantAccessDeniedError,
|
|
32
|
+
} from "./auth_types.js";
|
|
33
|
+
|
|
14
34
|
// section: client_exports
|
|
15
35
|
export { get_server_auth_user } from "./server_auth.js";
|
|
16
36
|
export type { ServerAuthResult } from "./server_auth";
|
|
@@ -25,6 +25,7 @@ export const BASE_COOKIE_NAMES = {
|
|
|
25
25
|
USER_EMAIL: "hazo_auth_user_email",
|
|
26
26
|
SESSION: "hazo_auth_session",
|
|
27
27
|
DEV_LOCK: "hazo_auth_dev_lock",
|
|
28
|
+
SCOPE_ID: "hazo_auth_scope_id", // v5.2: Tenant context cookie for multi-tenancy
|
|
28
29
|
} as const;
|
|
29
30
|
|
|
30
31
|
// section: main_function
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import type { HazoAuthUser } from "./auth_types";
|
|
1
|
+
import type { HazoAuthUser, ScopeDetails } from "./auth_types";
|
|
2
2
|
/**
|
|
3
3
|
* Cache entry structure
|
|
4
4
|
* v5.x: role_ids are now string UUIDs (from hazo_user_scopes)
|
|
5
|
+
* v5.2: Added scopes with full details for multi-tenancy support
|
|
5
6
|
*/
|
|
6
7
|
type CacheEntry = {
|
|
7
8
|
user: HazoAuthUser;
|
|
8
9
|
permissions: string[];
|
|
9
10
|
role_ids: string[];
|
|
11
|
+
scopes: ScopeDetails[];
|
|
10
12
|
timestamp: number;
|
|
11
13
|
cache_version: number;
|
|
12
14
|
};
|
|
@@ -35,8 +37,9 @@ declare class AuthCache {
|
|
|
35
37
|
* @param user - User data
|
|
36
38
|
* @param permissions - User permissions
|
|
37
39
|
* @param role_ids - User role IDs (v5.x: string UUIDs)
|
|
40
|
+
* @param scopes - User scope details with full information (v5.2+)
|
|
38
41
|
*/
|
|
39
|
-
set(user_id: string, user: HazoAuthUser, permissions: string[], role_ids: string[]): void;
|
|
42
|
+
set(user_id: string, user: HazoAuthUser, permissions: string[], role_ids: string[], scopes?: ScopeDetails[]): void;
|
|
40
43
|
/**
|
|
41
44
|
* Invalidates cache for a specific user
|
|
42
45
|
* @param user_id - User ID to invalidate
|
|
@@ -52,6 +55,12 @@ declare class AuthCache {
|
|
|
52
55
|
* Invalidates all cache entries
|
|
53
56
|
*/
|
|
54
57
|
invalidate_all(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Invalidates cache entries for users who have access to specific scopes
|
|
60
|
+
* Used when scope details change (name, branding, etc.)
|
|
61
|
+
* @param scope_ids - Array of scope IDs to invalidate
|
|
62
|
+
*/
|
|
63
|
+
invalidate_by_scope_ids(scope_ids: string[]): void;
|
|
55
64
|
/**
|
|
56
65
|
* Gets the maximum cache version for a set of roles
|
|
57
66
|
* Used to determine if cache entry is stale
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth_cache.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/auth_cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"auth_cache.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/auth_cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAI/D;;;;GAIG;AACH,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,cAAM,SAAS;IACb,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAsB;gBAG5C,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM;IASzB;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IA+B5C;;;;;;;;OAQG;IACH,GAAG,CACD,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,YAAY,EAClB,WAAW,EAAE,MAAM,EAAE,EACrB,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,GAAE,YAAY,EAAO,GAC1B,IAAI;IA0BP;;;OAGG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAItC;;;;OAIG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAqB7C;;OAEG;IACH,cAAc,IAAI,IAAI;IAItB;;;;OAIG;IACH,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAalD;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAc5B;;;OAGG;IACH,SAAS,IAAI;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB;CAMF;AAMD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,GAAE,MAAc,EACxB,WAAW,GAAE,MAAW,EACxB,eAAe,GAAE,MAAW,GAC3B,SAAS,CAKX;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
|
@@ -47,8 +47,9 @@ class AuthCache {
|
|
|
47
47
|
* @param user - User data
|
|
48
48
|
* @param permissions - User permissions
|
|
49
49
|
* @param role_ids - User role IDs (v5.x: string UUIDs)
|
|
50
|
+
* @param scopes - User scope details with full information (v5.2+)
|
|
50
51
|
*/
|
|
51
|
-
set(user_id, user, permissions, role_ids) {
|
|
52
|
+
set(user_id, user, permissions, role_ids, scopes = []) {
|
|
52
53
|
// Evict LRU entries if cache is full
|
|
53
54
|
while (this.cache.size >= this.max_size) {
|
|
54
55
|
const first_key = this.cache.keys().next().value;
|
|
@@ -65,6 +66,7 @@ class AuthCache {
|
|
|
65
66
|
user,
|
|
66
67
|
permissions,
|
|
67
68
|
role_ids,
|
|
69
|
+
scopes,
|
|
68
70
|
timestamp: Date.now(),
|
|
69
71
|
cache_version,
|
|
70
72
|
};
|
|
@@ -106,6 +108,23 @@ class AuthCache {
|
|
|
106
108
|
invalidate_all() {
|
|
107
109
|
this.cache.clear();
|
|
108
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Invalidates cache entries for users who have access to specific scopes
|
|
113
|
+
* Used when scope details change (name, branding, etc.)
|
|
114
|
+
* @param scope_ids - Array of scope IDs to invalidate
|
|
115
|
+
*/
|
|
116
|
+
invalidate_by_scope_ids(scope_ids) {
|
|
117
|
+
const entries_to_remove = [];
|
|
118
|
+
for (const [user_id, entry] of this.cache.entries()) {
|
|
119
|
+
const has_scope = entry.scopes.some((s) => scope_ids.includes(s.id));
|
|
120
|
+
if (has_scope) {
|
|
121
|
+
entries_to_remove.push(user_id);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
for (const user_id of entries_to_remove) {
|
|
125
|
+
this.cache.delete(user_id);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
109
128
|
/**
|
|
110
129
|
* Gets the maximum cache version for a set of roles
|
|
111
130
|
* Used to determine if cache entry is stale
|
|
@@ -85,4 +85,117 @@ export declare class ScopeAccessError extends Error {
|
|
|
85
85
|
scope_name?: string;
|
|
86
86
|
}>);
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Full scope details with branding information
|
|
90
|
+
* Used in cache and tenant auth results for multi-tenancy support
|
|
91
|
+
*/
|
|
92
|
+
export type ScopeDetails = {
|
|
93
|
+
id: string;
|
|
94
|
+
name: string;
|
|
95
|
+
slug: string | null;
|
|
96
|
+
level: string;
|
|
97
|
+
parent_id: string | null;
|
|
98
|
+
role_id: string;
|
|
99
|
+
logo_url: string | null;
|
|
100
|
+
primary_color: string | null;
|
|
101
|
+
secondary_color: string | null;
|
|
102
|
+
tagline: string | null;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Tenant/organization information returned in tenant auth results
|
|
106
|
+
* Simplified view of scope for API responses
|
|
107
|
+
*/
|
|
108
|
+
export type TenantOrganization = {
|
|
109
|
+
id: string;
|
|
110
|
+
name: string;
|
|
111
|
+
slug: string | null;
|
|
112
|
+
level: string;
|
|
113
|
+
role_id: string;
|
|
114
|
+
is_super_admin: boolean;
|
|
115
|
+
branding?: {
|
|
116
|
+
logo_url: string | null;
|
|
117
|
+
primary_color: string | null;
|
|
118
|
+
secondary_color: string | null;
|
|
119
|
+
tagline: string | null;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Options for hazo_get_tenant_auth function
|
|
124
|
+
* Extends HazoAuthOptions with tenant-specific configuration
|
|
125
|
+
*/
|
|
126
|
+
export type TenantAuthOptions = HazoAuthOptions & {
|
|
127
|
+
/**
|
|
128
|
+
* Header name to check for scope ID (default: "X-Hazo-Scope-Id")
|
|
129
|
+
*/
|
|
130
|
+
scope_header_name?: string;
|
|
131
|
+
/**
|
|
132
|
+
* Cookie name to check for scope ID (uses cookie prefix if not specified)
|
|
133
|
+
*/
|
|
134
|
+
scope_cookie_name?: string;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Result type for hazo_get_tenant_auth function
|
|
138
|
+
* Extends HazoAuthResult with tenant-specific information
|
|
139
|
+
*/
|
|
140
|
+
export type TenantAuthResult = {
|
|
141
|
+
authenticated: true;
|
|
142
|
+
user: HazoAuthUser;
|
|
143
|
+
permissions: string[];
|
|
144
|
+
permission_ok: boolean;
|
|
145
|
+
missing_permissions?: string[];
|
|
146
|
+
organization: TenantOrganization | null;
|
|
147
|
+
/** Shorthand for organization?.id - commonly used for DB query filters */
|
|
148
|
+
organization_id: string | null;
|
|
149
|
+
user_scopes: ScopeDetails[];
|
|
150
|
+
scope_ok?: boolean;
|
|
151
|
+
scope_access_via?: ScopeAccessInfo;
|
|
152
|
+
} | {
|
|
153
|
+
authenticated: false;
|
|
154
|
+
user: null;
|
|
155
|
+
permissions: [];
|
|
156
|
+
permission_ok: false;
|
|
157
|
+
organization: null;
|
|
158
|
+
/** Shorthand for organization?.id - commonly used for DB query filters */
|
|
159
|
+
organization_id: null;
|
|
160
|
+
user_scopes: [];
|
|
161
|
+
scope_ok?: false;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Guaranteed authenticated result with non-null organization
|
|
165
|
+
* Returned by require_tenant_auth when validation passes
|
|
166
|
+
*/
|
|
167
|
+
export type RequiredTenantAuthResult = TenantAuthResult & {
|
|
168
|
+
authenticated: true;
|
|
169
|
+
organization: TenantOrganization;
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Base error class for all hazo_auth errors
|
|
173
|
+
* Provides error code and HTTP status code for API responses
|
|
174
|
+
*/
|
|
175
|
+
export declare class HazoAuthError extends Error {
|
|
176
|
+
readonly code: string;
|
|
177
|
+
readonly status_code: number;
|
|
178
|
+
constructor(message: string, code: string, status_code: number);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Error thrown when authentication is required but user is not authenticated
|
|
182
|
+
*/
|
|
183
|
+
export declare class AuthenticationRequiredError extends HazoAuthError {
|
|
184
|
+
constructor(message?: string);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Error thrown when a tenant/scope context is required but not provided
|
|
188
|
+
*/
|
|
189
|
+
export declare class TenantRequiredError extends HazoAuthError {
|
|
190
|
+
readonly user_scopes: ScopeDetails[];
|
|
191
|
+
constructor(message?: string, user_scopes?: ScopeDetails[]);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Error thrown when user lacks access to the requested tenant/scope
|
|
195
|
+
*/
|
|
196
|
+
export declare class TenantAccessDeniedError extends HazoAuthError {
|
|
197
|
+
readonly scope_id: string;
|
|
198
|
+
readonly user_scopes: ScopeDetails[];
|
|
199
|
+
constructor(scope_id: string, user_scopes?: ScopeDetails[]);
|
|
200
|
+
}
|
|
88
201
|
//# sourceMappingURL=auth_types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth_types.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/auth_types.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC/C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,cAAc,GACtB;IACE,aAAa,EAAE,IAAI,CAAC;IACpB,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,eAAe,CAAC;CACpC,GACD;IACE,aAAa,EAAE,KAAK,CAAC;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,WAAW,EAAE,EAAE,CAAC;IAChB,aAAa,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB,CAAC;AAEN;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IAE/B,mBAAmB,EAAE,MAAM,EAAE;IAC7B,gBAAgB,EAAE,MAAM,EAAE;IAC1B,oBAAoB,EAAE,MAAM,EAAE;IAC9B,qBAAqB,CAAC,EAAE,MAAM;IAC9B,uBAAuB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;gBAJ7C,mBAAmB,EAAE,MAAM,EAAE,EAC7B,gBAAgB,EAAE,MAAM,EAAE,EAC1B,oBAAoB,EAAE,MAAM,EAAE,EAC9B,qBAAqB,CAAC,EAAE,MAAM,YAAA,EAC9B,uBAAuB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,YAAA;CAKvD;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IAEhC,QAAQ,EAAE,MAAM;IAChB,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;gBAD7D,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAKvE"}
|
|
1
|
+
{"version":3,"file":"auth_types.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/auth_types.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,3 +28,46 @@ export class ScopeAccessError extends Error {
|
|
|
28
28
|
this.name = "ScopeAccessError";
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
// section: tenant_error_classes
|
|
32
|
+
/**
|
|
33
|
+
* Base error class for all hazo_auth errors
|
|
34
|
+
* Provides error code and HTTP status code for API responses
|
|
35
|
+
*/
|
|
36
|
+
export class HazoAuthError extends Error {
|
|
37
|
+
constructor(message, code, status_code) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.code = code;
|
|
40
|
+
this.status_code = status_code;
|
|
41
|
+
this.name = "HazoAuthError";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Error thrown when authentication is required but user is not authenticated
|
|
46
|
+
*/
|
|
47
|
+
export class AuthenticationRequiredError extends HazoAuthError {
|
|
48
|
+
constructor(message = "Authentication required") {
|
|
49
|
+
super(message, "AUTHENTICATION_REQUIRED", 401);
|
|
50
|
+
this.name = "AuthenticationRequiredError";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Error thrown when a tenant/scope context is required but not provided
|
|
55
|
+
*/
|
|
56
|
+
export class TenantRequiredError extends HazoAuthError {
|
|
57
|
+
constructor(message = "Tenant context required", user_scopes = []) {
|
|
58
|
+
super(message, "TENANT_REQUIRED", 403);
|
|
59
|
+
this.user_scopes = user_scopes;
|
|
60
|
+
this.name = "TenantRequiredError";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Error thrown when user lacks access to the requested tenant/scope
|
|
65
|
+
*/
|
|
66
|
+
export class TenantAccessDeniedError extends HazoAuthError {
|
|
67
|
+
constructor(scope_id, user_scopes = []) {
|
|
68
|
+
super(`Access denied to scope: ${scope_id}`, "TENANT_ACCESS_DENIED", 403);
|
|
69
|
+
this.scope_id = scope_id;
|
|
70
|
+
this.user_scopes = user_scopes;
|
|
71
|
+
this.name = "TenantAccessDeniedError";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hazo_get_auth.server.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/hazo_get_auth.server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK1C,OAAO,KAAK,EACV,cAAc,EAEd,eAAe,
|
|
1
|
+
{"version":3,"file":"hazo_get_auth.server.d.ts","sourceRoot":"","sources":["../../../src/lib/auth/hazo_get_auth.server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK1C,OAAO,KAAK,EACV,cAAc,EAEd,eAAe,EAGhB,MAAM,cAAc,CAAC;AAsWtB;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,cAAc,CAAC,CAmNzB"}
|