Skip to content

Commit 47fc2e4

Browse files
committed
feat: automate token fetching in the backend
1 parent 701547f commit 47fc2e4

File tree

4 files changed

+97
-2
lines changed

4 files changed

+97
-2
lines changed

backend/companion/TokenManager.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { getKcpToken } from './getKcpToken';
2+
3+
export class TokenManager {
4+
constructor() {
5+
this.currentToken = null;
6+
this.tokenExpirationTime = null;
7+
// Add buffer time (e.g., 5 minutes) before actual expiration to prevent edge cases
8+
this.expirationBuffer = 5 * 60 * 1000;
9+
}
10+
11+
async getToken() {
12+
// Check if token exists and is not near expiration
13+
if (
14+
this.currentToken &&
15+
this.tokenExpirationTime &&
16+
Date.now() < this.tokenExpirationTime - this.expirationBuffer
17+
) {
18+
return this.currentToken;
19+
}
20+
21+
// If token doesn't exist or is expired/near expiration, fetch new token
22+
try {
23+
const newToken = await getKcpToken();
24+
this.currentToken = newToken;
25+
// Set expiration time based on JWT expiry
26+
// You'll need to decode the JWT to get the actual expiration
27+
this.tokenExpirationTime = this.getExpirationFromJWT(newToken);
28+
return newToken;
29+
} catch (error) {
30+
console.error('Failed to refresh token:', error);
31+
throw error;
32+
}
33+
}
34+
35+
getExpirationFromJWT(token) {
36+
try {
37+
// Split the token and get the payload
38+
const payload = JSON.parse(
39+
Buffer.from(token.split('.')[1], 'base64').toString(),
40+
);
41+
// exp is in seconds, convert to milliseconds
42+
return payload.exp * 1000;
43+
} catch (error) {
44+
console.error('Error parsing JWT:', error);
45+
// If we can't parse the expiration, set a default (e.g., 1 hour from now)
46+
return Date.now() + 60 * 60 * 1000;
47+
}
48+
}
49+
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import express from 'express';
2+
import { TokenManager } from './TokenManager';
3+
4+
const tokenManager = new TokenManager();
5+
26
const router = express.Router();
37

48
router.use(express.json());
@@ -24,7 +28,7 @@ async function handleAIChatRequest(req, res) {
2428
namespace: namespace,
2529
};
2630

27-
const AUTH_TOKEN = '<AUTH_TOKEN>';
31+
const AUTH_TOKEN = await tokenManager.getToken();
2832

2933
const response = await fetch(url, {
3034
method: 'POST',

backend/companion/getKcpToken.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
export async function getKcpToken() {
2+
const tokenUrl = 'https://kymatest.accounts400.ondemand.com/oauth2/token';
3+
const grantType = 'client_credentials';
4+
const clientId = process.env.COMPANION_KCP_AUTH_CLIENT_ID;
5+
const clientSecret = process.env.COMPANION_KCP_AUTH_CLIENT_SECRET;
6+
7+
if (!clientId) {
8+
throw new Error('COMPANION_KCP_AUTH_CLIENT_ID is not set');
9+
}
10+
if (!clientSecret) {
11+
throw new Error('COMPANION_KCP_AUTH_CLIENT_SECRET is not set');
12+
}
13+
14+
// Prepare request data
15+
const requestBody = new URLSearchParams();
16+
requestBody.append('grant_type', grantType);
17+
18+
// Prepare authorization header
19+
const authHeader = Buffer.from(`${clientId}:${clientSecret}`).toString(
20+
'base64',
21+
);
22+
23+
try {
24+
const response = await fetch(tokenUrl, {
25+
method: 'POST',
26+
headers: {
27+
Authorization: `Basic ${authHeader}`,
28+
'Content-Type': 'application/x-www-form-urlencoded',
29+
},
30+
body: requestBody.toString(),
31+
});
32+
33+
if (!response.ok) {
34+
throw new Error(`HTTP error! status: ${response.status}`);
35+
}
36+
37+
const data = await response.json();
38+
return data.access_token;
39+
} catch (error) {
40+
throw new Error(`Failed to fetch token: ${error.message}`);
41+
}
42+
}

backend/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { makeHandleRequest, serveStaticApp, serveMonaco } from './common';
22
import { handleTracking } from './tracking.js';
33
import jsyaml from 'js-yaml';
4-
import companionRouter from './routes/companion';
4+
import companionRouter from './companion/companionRouter';
55
//import { requestLogger } from './utils/other'; //uncomment this to log the outgoing traffic
66

77
const express = require('express');

0 commit comments

Comments
 (0)