@payez/next-mvp 3.0.0 → 3.1.1
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/dist/auth/callbacks/jwt.js +305 -305
- package/dist/auth/providers/credentials.js +223 -223
- package/dist/lib/idp-client-config.js +4 -2
- package/dist/vibe/enterprise-auth.d.ts +106 -0
- package/dist/vibe/enterprise-auth.js +173 -0
- package/dist/vibe/index.d.ts +2 -0
- package/dist/vibe/index.js +6 -1
- package/package.json +1 -1
- package/src/lib/idp-client-config.ts +4 -2
- package/src/vibe/enterprise-auth.ts +208 -0
- package/src/vibe/index.ts +11 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* =============================================================================
|
|
4
|
+
* VIBE ENTERPRISE AUTHENTICATION
|
|
5
|
+
* =============================================================================
|
|
6
|
+
*
|
|
7
|
+
* Server-side HMAC authentication for enterprise/service account requests.
|
|
8
|
+
* Validates incoming requests with X-Vibe-Client-Id, X-Vibe-Timestamp, and
|
|
9
|
+
* X-Vibe-Signature headers.
|
|
10
|
+
*
|
|
11
|
+
* Usage in Next.js API routes:
|
|
12
|
+
* import { validateEnterpriseAuth, hasEnterpriseAuthHeaders } from '@payez/next-mvp/vibe/enterprise-auth'
|
|
13
|
+
*
|
|
14
|
+
* export async function GET(request: NextRequest) {
|
|
15
|
+
* if (hasEnterpriseAuthHeaders(request)) {
|
|
16
|
+
* const auth = await validateEnterpriseAuth(request, ENTERPRISE_CLIENTS);
|
|
17
|
+
* if (!auth.success) {
|
|
18
|
+
* return NextResponse.json({ error: auth.error }, { status: 401 });
|
|
19
|
+
* }
|
|
20
|
+
* // Use auth.clientId for authenticated requests
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* =============================================================================
|
|
25
|
+
*/
|
|
26
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
|
+
};
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.validateEnterpriseAuth = validateEnterpriseAuth;
|
|
31
|
+
exports.hasEnterpriseAuthHeaders = hasEnterpriseAuthHeaders;
|
|
32
|
+
exports.generateBackendHmacSignature = generateBackendHmacSignature;
|
|
33
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
34
|
+
/**
|
|
35
|
+
* Validates enterprise HMAC authentication headers on incoming requests.
|
|
36
|
+
*
|
|
37
|
+
* Expected headers:
|
|
38
|
+
* - X-Vibe-Client-Id: The client identifier
|
|
39
|
+
* - X-Vibe-Timestamp: Unix timestamp in seconds
|
|
40
|
+
* - X-Vibe-Signature: HMAC-SHA256 signature of "{timestamp}|{method}|{path}"
|
|
41
|
+
*
|
|
42
|
+
* Security features:
|
|
43
|
+
* - Constant-time signature comparison (prevents timing attacks)
|
|
44
|
+
* - Timestamp validation with 5-minute window (prevents replay attacks)
|
|
45
|
+
* - Base64-encoded secret keys
|
|
46
|
+
*
|
|
47
|
+
* @param request - The Next.js request object
|
|
48
|
+
* @param enterpriseClients - Map of client IDs to secret keys
|
|
49
|
+
* @returns Authentication result with success status and client ID
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* const CLIENTS = {
|
|
53
|
+
* 'vibe_abc123': 'base64SecretKey=='
|
|
54
|
+
* };
|
|
55
|
+
* const result = await validateEnterpriseAuth(request, CLIENTS);
|
|
56
|
+
* if (result.success) {
|
|
57
|
+
* console.log(`Authenticated client: ${result.clientId}`);
|
|
58
|
+
* }
|
|
59
|
+
*/
|
|
60
|
+
async function validateEnterpriseAuth(request, enterpriseClients) {
|
|
61
|
+
// Check for required headers
|
|
62
|
+
const clientId = request.headers.get('X-Vibe-Client-Id');
|
|
63
|
+
const timestamp = request.headers.get('X-Vibe-Timestamp');
|
|
64
|
+
const signature = request.headers.get('X-Vibe-Signature');
|
|
65
|
+
// If any header is missing, this is not an enterprise auth request
|
|
66
|
+
if (!clientId || !timestamp || !signature) {
|
|
67
|
+
return {
|
|
68
|
+
success: false,
|
|
69
|
+
error: 'MISSING_ENTERPRISE_HEADERS'
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// Validate client ID exists in configuration
|
|
73
|
+
const secretKey = enterpriseClients[clientId];
|
|
74
|
+
if (!secretKey) {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: 'INVALID_CLIENT_ID'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// Validate timestamp is recent (within 5 minutes)
|
|
81
|
+
const now = Math.floor(Date.now() / 1000);
|
|
82
|
+
const requestTime = parseInt(timestamp, 10);
|
|
83
|
+
if (isNaN(requestTime)) {
|
|
84
|
+
return {
|
|
85
|
+
success: false,
|
|
86
|
+
error: 'INVALID_TIMESTAMP'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const timeDiff = Math.abs(now - requestTime);
|
|
90
|
+
if (timeDiff > 300) { // 5 minutes
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: 'TIMESTAMP_EXPIRED'
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Compute expected signature
|
|
97
|
+
// Format: "{timestamp}|{method}|{path}"
|
|
98
|
+
const method = request.method;
|
|
99
|
+
const url = new URL(request.url);
|
|
100
|
+
const path = url.pathname;
|
|
101
|
+
const message = `${timestamp}|${method}|${path}`;
|
|
102
|
+
const expectedSignature = crypto_1.default
|
|
103
|
+
.createHmac('sha256', Buffer.from(secretKey, 'base64'))
|
|
104
|
+
.update(message)
|
|
105
|
+
.digest('base64');
|
|
106
|
+
// Compare signatures (constant-time comparison to prevent timing attacks)
|
|
107
|
+
if (!crypto_1.default.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
error: 'INVALID_SIGNATURE'
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// Enterprise auth successful
|
|
114
|
+
return {
|
|
115
|
+
success: true,
|
|
116
|
+
clientId
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Checks if request has enterprise authentication headers.
|
|
121
|
+
* Does not validate - just checks if all required headers are present.
|
|
122
|
+
*
|
|
123
|
+
* @param request - The Next.js request object
|
|
124
|
+
* @returns True if all enterprise auth headers are present
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* if (hasEnterpriseAuthHeaders(request)) {
|
|
128
|
+
* // Validate the headers
|
|
129
|
+
* const auth = await validateEnterpriseAuth(request, clients);
|
|
130
|
+
* } else {
|
|
131
|
+
* // Fall back to user session auth
|
|
132
|
+
* const token = await ensureFreshToken(request);
|
|
133
|
+
* }
|
|
134
|
+
*/
|
|
135
|
+
function hasEnterpriseAuthHeaders(request) {
|
|
136
|
+
return !!(request.headers.get('X-Vibe-Client-Id') &&
|
|
137
|
+
request.headers.get('X-Vibe-Timestamp') &&
|
|
138
|
+
request.headers.get('X-Vibe-Signature'));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Generates HMAC signature for backend API requests.
|
|
142
|
+
* Used when frontend needs to proxy enterprise auth requests to backend
|
|
143
|
+
* with a different path (e.g., /api/vibe/* -> /v1/collections/*).
|
|
144
|
+
*
|
|
145
|
+
* @param clientId - The Vibe client ID
|
|
146
|
+
* @param secretKey - Base64-encoded HMAC secret key
|
|
147
|
+
* @param timestamp - Unix timestamp (seconds) as string
|
|
148
|
+
* @param method - HTTP method (GET, POST, etc)
|
|
149
|
+
* @param backendPath - The backend API path (e.g., "/v1/collections/agent_mail/tables")
|
|
150
|
+
* @returns HMAC signature for the backend request
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* // Frontend received request for /api/vibe/agent_mail/tables
|
|
154
|
+
* // Need to call backend at /v1/collections/agent_mail/tables
|
|
155
|
+
* const signature = generateBackendHmacSignature(
|
|
156
|
+
* 'vibe_abc123',
|
|
157
|
+
* 'base64SecretKey==',
|
|
158
|
+
* '1234567890',
|
|
159
|
+
* 'GET',
|
|
160
|
+
* '/v1/collections/agent_mail/tables'
|
|
161
|
+
* );
|
|
162
|
+
* // Use signature in backend request headers
|
|
163
|
+
*/
|
|
164
|
+
function generateBackendHmacSignature(clientId, secretKey, timestamp, method, backendPath) {
|
|
165
|
+
if (!secretKey) {
|
|
166
|
+
throw new Error(`No secret key provided for client ID: ${clientId}`);
|
|
167
|
+
}
|
|
168
|
+
const message = `${timestamp}|${method}|${backendPath}`;
|
|
169
|
+
return crypto_1.default
|
|
170
|
+
.createHmac('sha256', Buffer.from(secretKey, 'base64'))
|
|
171
|
+
.update(message)
|
|
172
|
+
.digest('base64');
|
|
173
|
+
}
|
package/dist/vibe/index.d.ts
CHANGED
|
@@ -21,3 +21,5 @@ export { createLoginSession, getUserSessions, getAllSessions, getSessionById, re
|
|
|
21
21
|
export type { CreateSessionInput, SessionQueryOptions } from './sessions';
|
|
22
22
|
export { vibeCollection, vibeTable, vibeTablePath, vibeQueryPath, vibeGridPath, unwrapVibeDocument, extractVibeDocuments, GenericCollection, GenericTableDelegate, } from './generic';
|
|
23
23
|
export type { VibeDocumentWrapper } from './generic';
|
|
24
|
+
export { validateEnterpriseAuth, hasEnterpriseAuthHeaders, generateBackendHmacSignature, } from './enterprise-auth';
|
|
25
|
+
export type { EnterpriseClientsConfig, EnterpriseAuthResult, } from './enterprise-auth';
|
package/dist/vibe/index.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* =============================================================================
|
|
15
15
|
*/
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.GenericTableDelegate = exports.GenericCollection = exports.extractVibeDocuments = exports.unwrapVibeDocument = exports.vibeGridPath = exports.vibeQueryPath = exports.vibeTablePath = exports.vibeTable = exports.vibeCollection = exports.getSessionStats = exports.checkSessionRevocation = exports.isSessionValid = exports.updateSessionActivity = exports.revokeAllUserSessions = exports.revokeSession = exports.getSessionById = exports.getAllSessions = exports.getUserSessions = exports.createLoginSession = exports.VibeServiceError = exports.VibeConflictError = exports.VibeRateLimitError = exports.VibeAuthError = exports.VibeValidationError = exports.VibeNotFoundError = exports.VibeError = exports.VibeSiteLog = exports.VibeComment = exports.VibeTag = exports.VibeActivityLog = exports.VibeNotification = exports.VibeFile = exports.VibeSetting = exports.VibeProfile = exports.VibeLoginSession = exports.VibeUser = exports.VibeTableDelegate = exports.VibeClient = exports.createVibeClient = exports.vibe = void 0;
|
|
17
|
+
exports.generateBackendHmacSignature = exports.hasEnterpriseAuthHeaders = exports.validateEnterpriseAuth = exports.GenericTableDelegate = exports.GenericCollection = exports.extractVibeDocuments = exports.unwrapVibeDocument = exports.vibeGridPath = exports.vibeQueryPath = exports.vibeTablePath = exports.vibeTable = exports.vibeCollection = exports.getSessionStats = exports.checkSessionRevocation = exports.isSessionValid = exports.updateSessionActivity = exports.revokeAllUserSessions = exports.revokeSession = exports.getSessionById = exports.getAllSessions = exports.getUserSessions = exports.createLoginSession = exports.VibeServiceError = exports.VibeConflictError = exports.VibeRateLimitError = exports.VibeAuthError = exports.VibeValidationError = exports.VibeNotFoundError = exports.VibeError = exports.VibeSiteLog = exports.VibeComment = exports.VibeTag = exports.VibeActivityLog = exports.VibeNotification = exports.VibeFile = exports.VibeSetting = exports.VibeProfile = exports.VibeLoginSession = exports.VibeUser = exports.VibeTableDelegate = exports.VibeClient = exports.createVibeClient = exports.vibe = void 0;
|
|
18
18
|
// Client exports
|
|
19
19
|
var client_1 = require("./client");
|
|
20
20
|
Object.defineProperty(exports, "vibe", { enumerable: true, get: function () { return client_1.vibe; } });
|
|
@@ -65,3 +65,8 @@ Object.defineProperty(exports, "unwrapVibeDocument", { enumerable: true, get: fu
|
|
|
65
65
|
Object.defineProperty(exports, "extractVibeDocuments", { enumerable: true, get: function () { return generic_1.extractVibeDocuments; } });
|
|
66
66
|
Object.defineProperty(exports, "GenericCollection", { enumerable: true, get: function () { return generic_1.GenericCollection; } });
|
|
67
67
|
Object.defineProperty(exports, "GenericTableDelegate", { enumerable: true, get: function () { return generic_1.GenericTableDelegate; } });
|
|
68
|
+
// Enterprise authentication exports
|
|
69
|
+
var enterprise_auth_1 = require("./enterprise-auth");
|
|
70
|
+
Object.defineProperty(exports, "validateEnterpriseAuth", { enumerable: true, get: function () { return enterprise_auth_1.validateEnterpriseAuth; } });
|
|
71
|
+
Object.defineProperty(exports, "hasEnterpriseAuthHeaders", { enumerable: true, get: function () { return enterprise_auth_1.hasEnterpriseAuthHeaders; } });
|
|
72
|
+
Object.defineProperty(exports, "generateBackendHmacSignature", { enumerable: true, get: function () { return enterprise_auth_1.generateBackendHmacSignature; } });
|
package/package.json
CHANGED
|
@@ -177,7 +177,8 @@ export async function getIDPClientConfig(forceRefresh: boolean = false): Promise
|
|
|
177
177
|
|
|
178
178
|
// Set IDENTITY_CLIENT_BASE_EXTERNAL_URL from cached config
|
|
179
179
|
// AUTH_TRUST_HOST=true tells NextAuth to derive OAuth callback URLs from headers.
|
|
180
|
-
if (
|
|
180
|
+
// Only set if not already defined (allows deployment override for beta/staging)
|
|
181
|
+
if (redisConfig.baseClientUrl && !process.env.IDENTITY_CLIENT_BASE_EXTERNAL_URL) {
|
|
181
182
|
process.env.IDENTITY_CLIENT_BASE_EXTERNAL_URL = redisConfig.baseClientUrl;
|
|
182
183
|
}
|
|
183
184
|
|
|
@@ -215,7 +216,8 @@ export async function getIDPClientConfig(forceRefresh: boolean = false): Promise
|
|
|
215
216
|
|
|
216
217
|
// Set IDENTITY_CLIENT_BASE_EXTERNAL_URL from config
|
|
217
218
|
// AUTH_TRUST_HOST=true tells NextAuth to derive OAuth callback URLs from headers.
|
|
218
|
-
if (
|
|
219
|
+
// Only set if not already defined (allows deployment override for beta/staging)
|
|
220
|
+
if (config.baseClientUrl && !process.env.IDENTITY_CLIENT_BASE_EXTERNAL_URL) {
|
|
219
221
|
process.env.IDENTITY_CLIENT_BASE_EXTERNAL_URL = config.baseClientUrl;
|
|
220
222
|
console.log("[IDP_CONFIG] Set IDENTITY_CLIENT_BASE_EXTERNAL_URL:", config.baseClientUrl);
|
|
221
223
|
}
|
|
@@ -0,0 +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
|
+
}
|
package/src/vibe/index.ts
CHANGED
|
@@ -119,3 +119,14 @@ export {
|
|
|
119
119
|
GenericTableDelegate,
|
|
120
120
|
} from './generic';
|
|
121
121
|
export type { VibeDocumentWrapper } from './generic';
|
|
122
|
+
|
|
123
|
+
// Enterprise authentication exports
|
|
124
|
+
export {
|
|
125
|
+
validateEnterpriseAuth,
|
|
126
|
+
hasEnterpriseAuthHeaders,
|
|
127
|
+
generateBackendHmacSignature,
|
|
128
|
+
} from './enterprise-auth';
|
|
129
|
+
export type {
|
|
130
|
+
EnterpriseClientsConfig,
|
|
131
|
+
EnterpriseAuthResult,
|
|
132
|
+
} from './enterprise-auth';
|