{"version":3,"file":"imdsMsi.js","sourceRoot":"","sources":["../../../../src/credentials/managedIdentityCredential/imdsMsi.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { delay } from \"@azure/core-util\";\nimport { AccessToken, GetTokenOptions } from \"@azure/core-auth\";\nimport {\n PipelineRequestOptions,\n RestError,\n createHttpHeaders,\n createPipelineRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { TokenResponseParsedBody } from \"../../client/identityClient\";\nimport { credentialLogger } from \"../../util/logging\";\nimport { AuthenticationError } from \"../../errors\";\nimport { tracingClient } from \"../../util/tracing\";\nimport { imdsApiVersion, imdsEndpointPath, imdsHost } from \"./constants\";\nimport { MSI, MSIConfiguration } from \"./models\";\nimport { mapScopesToResource } from \"./utils\";\n\nconst msiName = \"ManagedIdentityCredential - IMDS\";\nconst logger = credentialLogger(msiName);\n\n/**\n * Formats the expiration date of the received token into the number of milliseconds between that date and midnight, January 1, 1970.\n */\nfunction expiresOnParser(requestBody: TokenResponseParsedBody): number {\n if (requestBody.expires_on) {\n // Use the expires_on timestamp if it's available\n const expires = +requestBody.expires_on * 1000;\n logger.info(\n `${msiName}: Using expires_on: ${expires} (original value: ${requestBody.expires_on})`\n );\n return expires;\n } else {\n // If these aren't possible, use expires_in and calculate a timestamp\n const expires = Date.now() + requestBody.expires_in * 1000;\n logger.info(\n `${msiName}: IMDS using expires_in: ${expires} (original value: ${requestBody.expires_in})`\n );\n return expires;\n }\n}\n\n/**\n * Generates the options used on the request for an access token.\n */\nfunction prepareRequestOptions(\n scopes: string | string[],\n clientId?: string,\n resourceId?: string,\n options?: {\n skipQuery?: boolean;\n skipMetadataHeader?: boolean;\n }\n): PipelineRequestOptions {\n const resource = mapScopesToResource(scopes);\n if (!resource) {\n throw new Error(`${msiName}: Multiple scopes are not supported.`);\n }\n\n const { skipQuery, skipMetadataHeader } = options || {};\n let query = \"\";\n\n // Pod Identity will try to process this request even if the Metadata header is missing.\n // We can exclude the request query to ensure no IMDS endpoint tries to process the ping request.\n if (!skipQuery) {\n const queryParameters: Record = {\n resource,\n \"api-version\": imdsApiVersion,\n };\n if (clientId) {\n queryParameters.client_id = clientId;\n }\n if (resourceId) {\n queryParameters.msi_res_id = resourceId;\n }\n const params = new URLSearchParams(queryParameters);\n query = `?${params.toString()}`;\n }\n\n const url = new URL(imdsEndpointPath, process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST ?? imdsHost);\n\n const rawHeaders: Record = {\n Accept: \"application/json\",\n Metadata: \"true\",\n };\n\n // Remove the Metadata header to invoke a request error from some IMDS endpoints.\n if (skipMetadataHeader) {\n delete rawHeaders.Metadata;\n }\n\n return {\n // In this case, the `?` should be added in the \"query\" variable `skipQuery` is not set.\n url: `${url}${query}`,\n method: \"GET\",\n headers: createHttpHeaders(rawHeaders),\n };\n}\n\n// 800ms -> 1600ms -> 3200ms\nexport const imdsMsiRetryConfig = {\n maxRetries: 3,\n startDelayInMs: 800,\n intervalIncrement: 2,\n};\n\n/**\n * Defines how to determine whether the Azure IMDS MSI is available, and also how to retrieve a token from the Azure IMDS MSI.\n */\nexport const imdsMsi: MSI = {\n async isAvailable({\n scopes,\n identityClient,\n clientId,\n resourceId,\n getTokenOptions = {},\n }): Promise {\n const resource = mapScopesToResource(scopes);\n if (!resource) {\n logger.info(`${msiName}: Unavailable. Multiple scopes are not supported.`);\n return false;\n }\n\n // if the PodIdentityEndpoint environment variable was set no need to probe the endpoint, it can be assumed to exist\n if (process.env.AZURE_POD_IDENTITY_AUTHORITY_HOST) {\n return true;\n }\n\n if (!identityClient) {\n throw new Error(\"Missing IdentityClient\");\n }\n\n const requestOptions = prepareRequestOptions(resource, clientId, resourceId, {\n skipMetadataHeader: true,\n skipQuery: true,\n });\n\n return tracingClient.withSpan(\n \"ManagedIdentityCredential-pingImdsEndpoint\",\n getTokenOptions,\n async (options) => {\n requestOptions.tracingOptions = options.tracingOptions;\n try {\n // Create a request with a timeout since we expect that\n // not having a \"Metadata\" header should cause an error to be\n // returned quickly from the endpoint, proving its availability.\n const request = createPipelineRequest(requestOptions);\n\n request.timeout = options.requestOptions?.timeout ?? 300;\n\n // This MSI uses the imdsEndpoint to get the token, which only uses http://\n request.allowInsecureConnection = true;\n\n try {\n logger.info(`${msiName}: Pinging the Azure IMDS endpoint`);\n await identityClient.sendRequest(request);\n } catch (err: any) {\n if (\n (err.name === \"RestError\" && err.code === RestError.REQUEST_SEND_ERROR) ||\n err.name === \"AbortError\" ||\n err.code === \"ENETUNREACH\" || // Network unreachable\n err.code === \"ECONNREFUSED\" || // connection refused\n err.code === \"EHOSTDOWN\" // host is down\n ) {\n // If the request failed, or Node.js was unable to establish a connection,\n // or the host was down, we'll assume the IMDS endpoint isn't available.\n logger.info(`${msiName}: The Azure IMDS endpoint is unavailable`);\n return false;\n }\n }\n\n // If we received any response, the endpoint is available\n logger.info(`${msiName}: The Azure IMDS endpoint is available`);\n return true;\n } catch (err: any) {\n // createWebResource failed.\n // This error should bubble up to the user.\n logger.info(\n `${msiName}: Error when creating the WebResource for the Azure IMDS endpoint: ${err.message}`\n );\n throw err;\n }\n }\n );\n },\n async getToken(\n configuration: MSIConfiguration,\n getTokenOptions: GetTokenOptions = {}\n ): Promise {\n const { identityClient, scopes, clientId, resourceId } = configuration;\n\n logger.info(\n `${msiName}: Using the Azure IMDS endpoint coming from the environment variable MSI_ENDPOINT=${process.env.MSI_ENDPOINT}, and using the cloud shell to proceed with the authentication.`\n );\n\n let nextDelayInMs = imdsMsiRetryConfig.startDelayInMs;\n for (let retries = 0; retries < imdsMsiRetryConfig.maxRetries; retries++) {\n try {\n const request = createPipelineRequest({\n abortSignal: getTokenOptions.abortSignal,\n ...prepareRequestOptions(scopes, clientId, resourceId),\n allowInsecureConnection: true,\n });\n const tokenResponse = await identityClient.sendTokenRequest(request, expiresOnParser);\n return (tokenResponse && tokenResponse.accessToken) || null;\n } catch (error: any) {\n if (error.statusCode === 404) {\n await delay(nextDelayInMs);\n nextDelayInMs *= imdsMsiRetryConfig.intervalIncrement;\n continue;\n }\n throw error;\n }\n }\n\n throw new AuthenticationError(\n 404,\n `${msiName}: Failed to retrieve IMDS token after ${imdsMsiRetryConfig.maxRetries} retries.`\n );\n },\n};\n"]}