Refactor Google authentication functions for Vertex AI - Exported `getVertexAIAuth`, `generateJWTToken`, and `getAccessToken` functions from google.js for better modularity. - Removed redundant JWT token generation and access token retrieval logic from chat-completions.js, utilizing the newly exported functions instead. - Improved code organization and maintainability by centralizing authentication logic.

1c048a6c799a37090b2a3e6bb024f8ebc1b25d68

InterestingDarkness <computeridiot999@gmail.com>

2 files changed, +4 -116Ignore whitespace
src/endpoints/backends/chat-completions.js+1 -113
@@ -1,6 +1,5 @@
1import process from 'node:process';1import process from 'node:process';
2import util from 'node:util';2import util from 'node:util';
3import crypto from 'node:crypto';
4import express from 'express';3import express from 'express';
5import fetch from 'node-fetch';4import fetch from 'node-fetch';
65
@@ -44,6 +43,7 @@ import {
44 webTokenizers,43 webTokenizers,
45 getWebTokenizer,44 getWebTokenizer,
46} from '../tokenizers.js';45} from '../tokenizers.js';
46import { getVertexAIAuth } from '../google.js';
4747
48const API_OPENAI = 'https://api.openai.com/v1';48const API_OPENAI = 'https://api.openai.com/v1';
49const API_CLAUDE = 'https://api.anthropic.com/v1';49const API_CLAUDE = 'https://api.anthropic.com/v1';
@@ -61,123 +61,11 @@ const API_DEEPSEEK = 'https://api.deepseek.com/beta';
61const API_XAI = 'https://api.x.ai/v1';61const API_XAI = 'https://api.x.ai/v1';
62const API_POLLINATIONS = 'https://text.pollinations.ai/openai';62const API_POLLINATIONS = 'https://text.pollinations.ai/openai';
6363
64/**
65 * Generates a JWT token for Google Cloud authentication using service account credentials.
66 * @param {object} serviceAccount Service account JSON object
67 * @returns {Promise<string>} JWT token
68 */
69async function generateJWTToken(serviceAccount) {
70 const now = Math.floor(Date.now() / 1000);
71 const expiry = now + 3600; // 1 hour
72
73 const header = {
74 alg: 'RS256',
75 typ: 'JWT',
76 };
77
78 const payload = {
79 iss: serviceAccount.client_email,
80 scope: 'https://www.googleapis.com/auth/cloud-platform',
81 aud: 'https://oauth2.googleapis.com/token',
82 iat: now,
83 exp: expiry,
84 };
85
86 const headerBase64 = Buffer.from(JSON.stringify(header)).toString('base64url');
87 const payloadBase64 = Buffer.from(JSON.stringify(payload)).toString('base64url');
88 const signatureInput = `${headerBase64}.${payloadBase64}`;
89
90 // Create signature using private key
91 const sign = crypto.createSign('RSA-SHA256');
92 sign.update(signatureInput);
93 const signature = sign.sign(serviceAccount.private_key, 'base64url');
94
95 return `${signatureInput}.${signature}`;
96}
97
98/**
99 * Gets an access token from Google OAuth2 using JWT assertion.
100 * @param {string} jwtToken JWT token
101 * @returns {Promise<string>} Access token
102 */
103async function getAccessToken(jwtToken) {
104 const response = await fetch('https://oauth2.googleapis.com/token', {
105 method: 'POST',
106 headers: {
107 'Content-Type': 'application/x-www-form-urlencoded',
108 },
109 body: new URLSearchParams({
110 grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
111 assertion: jwtToken,
112 }),
113 });
114
115 if (!response.ok) {
116 throw new Error(`Failed to get access token: ${response.status} ${response.statusText}`);
117 }
118
119 const data = await response.json();
120 return data.access_token;
121}
122
123/**
124 * Gets authentication for Vertex AI - either API key or service account token.
125 * @param {object} request Express request
126 * @returns {Promise<{authHeader: string, authType: string}>} Authentication header and type
127 */
128async function getVertexAIAuth(request) {
129 // Get the authentication mode from frontend
130 const authMode = request.body.vertexai_auth_mode || 'express';
131
132 // Check if using reverse proxy
133 if (request.body.reverse_proxy) {
134 return {
135 authHeader: `Bearer ${request.body.proxy_password}`,
136 authType: 'proxy',
137 };
138 }
139
140 if (authMode === 'express') {
141 // Express mode: use API key
142 const apiKey = readSecret(request.user.directories, SECRET_KEYS.VERTEXAI);
143 if (apiKey) {
144 return {
145 authHeader: `Bearer ${apiKey}`,
146 authType: 'express',
147 };
148 }
149 throw new Error('API key is required for Vertex AI Express mode');
150 } else if (authMode === 'full') {
151 // Full mode: use service account JSON
152 // First try to read from backend secret storage
153 let serviceAccountJson = readSecret(request.user.directories, SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT);
15464
155 // If not found in secrets, fall back to request body (for backward compatibility)
156 if (!serviceAccountJson) {
157 serviceAccountJson = request.body.vertexai_service_account_json;
158 }
15965
160 if (serviceAccountJson) {
161 try {
162 const serviceAccount = JSON.parse(serviceAccountJson);
16366
164 const jwtToken = await generateJWTToken(serviceAccount);
165 const accessToken = await getAccessToken(jwtToken);
16667
167 return {
168 authHeader: `Bearer ${accessToken}`,
169 authType: 'full',
170 };
171 } catch (error) {
172 console.error('Failed to authenticate with service account:', error);
173 throw new Error(`Service account authentication failed: ${error.message}`);
174 }
175 }
176 throw new Error('Service Account JSON is required for Vertex AI Full mode');
177 }
17868
179 throw new Error(`Unsupported Vertex AI authentication mode: ${authMode}`);
180}
18169
182/**70/**
183 * Applies a post-processing step to the generated messages.71 * Applies a post-processing step to the generated messages.
src/endpoints/google.js+3 -3
@@ -11,7 +11,7 @@ const API_MAKERSUITE = 'https://generativelanguage.googleapis.com';
11const API_VERTEX_AI = 'https://us-central1-aiplatform.googleapis.com';11const API_VERTEX_AI = 'https://us-central1-aiplatform.googleapis.com';
1212
13// Vertex AI authentication helper functions13// Vertex AI authentication helper functions
14async function getVertexAIAuth(request) {14export async function getVertexAIAuth(request) {
15 const authMode = request.body.vertexai_auth_mode || 'express';15 const authMode = request.body.vertexai_auth_mode || 'express';
1616
17 if (request.body.reverse_proxy) {17 if (request.body.reverse_proxy) {
@@ -64,7 +64,7 @@ async function getVertexAIAuth(request) {
64 * @param {object} serviceAccount Service account JSON object64 * @param {object} serviceAccount Service account JSON object
65 * @returns {Promise<string>} JWT token65 * @returns {Promise<string>} JWT token
66 */66 */
67async function generateJWTToken(serviceAccount) {67export async function generateJWTToken(serviceAccount) {
68 const now = Math.floor(Date.now() / 1000);68 const now = Math.floor(Date.now() / 1000);
69 const expiry = now + 3600; // 1 hour69 const expiry = now + 3600; // 1 hour
7070
@@ -93,7 +93,7 @@ async function generateJWTToken(serviceAccount) {
93 return `${signatureInput}.${signature}`;93 return `${signatureInput}.${signature}`;
94}94}
9595
96async function getAccessToken(jwtToken) {96export async function getAccessToken(jwtToken) {
97 const response = await fetch('https://oauth2.googleapis.com/token', {97 const response = await fetch('https://oauth2.googleapis.com/token', {
98 method: 'POST',98 method: 'POST',
99 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },99 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },