msiVmTokenCredentials.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // Licensed under the MIT License. See License.txt in the project root for license information.
  3. import { MSITokenCredentials, MSIOptions, MSITokenResponse } from "./msiTokenCredentials";
  4. import { RequestPrepareOptions, HttpOperationResponse, WebResource, URLBuilder, HttpMethods } from "@azure/ms-rest-js";
  5. /**
  6. * @interface MSIVmOptions Defines the optional parameters for authentication with MSI for Virtual Machine.
  7. */
  8. export interface MSIVmOptions extends MSIOptions {
  9. /**
  10. * @property {string} [msiEndpoint] - Azure Instance Metadata Service identity endpoint.
  11. *
  12. * The default and recommended endpoint is "http://169.254.169.254/metadata/identity/oauth2/token"
  13. * per https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
  14. */
  15. msiEndpoint?: string;
  16. /**
  17. * The API version parameter specifies the Azure Instance Metadata Service version.
  18. * Use api-version=2018-02-01 (default) or higher.
  19. */
  20. apiVersion?: string;
  21. /**
  22. * HTTP method used to make HTTP request to MSI service. GET by default.
  23. */
  24. httpMethod?: HttpMethods;
  25. /**
  26. * The objectId of the managed identity you would like the token for. Required, if your
  27. * VM has multiple user-assigned managed identities.
  28. */
  29. objectId?: string;
  30. /**
  31. * The clientId of the managed identity you would like the token for. Required, if your
  32. * VM has multiple user-assigned managed identities.
  33. */
  34. clientId?: string;
  35. /**
  36. * The `Azure Resource ID` of the managed identity you would like the token for. Required,
  37. * if your VM has multiple user-assigned managed identities.
  38. */
  39. identityId?: string;
  40. }
  41. /**
  42. * @class MSIVmTokenCredentials
  43. */
  44. export class MSIVmTokenCredentials extends MSITokenCredentials {
  45. msiEndpoint: string;
  46. apiVersion: string;
  47. httpMethod: HttpMethods;
  48. objectId?: string;
  49. clientId?: string;
  50. identityId?: string;
  51. constructor(options?: MSIVmOptions) {
  52. if (!options) options = {};
  53. super(options);
  54. if (!options.msiEndpoint) {
  55. options.msiEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token";
  56. } else if (typeof options.msiEndpoint !== "string") {
  57. throw new Error("msiEndpoint must be a string.");
  58. }
  59. const urlBuilder = URLBuilder.parse(options.msiEndpoint);
  60. if (!urlBuilder.getScheme()) {
  61. options.msiEndpoint = `http://${options.msiEndpoint}`;
  62. }
  63. if (!options.apiVersion) {
  64. options.apiVersion = "2018-02-01";
  65. } else if (typeof options.apiVersion !== "string") {
  66. throw new Error("apiVersion must be a string.");
  67. }
  68. if (!options.httpMethod) {
  69. options.httpMethod = "GET";
  70. }
  71. this.apiVersion = options.apiVersion;
  72. this.msiEndpoint = options.msiEndpoint;
  73. this.httpMethod = options.httpMethod;
  74. this.objectId = options.objectId;
  75. this.clientId = options.clientId;
  76. this.identityId = options.identityId;
  77. }
  78. /**
  79. * Prepares and sends a POST request to a service endpoint hosted on the Azure VM, which responds with the access token.
  80. * @return {Promise<MSITokenResponse>} Promise with the tokenResponse (tokenType and accessToken are the two important properties).
  81. */
  82. async getToken(): Promise<MSITokenResponse> {
  83. const reqOptions = this.prepareRequestOptions();
  84. let opRes: HttpOperationResponse;
  85. let result: MSITokenResponse;
  86. opRes = await this._httpClient.sendRequest(reqOptions);
  87. result = this.parseTokenResponse(opRes.bodyAsText!) as MSITokenResponse;
  88. if (!result.tokenType) {
  89. throw new Error(`Invalid token response, did not find tokenType. Response body is: ${opRes.bodyAsText}`);
  90. } else if (!result.accessToken) {
  91. throw new Error(`Invalid token response, did not find accessToken. Response body is: ${opRes.bodyAsText}`);
  92. }
  93. return result;
  94. }
  95. protected prepareRequestOptions(): WebResource {
  96. const reqOptions: RequestPrepareOptions = {
  97. url: this.msiEndpoint,
  98. headers: {
  99. "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
  100. "Metadata": "true"
  101. },
  102. method: this.httpMethod,
  103. queryParameters: {
  104. "api-version": this.apiVersion,
  105. "resource": this.resource,
  106. "object_id": this.objectId,
  107. "client_id": this.clientId,
  108. "mi_res_id": this.identityId
  109. }
  110. };
  111. const webResource = new WebResource();
  112. return webResource.prepare(reqOptions);
  113. }
  114. }