@payez/next-mvp 4.1.9 → 4.1.11
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 +792 -792
- package/dist/api-handlers/auth/refresh.js +38 -15
- package/dist/auth/better-auth.js +4 -3
- package/dist/components/account/MobileNavDrawer.js +18 -18
- package/dist/components/account/UserAvatarMenu.js +1 -1
- package/dist/components/admin/AlertSettingsTab.js +40 -40
- package/dist/components/admin/LoggingSettingsTab.js +28 -28
- package/dist/lib/ensure-fresh-access-token.js +42 -8
- package/dist/lib/idp-fetch.js +25 -9
- package/dist/lib/session-store.d.ts +1 -0
- package/dist/lib/session-store.js +12 -8
- package/dist/lib/token-lifecycle.d.ts +2 -0
- package/dist/lib/token-lifecycle.js +21 -3
- package/dist/pages/login/page.js +28 -28
- package/dist/pages/settings/EnhancedSettingsPage.js +17 -17
- package/package.json +893 -893
- package/scripts/dev-broker.ps1 +35 -35
- package/scripts/dev-local.ps1 +45 -45
- package/src/api/auth-handler.ts +548 -548
- package/src/api-handlers/account/change-password.ts +142 -142
- package/src/api-handlers/account/update-phone.ts +79 -79
- package/src/api-handlers/account/verify-email.ts +125 -125
- package/src/api-handlers/account/verify-sms.ts +125 -125
- package/src/api-handlers/admin/analytics.ts +443 -443
- package/src/api-handlers/admin/audit.ts +223 -223
- package/src/api-handlers/admin/index.ts +63 -63
- package/src/api-handlers/admin/redis-sessions.ts +251 -251
- package/src/api-handlers/admin/sessions.ts +318 -318
- package/src/api-handlers/admin/site-logs.ts +365 -365
- package/src/api-handlers/admin/users.ts +242 -242
- package/src/api-handlers/admin/vibe-data.ts +324 -324
- package/src/api-handlers/auth/refresh.ts +705 -682
- package/src/api-handlers/auth/signout.ts +199 -199
- package/src/api-handlers/auth/status.ts +20 -20
- package/src/api-handlers/auth/update-session.ts +110 -110
- package/src/api-handlers/auth/verify-code.ts +112 -112
- package/src/api-handlers/session/refresh-viability.ts +36 -36
- package/src/api-handlers/test/force-expire.ts +60 -60
- package/src/auth/auth-decision.ts +229 -229
- package/src/auth/better-auth.ts +467 -466
- package/src/auth/route-config.ts +219 -219
- package/src/auth/types/auth-types.ts +555 -555
- package/src/auth/types/index.ts +7 -7
- package/src/auth/utils/idp-client.ts +445 -445
- package/src/auth/utils/index.ts +6 -6
- package/src/auth/utils/token-utils.ts +243 -243
- package/src/client/AuthContext.tsx +143 -143
- package/src/client/fetch-with-auth.ts +47 -47
- package/src/components/SessionSync.tsx +138 -138
- package/src/components/SignalRHealthCheck.tsx +131 -131
- package/src/components/account/MobileNavDrawer.tsx +305 -305
- package/src/components/account/UserAvatarMenu.tsx +266 -266
- package/src/components/account/index.ts +13 -13
- package/src/components/admin/AlertSettingsTab.tsx +728 -728
- package/src/components/admin/AnalyticsTab.tsx +703 -703
- package/src/components/admin/DataBrowserTab.tsx +505 -505
- package/src/components/admin/LoggingSettingsTab.tsx +665 -665
- package/src/components/admin/SessionsTab.tsx +414 -414
- package/src/components/admin/StatsTab.tsx +379 -379
- package/src/components/admin/VibeAdminLayout.tsx +187 -187
- package/src/components/admin/index.ts +59 -59
- package/src/components/auth/FederatedAuthSection.tsx +95 -95
- package/src/components/auth/ModeAwareLoginPage.tsx +135 -135
- package/src/components/auth/ModeAwareSignupPage.tsx +267 -267
- package/src/components/auth/TraditionalAuthSection.tsx +99 -99
- package/src/components/recovery/CompleteStep.tsx +36 -36
- package/src/components/recovery/InitiateRecoveryStep.tsx +68 -68
- package/src/components/recovery/SelectMethodStep.tsx +73 -73
- package/src/components/recovery/SetPasswordStep.tsx +97 -97
- package/src/components/recovery/VerifyCodeStep.tsx +90 -90
- package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -160
- package/src/components/reserved/ReservedStatusBox.tsx +118 -118
- package/src/components/ui/BetaBadge.tsx +58 -58
- package/src/components/ui/Footer.tsx +93 -93
- package/src/config/env.ts +57 -57
- package/src/config/logger.ts +62 -62
- package/src/config/vibe-log-transport.ts +264 -264
- package/src/hooks/useAuth.ts +117 -117
- package/src/hooks/useAuthSettings.ts +97 -97
- package/src/hooks/useAvailableProviders.ts +120 -120
- package/src/hooks/usePasswordValidation.ts +127 -127
- package/src/hooks/usePublicAuthSettings.ts +149 -149
- package/src/hooks/useSessionExpiration.ts +101 -101
- package/src/hooks/useViabilitySession.ts +336 -336
- package/src/index.ts +60 -60
- package/src/lib/api-handler.ts +643 -643
- package/src/lib/app-slug.ts +178 -178
- package/src/lib/ensure-fresh-access-token.ts +369 -335
- package/src/lib/geolocation.ts +265 -265
- package/src/lib/idp-fetch.ts +26 -10
- package/src/lib/jwt-decode.ts +82 -82
- package/src/lib/roles.ts +177 -177
- package/src/lib/session-store.ts +4 -1
- package/src/lib/site-logger.ts +245 -245
- package/src/lib/standardized-client-api.ts +901 -901
- package/src/lib/test-aware-get-token.ts +41 -41
- package/src/lib/token-lifecycle.ts +30 -3
- package/src/lib/user-agent-parser.ts +252 -252
- package/src/logging/api/admin-analytics.ts +51 -51
- package/src/logging/api/audit-log.ts +53 -53
- package/src/logging/components/AdminAnalyticsLayout.tsx +49 -49
- package/src/logging/components/AuditLogViewer.tsx +125 -125
- package/src/logging/components/ErrorMetricsCard.tsx +98 -98
- package/src/logging/components/HealthMetricsCard.tsx +70 -70
- package/src/logging/hooks/useAdminAnalytics.ts +22 -22
- package/src/logging/hooks/useAuditLog.ts +24 -24
- package/src/logging/hooks/useErrorMetrics.ts +40 -40
- package/src/logging/hooks/useHealthMetrics.ts +44 -44
- package/src/logging/index.ts +18 -18
- package/src/logging/types/analytics.ts +81 -81
- package/src/logging/types/audit.ts +31 -31
- package/src/logging/types/index.ts +3 -3
- package/src/middleware/create-middleware.ts +663 -663
- package/src/middleware/rbac-check.ts +279 -279
- package/src/middleware/twofa-presets.ts +224 -224
- package/src/pages/admin-login/page.tsx +221 -221
- package/src/pages/admin-page-permissions/PagePermissionsAdminPage.tsx +527 -527
- package/src/pages/admin-page-permissions/index.ts +7 -7
- package/src/pages/admin-roles/RolesAdminPage.tsx +533 -533
- package/src/pages/admin-roles/modals.tsx +469 -469
- package/src/pages/client-admin/ClientSiteAdminPage.tsx +382 -382
- package/src/pages/client-admin/index.ts +33 -33
- package/src/pages/coming-soon/page.tsx +83 -83
- package/src/pages/login/page.tsx +456 -456
- package/src/pages/profile/EnhancedProfilePage.tsx +485 -485
- package/src/pages/profile/index.ts +9 -9
- package/src/pages/profile/page.tsx +166 -166
- package/src/pages/recovery/page.tsx +234 -234
- package/src/pages/roles/MyRolesPage.tsx +211 -211
- package/src/pages/roles/components.tsx +294 -294
- package/src/pages/roles/index.ts +17 -17
- package/src/pages/security/EnhancedSecurityPage.tsx +565 -565
- package/src/pages/security/index.ts +9 -9
- package/src/pages/security/page.tsx +507 -507
- package/src/pages/settings/EnhancedSettingsPage.tsx +642 -642
- package/src/pages/settings/index.ts +9 -9
- package/src/pages/settings/page.tsx +47 -47
- package/src/pages/showcase/ShowcasePage.tsx +532 -532
- package/src/pages/showcase/index.ts +13 -13
- package/src/pages/test-env/EmergencyLogoutPage.tsx +180 -180
- package/src/pages/test-env/JwtInspectPage.tsx +456 -456
- package/src/pages/test-env/RefreshTokenPage.tsx +157 -157
- package/src/pages/test-env/TestEnvPage.tsx +118 -118
- package/src/pages/test-env/index.ts +25 -25
- package/src/pages/verify-code/page.tsx +652 -652
- package/src/routes/account/update-phone.ts +13 -13
- package/src/routes/auth/logout.ts +108 -108
- package/src/routes/auth/nextauth.ts +45 -45
- package/src/routes/auth/settings.ts +75 -75
- package/src/routes/auth/viability.ts +224 -224
- package/src/server/auth-guard.ts +170 -170
- package/src/server/slim-middleware.ts +117 -117
- package/src/server/with-auth.ts +93 -93
- package/src/stores/authStore.ts +1899 -1899
- package/src/templates/instrumentation.ts +41 -41
- package/src/utils/logout.ts +30 -30
- package/src/vibe/client.ts +590 -590
- package/src/vibe/enterprise-auth.ts +208 -208
- package/src/vibe/errors.ts +185 -185
- package/src/vibe/generic.ts +431 -431
- package/src/vibe/hooks/index.ts +367 -367
- package/src/vibe/index.ts +132 -132
- package/src/vibe/sessions.ts +551 -551
- package/src/vibe/types.ts +577 -577
|
@@ -1,208 +1,208 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* =============================================================================
|
|
3
|
-
* VIBE ENTERPRISE AUTHENTICATION
|
|
4
|
-
* =============================================================================
|
|
5
|
-
*
|
|
6
|
-
* Server-side HMAC authentication for enterprise/service account requests.
|
|
7
|
-
* Validates incoming requests with X-Vibe-Client-Id, X-Vibe-Timestamp, and
|
|
8
|
-
* X-Vibe-Signature headers.
|
|
9
|
-
*
|
|
10
|
-
* Usage in Next.js API routes:
|
|
11
|
-
* import { validateEnterpriseAuth, hasEnterpriseAuthHeaders } from '@payez/next-mvp/vibe/enterprise-auth'
|
|
12
|
-
*
|
|
13
|
-
* export async function GET(request: NextRequest) {
|
|
14
|
-
* if (hasEnterpriseAuthHeaders(request)) {
|
|
15
|
-
* const auth = await validateEnterpriseAuth(request, ENTERPRISE_CLIENTS);
|
|
16
|
-
* if (!auth.success) {
|
|
17
|
-
* return NextResponse.json({ error: auth.error }, { status: 401 });
|
|
18
|
-
* }
|
|
19
|
-
* // Use auth.clientId for authenticated requests
|
|
20
|
-
* }
|
|
21
|
-
* }
|
|
22
|
-
*
|
|
23
|
-
* =============================================================================
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { NextRequest } from 'next/server';
|
|
27
|
-
import crypto from 'crypto';
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Enterprise client credentials configuration
|
|
31
|
-
* Maps client IDs to their HMAC secret keys (base64-encoded)
|
|
32
|
-
*/
|
|
33
|
-
export interface EnterpriseClientsConfig {
|
|
34
|
-
[clientId: string]: string; // clientId -> base64-encoded secret key
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface EnterpriseAuthResult {
|
|
38
|
-
success: boolean;
|
|
39
|
-
clientId?: string;
|
|
40
|
-
error?: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Validates enterprise HMAC authentication headers on incoming requests.
|
|
45
|
-
*
|
|
46
|
-
* Expected headers:
|
|
47
|
-
* - X-Vibe-Client-Id: The client identifier
|
|
48
|
-
* - X-Vibe-Timestamp: Unix timestamp in seconds
|
|
49
|
-
* - X-Vibe-Signature: HMAC-SHA256 signature of "{timestamp}|{method}|{path}"
|
|
50
|
-
*
|
|
51
|
-
* Security features:
|
|
52
|
-
* - Constant-time signature comparison (prevents timing attacks)
|
|
53
|
-
* - Timestamp validation with 5-minute window (prevents replay attacks)
|
|
54
|
-
* - Base64-encoded secret keys
|
|
55
|
-
*
|
|
56
|
-
* @param request - The Next.js request object
|
|
57
|
-
* @param enterpriseClients - Map of client IDs to secret keys
|
|
58
|
-
* @returns Authentication result with success status and client ID
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* const CLIENTS = {
|
|
62
|
-
* 'vibe_abc123': 'base64SecretKey=='
|
|
63
|
-
* };
|
|
64
|
-
* const result = await validateEnterpriseAuth(request, CLIENTS);
|
|
65
|
-
* if (result.success) {
|
|
66
|
-
* console.log(`Authenticated client: ${result.clientId}`);
|
|
67
|
-
* }
|
|
68
|
-
*/
|
|
69
|
-
export async function validateEnterpriseAuth(
|
|
70
|
-
request: NextRequest,
|
|
71
|
-
enterpriseClients: EnterpriseClientsConfig
|
|
72
|
-
): Promise<EnterpriseAuthResult> {
|
|
73
|
-
// Check for required headers
|
|
74
|
-
const clientId = request.headers.get('X-Vibe-Client-Id');
|
|
75
|
-
const timestamp = request.headers.get('X-Vibe-Timestamp');
|
|
76
|
-
const signature = request.headers.get('X-Vibe-Signature');
|
|
77
|
-
|
|
78
|
-
// If any header is missing, this is not an enterprise auth request
|
|
79
|
-
if (!clientId || !timestamp || !signature) {
|
|
80
|
-
return {
|
|
81
|
-
success: false,
|
|
82
|
-
error: 'MISSING_ENTERPRISE_HEADERS'
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Validate client ID exists in configuration
|
|
87
|
-
const secretKey = enterpriseClients[clientId];
|
|
88
|
-
if (!secretKey) {
|
|
89
|
-
return {
|
|
90
|
-
success: false,
|
|
91
|
-
error: 'INVALID_CLIENT_ID'
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Validate timestamp is recent (within 5 minutes)
|
|
96
|
-
const now = Math.floor(Date.now() / 1000);
|
|
97
|
-
const requestTime = parseInt(timestamp, 10);
|
|
98
|
-
|
|
99
|
-
if (isNaN(requestTime)) {
|
|
100
|
-
return {
|
|
101
|
-
success: false,
|
|
102
|
-
error: 'INVALID_TIMESTAMP'
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const timeDiff = Math.abs(now - requestTime);
|
|
107
|
-
if (timeDiff > 300) { // 5 minutes
|
|
108
|
-
return {
|
|
109
|
-
success: false,
|
|
110
|
-
error: 'TIMESTAMP_EXPIRED'
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Compute expected signature
|
|
115
|
-
// Format: "{timestamp}|{method}|{path}"
|
|
116
|
-
const method = request.method;
|
|
117
|
-
const url = new URL(request.url);
|
|
118
|
-
const path = url.pathname;
|
|
119
|
-
|
|
120
|
-
const message = `${timestamp}|${method}|${path}`;
|
|
121
|
-
const expectedSignature = crypto
|
|
122
|
-
.createHmac('sha256', Buffer.from(secretKey, 'base64'))
|
|
123
|
-
.update(message)
|
|
124
|
-
.digest('base64');
|
|
125
|
-
|
|
126
|
-
// Compare signatures (constant-time comparison to prevent timing attacks)
|
|
127
|
-
if (!crypto.timingSafeEqual(
|
|
128
|
-
Buffer.from(signature),
|
|
129
|
-
Buffer.from(expectedSignature)
|
|
130
|
-
)) {
|
|
131
|
-
return {
|
|
132
|
-
success: false,
|
|
133
|
-
error: 'INVALID_SIGNATURE'
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Enterprise auth successful
|
|
138
|
-
return {
|
|
139
|
-
success: true,
|
|
140
|
-
clientId
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Checks if request has enterprise authentication headers.
|
|
146
|
-
* Does not validate - just checks if all required headers are present.
|
|
147
|
-
*
|
|
148
|
-
* @param request - The Next.js request object
|
|
149
|
-
* @returns True if all enterprise auth headers are present
|
|
150
|
-
*
|
|
151
|
-
* @example
|
|
152
|
-
* if (hasEnterpriseAuthHeaders(request)) {
|
|
153
|
-
* // Validate the headers
|
|
154
|
-
* const auth = await validateEnterpriseAuth(request, clients);
|
|
155
|
-
* } else {
|
|
156
|
-
* // Fall back to user session auth
|
|
157
|
-
* const token = await ensureFreshToken(request);
|
|
158
|
-
* }
|
|
159
|
-
*/
|
|
160
|
-
export function hasEnterpriseAuthHeaders(request: NextRequest): boolean {
|
|
161
|
-
return !!(
|
|
162
|
-
request.headers.get('X-Vibe-Client-Id') &&
|
|
163
|
-
request.headers.get('X-Vibe-Timestamp') &&
|
|
164
|
-
request.headers.get('X-Vibe-Signature')
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Generates HMAC signature for backend API requests.
|
|
170
|
-
* Used when frontend needs to proxy enterprise auth requests to backend
|
|
171
|
-
* with a different path (e.g., /api/vibe/* -> /v1/collections/*).
|
|
172
|
-
*
|
|
173
|
-
* @param clientId - The Vibe client ID
|
|
174
|
-
* @param secretKey - Base64-encoded HMAC secret key
|
|
175
|
-
* @param timestamp - Unix timestamp (seconds) as string
|
|
176
|
-
* @param method - HTTP method (GET, POST, etc)
|
|
177
|
-
* @param backendPath - The backend API path (e.g., "/v1/collections/agent_mail/tables")
|
|
178
|
-
* @returns HMAC signature for the backend request
|
|
179
|
-
*
|
|
180
|
-
* @example
|
|
181
|
-
* // Frontend received request for /api/vibe/agent_mail/tables
|
|
182
|
-
* // Need to call backend at /v1/collections/agent_mail/tables
|
|
183
|
-
* const signature = generateBackendHmacSignature(
|
|
184
|
-
* 'vibe_abc123',
|
|
185
|
-
* 'base64SecretKey==',
|
|
186
|
-
* '1234567890',
|
|
187
|
-
* 'GET',
|
|
188
|
-
* '/v1/collections/agent_mail/tables'
|
|
189
|
-
* );
|
|
190
|
-
* // Use signature in backend request headers
|
|
191
|
-
*/
|
|
192
|
-
export function generateBackendHmacSignature(
|
|
193
|
-
clientId: string,
|
|
194
|
-
secretKey: string,
|
|
195
|
-
timestamp: string,
|
|
196
|
-
method: string,
|
|
197
|
-
backendPath: string
|
|
198
|
-
): string {
|
|
199
|
-
if (!secretKey) {
|
|
200
|
-
throw new Error(`No secret key provided for client ID: ${clientId}`);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const message = `${timestamp}|${method}|${backendPath}`;
|
|
204
|
-
return crypto
|
|
205
|
-
.createHmac('sha256', Buffer.from(secretKey, 'base64'))
|
|
206
|
-
.update(message)
|
|
207
|
-
.digest('base64');
|
|
208
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* =============================================================================
|
|
3
|
+
* VIBE ENTERPRISE AUTHENTICATION
|
|
4
|
+
* =============================================================================
|
|
5
|
+
*
|
|
6
|
+
* Server-side HMAC authentication for enterprise/service account requests.
|
|
7
|
+
* Validates incoming requests with X-Vibe-Client-Id, X-Vibe-Timestamp, and
|
|
8
|
+
* X-Vibe-Signature headers.
|
|
9
|
+
*
|
|
10
|
+
* Usage in Next.js API routes:
|
|
11
|
+
* import { validateEnterpriseAuth, hasEnterpriseAuthHeaders } from '@payez/next-mvp/vibe/enterprise-auth'
|
|
12
|
+
*
|
|
13
|
+
* export async function GET(request: NextRequest) {
|
|
14
|
+
* if (hasEnterpriseAuthHeaders(request)) {
|
|
15
|
+
* const auth = await validateEnterpriseAuth(request, ENTERPRISE_CLIENTS);
|
|
16
|
+
* if (!auth.success) {
|
|
17
|
+
* return NextResponse.json({ error: auth.error }, { status: 401 });
|
|
18
|
+
* }
|
|
19
|
+
* // Use auth.clientId for authenticated requests
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* =============================================================================
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { NextRequest } from 'next/server';
|
|
27
|
+
import crypto from 'crypto';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Enterprise client credentials configuration
|
|
31
|
+
* Maps client IDs to their HMAC secret keys (base64-encoded)
|
|
32
|
+
*/
|
|
33
|
+
export interface EnterpriseClientsConfig {
|
|
34
|
+
[clientId: string]: string; // clientId -> base64-encoded secret key
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface EnterpriseAuthResult {
|
|
38
|
+
success: boolean;
|
|
39
|
+
clientId?: string;
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validates enterprise HMAC authentication headers on incoming requests.
|
|
45
|
+
*
|
|
46
|
+
* Expected headers:
|
|
47
|
+
* - X-Vibe-Client-Id: The client identifier
|
|
48
|
+
* - X-Vibe-Timestamp: Unix timestamp in seconds
|
|
49
|
+
* - X-Vibe-Signature: HMAC-SHA256 signature of "{timestamp}|{method}|{path}"
|
|
50
|
+
*
|
|
51
|
+
* Security features:
|
|
52
|
+
* - Constant-time signature comparison (prevents timing attacks)
|
|
53
|
+
* - Timestamp validation with 5-minute window (prevents replay attacks)
|
|
54
|
+
* - Base64-encoded secret keys
|
|
55
|
+
*
|
|
56
|
+
* @param request - The Next.js request object
|
|
57
|
+
* @param enterpriseClients - Map of client IDs to secret keys
|
|
58
|
+
* @returns Authentication result with success status and client ID
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* const CLIENTS = {
|
|
62
|
+
* 'vibe_abc123': 'base64SecretKey=='
|
|
63
|
+
* };
|
|
64
|
+
* const result = await validateEnterpriseAuth(request, CLIENTS);
|
|
65
|
+
* if (result.success) {
|
|
66
|
+
* console.log(`Authenticated client: ${result.clientId}`);
|
|
67
|
+
* }
|
|
68
|
+
*/
|
|
69
|
+
export async function validateEnterpriseAuth(
|
|
70
|
+
request: NextRequest,
|
|
71
|
+
enterpriseClients: EnterpriseClientsConfig
|
|
72
|
+
): Promise<EnterpriseAuthResult> {
|
|
73
|
+
// Check for required headers
|
|
74
|
+
const clientId = request.headers.get('X-Vibe-Client-Id');
|
|
75
|
+
const timestamp = request.headers.get('X-Vibe-Timestamp');
|
|
76
|
+
const signature = request.headers.get('X-Vibe-Signature');
|
|
77
|
+
|
|
78
|
+
// If any header is missing, this is not an enterprise auth request
|
|
79
|
+
if (!clientId || !timestamp || !signature) {
|
|
80
|
+
return {
|
|
81
|
+
success: false,
|
|
82
|
+
error: 'MISSING_ENTERPRISE_HEADERS'
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Validate client ID exists in configuration
|
|
87
|
+
const secretKey = enterpriseClients[clientId];
|
|
88
|
+
if (!secretKey) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: 'INVALID_CLIENT_ID'
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Validate timestamp is recent (within 5 minutes)
|
|
96
|
+
const now = Math.floor(Date.now() / 1000);
|
|
97
|
+
const requestTime = parseInt(timestamp, 10);
|
|
98
|
+
|
|
99
|
+
if (isNaN(requestTime)) {
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
error: 'INVALID_TIMESTAMP'
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const timeDiff = Math.abs(now - requestTime);
|
|
107
|
+
if (timeDiff > 300) { // 5 minutes
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
error: 'TIMESTAMP_EXPIRED'
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Compute expected signature
|
|
115
|
+
// Format: "{timestamp}|{method}|{path}"
|
|
116
|
+
const method = request.method;
|
|
117
|
+
const url = new URL(request.url);
|
|
118
|
+
const path = url.pathname;
|
|
119
|
+
|
|
120
|
+
const message = `${timestamp}|${method}|${path}`;
|
|
121
|
+
const expectedSignature = crypto
|
|
122
|
+
.createHmac('sha256', Buffer.from(secretKey, 'base64'))
|
|
123
|
+
.update(message)
|
|
124
|
+
.digest('base64');
|
|
125
|
+
|
|
126
|
+
// Compare signatures (constant-time comparison to prevent timing attacks)
|
|
127
|
+
if (!crypto.timingSafeEqual(
|
|
128
|
+
Buffer.from(signature),
|
|
129
|
+
Buffer.from(expectedSignature)
|
|
130
|
+
)) {
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
error: 'INVALID_SIGNATURE'
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Enterprise auth successful
|
|
138
|
+
return {
|
|
139
|
+
success: true,
|
|
140
|
+
clientId
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Checks if request has enterprise authentication headers.
|
|
146
|
+
* Does not validate - just checks if all required headers are present.
|
|
147
|
+
*
|
|
148
|
+
* @param request - The Next.js request object
|
|
149
|
+
* @returns True if all enterprise auth headers are present
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* if (hasEnterpriseAuthHeaders(request)) {
|
|
153
|
+
* // Validate the headers
|
|
154
|
+
* const auth = await validateEnterpriseAuth(request, clients);
|
|
155
|
+
* } else {
|
|
156
|
+
* // Fall back to user session auth
|
|
157
|
+
* const token = await ensureFreshToken(request);
|
|
158
|
+
* }
|
|
159
|
+
*/
|
|
160
|
+
export function hasEnterpriseAuthHeaders(request: NextRequest): boolean {
|
|
161
|
+
return !!(
|
|
162
|
+
request.headers.get('X-Vibe-Client-Id') &&
|
|
163
|
+
request.headers.get('X-Vibe-Timestamp') &&
|
|
164
|
+
request.headers.get('X-Vibe-Signature')
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Generates HMAC signature for backend API requests.
|
|
170
|
+
* Used when frontend needs to proxy enterprise auth requests to backend
|
|
171
|
+
* with a different path (e.g., /api/vibe/* -> /v1/collections/*).
|
|
172
|
+
*
|
|
173
|
+
* @param clientId - The Vibe client ID
|
|
174
|
+
* @param secretKey - Base64-encoded HMAC secret key
|
|
175
|
+
* @param timestamp - Unix timestamp (seconds) as string
|
|
176
|
+
* @param method - HTTP method (GET, POST, etc)
|
|
177
|
+
* @param backendPath - The backend API path (e.g., "/v1/collections/agent_mail/tables")
|
|
178
|
+
* @returns HMAC signature for the backend request
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* // Frontend received request for /api/vibe/agent_mail/tables
|
|
182
|
+
* // Need to call backend at /v1/collections/agent_mail/tables
|
|
183
|
+
* const signature = generateBackendHmacSignature(
|
|
184
|
+
* 'vibe_abc123',
|
|
185
|
+
* 'base64SecretKey==',
|
|
186
|
+
* '1234567890',
|
|
187
|
+
* 'GET',
|
|
188
|
+
* '/v1/collections/agent_mail/tables'
|
|
189
|
+
* );
|
|
190
|
+
* // Use signature in backend request headers
|
|
191
|
+
*/
|
|
192
|
+
export function generateBackendHmacSignature(
|
|
193
|
+
clientId: string,
|
|
194
|
+
secretKey: string,
|
|
195
|
+
timestamp: string,
|
|
196
|
+
method: string,
|
|
197
|
+
backendPath: string
|
|
198
|
+
): string {
|
|
199
|
+
if (!secretKey) {
|
|
200
|
+
throw new Error(`No secret key provided for client ID: ${clientId}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const message = `${timestamp}|${method}|${backendPath}`;
|
|
204
|
+
return crypto
|
|
205
|
+
.createHmac('sha256', Buffer.from(secretKey, 'base64'))
|
|
206
|
+
.update(message)
|
|
207
|
+
.digest('base64');
|
|
208
|
+
}
|