Skip to content

Commit f8fb993

Browse files
pontemontiJohan BrobergCopilotCopilot
authored
Added auth token validation (#42)
* Added auth token validation * Refactor validateAuthToken from standalone function to private static method (#43) * Initial plan * Refactor validateAuthToken to be a private static method Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> * Removed validation of scope - that's going to be difficult to implement correctly. * Update packages/agents-a365-tooling/src/Utility.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Johan Broberg <johanb@microsoft.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: pontemonti <7850950+pontemonti@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 578efb7 commit f8fb993

5 files changed

Lines changed: 67 additions & 0 deletions

File tree

packages/agents-a365-tooling-extensions-claude/src/McpToolRegistrationService.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ export class McpToolRegistrationService {
3838
authToken = await AgenticAuthenticationService.GetAgenticUserToken(authorization, turnContext);
3939
}
4040

41+
// Validate the authentication token
42+
Utility.ValidateAuthToken(authToken);
43+
4144
const servers = await this.configService.listToolServers(agentUserId, environmentId, authToken);
4245
const mcpServers: Record<string, McpServerConfig> = {};
4346
const tools: McpClientTool[] = [];

packages/agents-a365-tooling-extensions-langchain/src/McpToolRegistrationService.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ export class McpToolRegistrationService {
4040
authToken = await AgenticAuthenticationService.GetAgenticUserToken(authorization, turnContext);
4141
}
4242

43+
// Validate the authentication token
44+
Utility.ValidateAuthToken(authToken);
45+
4346
const servers = await this.configService.listToolServers(agentUserId, environmentId, authToken);
4447
const mcpServers: Record<string, Connection> = {};
4548

packages/agents-a365-tooling-extensions-openai/src/McpToolRegistrationService.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export class McpToolRegistrationService {
3939
authToken = await AgenticAuthenticationService.GetAgenticUserToken(authorization, turnContext);
4040
}
4141

42+
// Validate the authentication token
43+
Utility.ValidateAuthToken(authToken);
44+
4245
const servers = await this.configService.listToolServers(agentUserId, environmentId, authToken);
4346
const mcpServers: MCPServerStreamableHttp[] = [];
4447

packages/agents-a365-tooling/src/McpToolServerConfigurationService.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ export class McpToolServerConfigurationService {
7979
* @throws Error when the gateway call fails or returns an unexpected payload.
8080
*/
8181
private async getMCPServerConfigsFromToolingGateway(agentUserId: string, environmentId: string, authToken: string): Promise<MCPServerConfig[]> {
82+
// Validate the authentication token
83+
Utility.ValidateAuthToken(authToken);
84+
8285
const configEndpoint = Utility.GetToolingGatewayForDigitalWorker(agentUserId);
8386

8487
try {

packages/agents-a365-tooling/src/Utility.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,61 @@ export enum ToolsMode {
1010
const MCP_PLATFORM_PROD_BASE_URL = 'https://agent365.svc.cloud.microsoft';
1111

1212
export class Utility {
13+
/**
14+
* Validates a JWT authentication token.
15+
* Checks that the token is a valid JWT and is not expired.
16+
*
17+
* @param authToken - The JWT token to validate.
18+
* @throws Error if the token is invalid or expired.
19+
*/
20+
public static ValidateAuthToken(authToken: string | undefined): void {
21+
return Utility.validateAuthToken(authToken);
22+
}
23+
24+
/**
25+
* Private helper to validate a JWT authentication token.
26+
* Checks that the token is a valid JWT and is not expired.
27+
*
28+
* @param authToken - The JWT token to validate.
29+
* @throws Error if the token is invalid or expired.
30+
*/
31+
private static validateAuthToken(authToken: string | undefined): void {
32+
if (!authToken) {
33+
throw new Error('Authentication token is required');
34+
}
35+
36+
// Parse JWT token (format: header.payload.signature)
37+
const parts = authToken.split('.');
38+
if (parts.length !== 3) {
39+
throw new Error('Invalid JWT token format');
40+
}
41+
42+
let payload: {
43+
exp?: number;
44+
};
45+
46+
try {
47+
// Decode the payload (second part of the JWT)
48+
const payloadBase64 = parts[1];
49+
// Handle URL-safe base64
50+
const paddedBase64 = payloadBase64.padEnd(payloadBase64.length + (4 - payloadBase64.length % 4) % 4, '=');
51+
const payloadJson = Buffer.from(paddedBase64.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf-8');
52+
payload = JSON.parse(payloadJson);
53+
} catch (error) {
54+
throw new Error('Failed to decode JWT token payload');
55+
}
56+
57+
// Check expiration
58+
if (payload.exp) {
59+
const currentTimestamp = Math.floor(Date.now() / 1000);
60+
if (payload.exp < currentTimestamp) {
61+
throw new Error('Authentication token has expired');
62+
}
63+
} else {
64+
throw new Error('Authentication token does not contain expiration claim');
65+
}
66+
}
67+
1368
/**
1469
* Construct the tooling gateway URL for a given digital worker (agent user).
1570
* This endpoint is used to discover MCP servers associated with the specified agent user.

0 commit comments

Comments
 (0)