@aashari/mcp-server-aws-sso
Version:
Node.js/TypeScript MCP server for AWS Single Sign-On (SSO). Enables AI systems (LLMs) with tools to initiate SSO login (device auth flow), list accounts/roles, and securely execute AWS CLI commands using temporary credentials. Streamlines AI interaction w
126 lines (125 loc) • 5 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.TokenResponseSchema = exports.DeviceAuthorizationResponseSchema = exports.ClientRegistrationResponseSchema = void 0;
exports.post = post;
const logger_util_js_1 = require("../utils/logger.util.js");
const retry_util_js_1 = require("../utils/retry.util.js");
const zod_1 = require("zod");
const logger = logger_util_js_1.Logger.forContext('services/vendor.aws.sso.auth.http.ts');
/**
* Make a POST request to a URL with JSON body
* @param url The URL to post to
* @param data The data to send in the request body
* @returns The JSON response
*/
async function post(url, data) {
const methodLogger = logger.forMethod('post');
methodLogger.debug(`Making POST request to ${url}`);
// Use the retry mechanism for handling potential 429 errors
const response = await (0, retry_util_js_1.withRetry)(async () => {
const fetchResponse = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!fetchResponse.ok) {
let errorBody = await fetchResponse.text();
// Try to parse the error response as JSON
try {
errorBody = JSON.parse(errorBody);
methodLogger.debug('Received error response from API', {
status: fetchResponse.status,
errorBody,
});
}
catch {
// If parsing fails, keep the text version
methodLogger.debug('Received non-JSON error response', {
status: fetchResponse.status,
errorBody,
});
}
const error = new Error(typeof errorBody === 'object' && errorBody.error_description
? String(errorBody.error_description)
: `Request failed with status ${fetchResponse.status}`);
error.$metadata = { httpStatusCode: fetchResponse.status };
// Add OIDC-specific error details if available
if (typeof errorBody === 'object') {
if (errorBody.error) {
error.error = String(errorBody.error);
}
if (errorBody.error_description) {
error.error_description = String(errorBody.error_description);
}
}
// Store the original response for more context
error.originalResponse = errorBody;
// Special handling for authorization_pending - this is expected during polling
// and should not be logged as an error
if (typeof errorBody === 'object' &&
errorBody.error === 'authorization_pending') {
methodLogger.debug('Received authorization_pending response');
}
else {
methodLogger.error(`API request failed: ${error.message}`, error);
}
throw error;
}
return fetchResponse;
}, {
// Define custom retry condition for fetch responses
retryCondition: (error) => {
// Default retry on 429 responses
if (error && typeof error === 'object') {
if ('$metadata' in error &&
typeof error.$metadata === 'object') {
const metadata = error.$metadata;
return metadata.httpStatusCode === 429;
}
// Also retry on slow_down OIDC errors
if ('error' in error && error.error === 'slow_down') {
return true;
}
}
return false;
},
// Increase backoff for OIDC slow_down errors
initialDelayMs: 2000,
maxRetries: 8,
});
return (await response.json());
}
/**
* Zod schema for client registration response
*/
exports.ClientRegistrationResponseSchema = zod_1.z.object({
clientId: zod_1.z.string(),
clientSecret: zod_1.z.string(),
expiresAt: zod_1.z.string().optional(),
});
/**
* Zod schema for device authorization response
*/
exports.DeviceAuthorizationResponseSchema = zod_1.z.object({
deviceCode: zod_1.z.string(),
userCode: zod_1.z.string(),
verificationUri: zod_1.z.string(),
verificationUriComplete: zod_1.z.string(),
expiresIn: zod_1.z.number(),
interval: zod_1.z.number(),
});
/**
* Zod schema for token response
*/
exports.TokenResponseSchema = zod_1.z.object({
accessToken: zod_1.z.string().optional(),
access_token: zod_1.z.string().optional(),
refreshToken: zod_1.z.string().optional().nullable(),
refresh_token: zod_1.z.string().optional().nullable(),
tokenType: zod_1.z.string().optional(),
token_type: zod_1.z.string().optional(),
expires_in: zod_1.z.number().optional(),
expiresIn: zod_1.z.number().optional(),
});
;