login.ts 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
  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 * as adal from "adal-node";
  4. import * as msRest from "@azure/ms-rest-js";
  5. import { exec } from "child_process";
  6. import { readFileSync } from "fs";
  7. import { Environment } from "@azure/ms-rest-azure-env";
  8. import { TokenCredentialsBase } from "./credentials/tokenCredentialsBase";
  9. import { ApplicationTokenCredentials } from "./credentials/applicationTokenCredentials";
  10. import { ApplicationTokenCertificateCredentials } from "./credentials/applicationTokenCertificateCredentials";
  11. import { DeviceTokenCredentials } from "./credentials/deviceTokenCredentials";
  12. import { UserTokenCredentials } from "./credentials/userTokenCredentials";
  13. import { AuthConstants, TokenAudience } from "./util/authConstants";
  14. import { buildTenantList, getSubscriptionsFromTenants, LinkedSubscription } from "./subscriptionManagement/subscriptionUtils";
  15. import { MSIVmTokenCredentials, MSIVmOptions } from "./credentials/msiVmTokenCredentials";
  16. import { MSIAppServiceTokenCredentials, MSIAppServiceOptions } from "./credentials/msiAppServiceTokenCredentials";
  17. import { MSITokenResponse } from "./credentials/msiTokenCredentials";
  18. /**
  19. * @constant {Array<string>} managementPlaneTokenAudiences - Urls for management plane token
  20. * audience across different azure environments.
  21. */
  22. const managementPlaneTokenAudiences = [
  23. "https://management.core.windows.net/",
  24. "https://management.core.chinacloudapi.cn/",
  25. "https://management.core.usgovcloudapi.net/",
  26. "https://management.core.cloudapi.de/",
  27. "https://management.azure.com/",
  28. "https://management.core.windows.net",
  29. "https://management.core.chinacloudapi.cn",
  30. "https://management.core.usgovcloudapi.net",
  31. "https://management.core.cloudapi.de",
  32. "https://management.azure.com"
  33. ];
  34. function turnOnLogging() {
  35. const log = adal.Logging;
  36. log.setLoggingOptions(
  37. {
  38. level: 3, // Please use log.LOGGING_LEVEL.VERBOSE once AD TypeScript mappings are updated,
  39. log: function (level: any, message: any, error: any) {
  40. level;
  41. console.info(message);
  42. if (error) {
  43. console.error(error);
  44. }
  45. }
  46. });
  47. }
  48. if (process.env["AZURE_ADAL_LOGGING_ENABLED"]) {
  49. turnOnLogging();
  50. }
  51. /**
  52. * @interface AzureTokenCredentialsOptions - Describes optional parameters for serviceprincipal/secret authentication.
  53. */
  54. export interface AzureTokenCredentialsOptions {
  55. /**
  56. * @property {TokenAudience} [tokenAudience] - The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
  57. * If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferrably in a guid format).
  58. */
  59. tokenAudience?: TokenAudience;
  60. /**
  61. * @property {AzureEnvironment} [environment] - The Azure environment to authenticate with.
  62. */
  63. environment?: Environment;
  64. /**
  65. * @property {TokenCache} [tokenCache] - The token cache. Default value is MemoryCache from adal.
  66. */
  67. tokenCache?: adal.TokenCache;
  68. }
  69. /**
  70. * @interface LoginWithUsernamePasswordOptions - Describes optional parameters for username/password authentication.
  71. */
  72. export interface LoginWithUsernamePasswordOptions extends AzureTokenCredentialsOptions {
  73. /**
  74. * @property {string} [clientId] - The active directory application client id.
  75. * See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
  76. * for an example.
  77. */
  78. clientId?: string;
  79. /**
  80. * @property {string} [domain] - The domain or tenant id containing this application. Default value is "common".
  81. */
  82. domain?: string;
  83. }
  84. /**
  85. * @interface InteractiveLoginOptions - Describes optional parameters for interactive authentication.
  86. */
  87. export interface InteractiveLoginOptions extends LoginWithUsernamePasswordOptions {
  88. /**
  89. * @property {object|function} [userCodeResponseLogger] A logger that logs the user code response message required for interactive login. When
  90. * this option is specified the usercode response message will not be logged to console.
  91. */
  92. userCodeResponseLogger?: any;
  93. /**
  94. * @property {string} [language] The language code specifying how the message should be localized to. Default value "en-us".
  95. */
  96. language?: string;
  97. }
  98. /**
  99. * @interface AuthResponse - Describes the authentication response.
  100. */
  101. export interface AuthResponse {
  102. /**
  103. * @property {TokenCredentialsBase} credentials - The credentials object.
  104. */
  105. credentials: TokenCredentialsBase;
  106. /**
  107. * @property {Array<LinkedSubscription>} [subscriptions] List of associated subscriptions.
  108. */
  109. subscriptions?: LinkedSubscription[];
  110. }
  111. /**
  112. * @interface LoginWithAuthFileOptions - Describes optional parameters for login withAuthFile.
  113. */
  114. export interface LoginWithAuthFileOptions {
  115. /**
  116. * @property {string} [filePath] - Absolute file path to the auth file. If not provided
  117. * then please set the environment variable AZURE_AUTH_LOCATION.
  118. */
  119. filePath?: string;
  120. /**
  121. * @property {string} [subscriptionEnvVariableName] - The subscriptionId environment variable
  122. * name. Default is "AZURE_SUBSCRIPTION_ID".
  123. */
  124. subscriptionEnvVariableName?: string;
  125. }
  126. /**
  127. * Generic callback type definition.
  128. *
  129. * @property {Error} error - The error occurred if any, while executing the request; otherwise undefined
  130. * @property {TResult} result - Result when call was successful.
  131. */
  132. export type Callback<TResult> = (error?: Error, result?: TResult) => void;
  133. /**
  134. * Provides a UserTokenCredentials object and the list of subscriptions associated with that userId across all the applicable tenants.
  135. * This method is applicable only for organizational ids that are not 2FA enabled otherwise please use interactive login.
  136. *
  137. * @param {string} username The user name for the Organization Id account.
  138. * @param {string} password The password for the Organization Id account.
  139. * @param {object} [options] Object representing optional parameters.
  140. * @param {string} [options.clientId] The active directory application client id.
  141. * See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
  142. * for an example.
  143. * @param {string} [options.tokenAudience] The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
  144. * If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferrably in a guid format).
  145. * @param {string} [options.domain] The domain or tenant id containing this application. Default value "common".
  146. * @param {Environment} [options.environment] The azure environment to authenticate with.
  147. * @param {object} [options.tokenCache] The token cache. Default value is the MemoryCache object from adal.
  148. *
  149. * @returns {Promise<AuthResponse>} A Promise that resolves to AuthResponse that contains "credentials" and optional "subscriptions" array and rejects with an Error.
  150. */
  151. export async function withUsernamePasswordWithAuthResponse(username: string, password: string, options?: LoginWithUsernamePasswordOptions): Promise<AuthResponse> {
  152. if (!options) {
  153. options = {};
  154. }
  155. if (!options.clientId) {
  156. options.clientId = AuthConstants.DEFAULT_ADAL_CLIENT_ID;
  157. }
  158. if (!options.domain) {
  159. options.domain = AuthConstants.AAD_COMMON_TENANT;
  160. }
  161. if (!options.environment) {
  162. options.environment = Environment.AzureCloud;
  163. }
  164. let creds: UserTokenCredentials;
  165. let tenantList: string[] = [];
  166. let subscriptionList: LinkedSubscription[] = [];
  167. try {
  168. creds = new UserTokenCredentials(options.clientId, options.domain, username, password, options.tokenAudience, options.environment);
  169. await creds.getToken();
  170. // The token cache gets propulated for all the tenants as a part of building the tenantList.
  171. tenantList = await buildTenantList(creds);
  172. subscriptionList = await _getSubscriptions(creds, tenantList, options.tokenAudience);
  173. } catch (err) {
  174. return Promise.reject(err);
  175. }
  176. return Promise.resolve({ credentials: creds, subscriptions: subscriptionList });
  177. }
  178. /**
  179. * Provides an ApplicationTokenCredentials object and the list of subscriptions associated with that servicePrinicpalId/clientId across all the applicable tenants.
  180. *
  181. * @param {string} clientId The active directory application client id also known as the SPN (ServicePrincipal Name).
  182. * See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
  183. * for an example.
  184. * @param {string} secret The application secret for the service principal.
  185. * @param {string} domain The domain or tenant id containing this application.
  186. * @param {object} [options] Object representing optional parameters.
  187. * @param {string} [options.tokenAudience] The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
  188. * If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferrably in a guid format).
  189. * @param {Environment} [options.environment] The azure environment to authenticate with.
  190. * @param {object} [options.tokenCache] The token cache. Default value is the MemoryCache object from adal.
  191. *
  192. * @returns {Promise<AuthResponse>} A Promise that resolves to AuthResponse that contains "credentials" and optional "subscriptions" array and rejects with an Error.
  193. */
  194. export async function withServicePrincipalSecretWithAuthResponse(clientId: string, secret: string, domain: string, options?: AzureTokenCredentialsOptions): Promise<AuthResponse> {
  195. if (!options) {
  196. options = {};
  197. }
  198. if (!options.environment) {
  199. options.environment = Environment.AzureCloud;
  200. }
  201. let creds: ApplicationTokenCredentials;
  202. let subscriptionList: LinkedSubscription[] = [];
  203. try {
  204. creds = new ApplicationTokenCredentials(clientId, domain, secret, options.tokenAudience, options.environment);
  205. await creds.getToken();
  206. subscriptionList = await _getSubscriptions(creds, [domain], options.tokenAudience);
  207. } catch (err) {
  208. return Promise.reject(err);
  209. }
  210. return Promise.resolve({ credentials: creds, subscriptions: subscriptionList });
  211. }
  212. /**
  213. * Provides an ApplicationTokenCertificateCredentials object and the list of subscriptions associated with that servicePrinicpalId/clientId across all the applicable tenants.
  214. *
  215. * @param {string} clientId The active directory application client id also known as the SPN (ServicePrincipal Name).
  216. * See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
  217. * for an example.
  218. * @param {string} certificateStringOrFilePath A PEM encoded certificate and private key OR an absolute filepath to the .pem file containing that information. For example:
  219. * - CertificateString: "-----BEGIN PRIVATE KEY-----\n<xxxxx>\n-----END PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\n<yyyyy>\n-----END CERTIFICATE-----\n"
  220. * - CertificateFilePath: **Absolute** file path of the .pem file.
  221. * @param {string} domain The domain or tenant id containing this application.
  222. * @param {object} [options] Object representing optional parameters.
  223. * @param {string} [options.tokenAudience] The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
  224. * If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferrably in a guid format).
  225. * @param {Environment} [options.environment] The azure environment to authenticate with.
  226. * @param {object} [options.tokenCache] The token cache. Default value is the MemoryCache object from adal.
  227. *
  228. * @returns {Promise<AuthResponse>} A Promise that resolves to AuthResponse that contains "credentials" and optional "subscriptions" array and rejects with an Error.
  229. */
  230. export async function withServicePrincipalCertificateWithAuthResponse(clientId: string, certificateStringOrFilePath: string, domain: string, options?: AzureTokenCredentialsOptions): Promise<AuthResponse> {
  231. if (!options) {
  232. options = {};
  233. }
  234. if (!options.environment) {
  235. options.environment = Environment.AzureCloud;
  236. }
  237. let creds: ApplicationTokenCertificateCredentials;
  238. let subscriptionList: LinkedSubscription[] = [];
  239. try {
  240. creds = ApplicationTokenCertificateCredentials.create(clientId, certificateStringOrFilePath, domain, options);
  241. await creds.getToken();
  242. subscriptionList = await _getSubscriptions(creds, [domain], options.tokenAudience);
  243. } catch (err) {
  244. return Promise.reject(err);
  245. }
  246. return Promise.resolve({ credentials: creds, subscriptions: subscriptionList });
  247. }
  248. function validateAuthFileContent(credsObj: any, filePath: string) {
  249. if (!credsObj) {
  250. throw new Error("Please provide a credsObj to validate.");
  251. }
  252. if (!filePath) {
  253. throw new Error("Please provide a filePath.");
  254. }
  255. if (!credsObj.clientId) {
  256. throw new Error(`"clientId" is missing from the auth file: ${filePath}.`);
  257. }
  258. if (!credsObj.clientSecret && !credsObj.clientCertificate) {
  259. throw new Error(`Either "clientSecret" or "clientCertificate" must be present in the auth file: ${filePath}.`);
  260. }
  261. if (!credsObj.subscriptionId) {
  262. throw new Error(`"subscriptionId" is missing from the auth file: ${filePath}.`);
  263. }
  264. if (!credsObj.tenantId) {
  265. throw new Error(`"tenantId" is missing from the auth file: ${filePath}.`);
  266. }
  267. if (!credsObj.activeDirectoryEndpointUrl) {
  268. throw new Error(`"activeDirectoryEndpointUrl" is missing from the auth file: ${filePath}.`);
  269. }
  270. if (!credsObj.resourceManagerEndpointUrl) {
  271. throw new Error(`"resourceManagerEndpointUrl" is missing from the auth file: ${filePath}.`);
  272. }
  273. if (!credsObj.activeDirectoryGraphResourceId) {
  274. throw new Error(`"activeDirectoryGraphResourceId" is missing from the auth file: ${filePath}.`);
  275. }
  276. if (!credsObj.sqlManagementEndpointUrl) {
  277. throw new Error(`"sqlManagementEndpointUrl" is missing from the auth file: ${filePath}.`);
  278. }
  279. }
  280. function foundManagementEndpointUrl(authFileUrl: string, envUrl: string): boolean {
  281. if (!authFileUrl || (authFileUrl && typeof authFileUrl.valueOf() !== "string")) {
  282. throw new Error("authFileUrl cannot be null or undefined and must be of type string.");
  283. }
  284. if (!envUrl || (envUrl && typeof envUrl.valueOf() !== "string")) {
  285. throw new Error("envUrl cannot be null or undefined and must be of type string.");
  286. }
  287. authFileUrl = authFileUrl.endsWith("/") ? authFileUrl.slice(0, -1) : authFileUrl;
  288. envUrl = envUrl.endsWith("/") ? envUrl.slice(0, -1) : envUrl;
  289. return (authFileUrl.toLowerCase() === envUrl.toLowerCase());
  290. }
  291. /**
  292. * Before using this method please install az cli from https://github.com/Azure/azure-cli/releases. Then execute `az ad sp create-for-rbac --sdk-auth > ${yourFilename.json}`.
  293. * If you want to create the sp for a different cloud/environment then please execute:
  294. * 1. az cloud list
  295. * 2. az cloud set –n <name of the environment>
  296. * 3. az ad sp create-for-rbac --sdk-auth > auth.json // create sp with secret
  297. * **OR**
  298. * 3. az ad sp create-for-rbac --create-cert --sdk-auth > auth.json // create sp with certificate
  299. * If the service principal is already created then login with service principal info:
  300. * 4. az login --service-principal -u <clientId> -p <clientSecret> -t <tenantId>
  301. * 5. az account show --sdk-auth > auth.json
  302. *
  303. * Authenticates using the service principal information provided in the auth file. This method will set
  304. * the subscriptionId from the auth file to the user provided environment variable in the options
  305. * parameter or the default "AZURE_SUBSCRIPTION_ID".
  306. *
  307. * @param {object} [options] - Optional parameters
  308. * @param {string} [options.filePath] - Absolute file path to the auth file. If not provided
  309. * then please set the environment variable AZURE_AUTH_LOCATION.
  310. * @param {string} [options.subscriptionEnvVariableName] - The subscriptionId environment variable
  311. * name. Default is "AZURE_SUBSCRIPTION_ID".
  312. * @param {function} [optionalCallback] The optional callback.
  313. *
  314. * @returns {Promise<AuthResponse>} A Promise that resolves to AuthResponse that contains "credentials" and optional "subscriptions" array and rejects with an Error.
  315. */
  316. export async function withAuthFileWithAuthResponse(options?: LoginWithAuthFileOptions): Promise<AuthResponse> {
  317. if (!options) options = { filePath: "" };
  318. const filePath = options.filePath || process.env[AuthConstants.AZURE_AUTH_LOCATION];
  319. const subscriptionEnvVariableName = options.subscriptionEnvVariableName || "AZURE_SUBSCRIPTION_ID";
  320. if (!filePath) {
  321. const msg = `Either provide an absolute file path to the auth file or set/export the environment variable - ${AuthConstants.AZURE_AUTH_LOCATION}.`;
  322. return Promise.reject(new Error(msg));
  323. }
  324. let content: string, credsObj: any = {};
  325. const optionsForSp: any = {};
  326. try {
  327. content = readFileSync(filePath, { encoding: "utf8" });
  328. credsObj = JSON.parse(content);
  329. validateAuthFileContent(credsObj, filePath);
  330. } catch (err) {
  331. return Promise.reject(err);
  332. }
  333. if (!credsObj.managementEndpointUrl) {
  334. credsObj.managementEndpointUrl = credsObj.resourceManagerEndpointUrl;
  335. }
  336. // setting the subscriptionId from auth file to the environment variable
  337. process.env[subscriptionEnvVariableName] = credsObj.subscriptionId;
  338. // get the AzureEnvironment or create a new AzureEnvironment based on the info provided in the auth file
  339. const envFound: any = {
  340. name: ""
  341. };
  342. const envNames = Object.keys(Environment);
  343. for (let i = 0; i < envNames.length; i++) {
  344. const env = envNames[i];
  345. const environmentObj = (Environment as any)[env];
  346. if (environmentObj &&
  347. environmentObj.managementEndpointUrl &&
  348. foundManagementEndpointUrl(credsObj.managementEndpointUrl, environmentObj.managementEndpointUrl)) {
  349. envFound.name = environmentObj.name;
  350. break;
  351. }
  352. }
  353. if (envFound.name) {
  354. optionsForSp.environment = (Environment as any)[envFound.name];
  355. } else {
  356. // create a new environment with provided info.
  357. const envParams: any = {
  358. // try to find a logical name or set the filepath as the env name.
  359. name: credsObj.managementEndpointUrl.match(/.*management\.core\.(.*)\..*/i)[1] || filePath
  360. };
  361. const keys = Object.keys(credsObj);
  362. for (let i = 0; i < keys.length; i++) {
  363. const key = keys[i];
  364. if (key.match(/^(clientId|clientSecret|clientCertificate|subscriptionId|tenantId)$/ig) === null) {
  365. if (key === "activeDirectoryEndpointUrl" && !key.endsWith("/")) {
  366. envParams[key] = credsObj[key] + "/";
  367. } else {
  368. envParams[key] = credsObj[key];
  369. }
  370. }
  371. }
  372. if (!envParams.activeDirectoryResourceId) {
  373. envParams.activeDirectoryResourceId = credsObj.managementEndpointUrl;
  374. }
  375. if (!envParams.portalUrl) {
  376. envParams.portalUrl = "https://portal.azure.com";
  377. }
  378. optionsForSp.environment = Environment.add(envParams);
  379. }
  380. if (credsObj.clientSecret) {
  381. return withServicePrincipalSecretWithAuthResponse(credsObj.clientId, credsObj.clientSecret, credsObj.tenantId, optionsForSp);
  382. }
  383. return withServicePrincipalCertificateWithAuthResponse(credsObj.clientId, credsObj.clientCertificate, credsObj.tenantId, optionsForSp);
  384. }
  385. /**
  386. * Provides a url and code that needs to be copy and pasted in a browser and authenticated over there. If successful, the user will get a
  387. * DeviceTokenCredentials object and the list of subscriptions associated with that userId across all the applicable tenants.
  388. *
  389. * @param {object} [options] Object representing optional parameters.
  390. *
  391. * @param {string} [options.clientId] The active directory application client id.
  392. * See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
  393. * for an example.
  394. *
  395. * @param {string} [options.tokenAudience] The audience for which the token is requested. Valid value is "graph".If tokenAudience is provided
  396. * then domain should also be provided its value should not be the default "common" tenant. It must be a string (preferrably in a guid format).
  397. *
  398. * @param {string} [options.domain] The domain or tenant id containing this application. Default value is "common".
  399. *
  400. * @param {Environment} [options.environment] The azure environment to authenticate with. Default environment is "Public Azure".
  401. *
  402. * @param {object} [options.tokenCache] The token cache. Default value is the MemoryCache object from adal.
  403. *
  404. * @param {object} [options.language] The language code specifying how the message should be localized to. Default value "en-us".
  405. *
  406. * @param {object|function} [options.userCodeResponseLogger] A logger that logs the user code response message required for interactive login. When
  407. * this option is specified the usercode response message will not be logged to console.
  408. *
  409. * @param {function} [optionalCallback] The optional callback.
  410. *
  411. * @returns {Promise<AuthResponse>} A Promise that resolves to AuthResponse that contains "credentials" and optional "subscriptions" array and rejects with an Error.
  412. */
  413. export async function withInteractiveWithAuthResponse(options?: InteractiveLoginOptions): Promise<AuthResponse> {
  414. if (!options) {
  415. options = {};
  416. }
  417. if (!options) {
  418. options = {};
  419. }
  420. if (!options.environment) {
  421. options.environment = Environment.AzureCloud;
  422. }
  423. if (!options.domain) {
  424. options.domain = AuthConstants.AAD_COMMON_TENANT;
  425. }
  426. if (!options.clientId) {
  427. options.clientId = AuthConstants.DEFAULT_ADAL_CLIENT_ID;
  428. }
  429. if (!options.tokenCache) {
  430. options.tokenCache = new adal.MemoryCache();
  431. }
  432. if (!options.language) {
  433. options.language = AuthConstants.DEFAULT_LANGUAGE;
  434. }
  435. if (!options.tokenAudience) {
  436. options.tokenAudience = options.environment.activeDirectoryResourceId;
  437. }
  438. const interactiveOptions: any = {};
  439. interactiveOptions.tokenAudience = options.tokenAudience;
  440. interactiveOptions.environment = options.environment;
  441. interactiveOptions.domain = options.domain;
  442. interactiveOptions.clientId = options.clientId;
  443. interactiveOptions.tokenCache = options.tokenCache;
  444. interactiveOptions.language = options.language;
  445. interactiveOptions.userCodeResponseLogger = options.userCodeResponseLogger;
  446. const authorityUrl: string = interactiveOptions.environment.activeDirectoryEndpointUrl + interactiveOptions.domain;
  447. const authContext = new adal.AuthenticationContext(authorityUrl, interactiveOptions.environment.validateAuthority, interactiveOptions.tokenCache);
  448. interactiveOptions.context = authContext;
  449. let userCodeResponse: any;
  450. let creds: DeviceTokenCredentials;
  451. function tryAcquireToken(interactiveOptions: InteractiveLoginOptions, resolve: any, reject: any) {
  452. authContext.acquireUserCode(interactiveOptions.tokenAudience!, interactiveOptions.clientId!, interactiveOptions.language!, (err: any, userCodeRes: adal.UserCodeInfo) => {
  453. if (err) {
  454. if (err.error === "authorization_pending") {
  455. setTimeout(() => {
  456. tryAcquireToken(interactiveOptions, resolve, reject);
  457. }, 1000);
  458. } else {
  459. return reject(err);
  460. }
  461. }
  462. userCodeResponse = userCodeRes;
  463. if (interactiveOptions.userCodeResponseLogger) {
  464. interactiveOptions.userCodeResponseLogger(userCodeResponse.message);
  465. } else {
  466. console.log(userCodeResponse.message);
  467. }
  468. return resolve(userCodeResponse);
  469. });
  470. }
  471. const getUserCode = new Promise<any>((resolve, reject) => {
  472. return tryAcquireToken(interactiveOptions, resolve, reject);
  473. });
  474. return getUserCode.then(() => {
  475. return new Promise<DeviceTokenCredentials>((resolve, reject) => {
  476. return authContext.acquireTokenWithDeviceCode(interactiveOptions.tokenAudience, interactiveOptions.clientId, userCodeResponse, (error: Error, tokenResponse: any) => {
  477. if (error) {
  478. return reject(error);
  479. }
  480. interactiveOptions.userName = tokenResponse.userId;
  481. interactiveOptions.authorizationScheme = tokenResponse.tokenType;
  482. try {
  483. creds = new DeviceTokenCredentials(interactiveOptions.clientId, interactiveOptions.domain, interactiveOptions.userName,
  484. interactiveOptions.tokenAudience, interactiveOptions.environment, interactiveOptions.tokenCache);
  485. } catch (err) {
  486. return reject(err);
  487. }
  488. return resolve(creds);
  489. });
  490. });
  491. }).then((creds) => {
  492. return buildTenantList(creds);
  493. }).then((tenants) => {
  494. return _getSubscriptions(creds, tenants, interactiveOptions.tokenAudience);
  495. }).then((subscriptions) => {
  496. return Promise.resolve({ credentials: creds, subscriptions: subscriptions });
  497. });
  498. }
  499. /**
  500. * Before using this method please install az cli from https://github.com/Azure/azure-cli/releases. Then execute `az ad sp create-for-rbac --sdk-auth > ${yourFilename.json}`.
  501. * If you want to create the sp for a different cloud/environment then please execute:
  502. * 1. az cloud list
  503. * 2. az cloud set –n <name of the environment>
  504. * 3. az ad sp create-for-rbac --sdk-auth > auth.json // create sp with secret
  505. * **OR**
  506. * 3. az ad sp create-for-rbac --create-cert --sdk-auth > auth.json // create sp with certificate
  507. * If the service principal is already created then login with service principal info:
  508. * 4. az login --service-principal -u <clientId> -p <clientSecret> -t <tenantId>
  509. * 5. az account show --sdk-auth > auth.json
  510. *
  511. * Authenticates using the service principal information provided in the auth file. This method will set
  512. * the subscriptionId from the auth file to the user provided environment variable in the options
  513. * parameter or the default "AZURE_SUBSCRIPTION_ID".
  514. *
  515. * @param {object} [options] - Optional parameters
  516. * @param {string} [options.filePath] - Absolute file path to the auth file. If not provided
  517. * then please set the environment variable AZURE_AUTH_LOCATION.
  518. * @param {string} [options.subscriptionEnvVariableName] - The subscriptionId environment variable
  519. * name. Default is "AZURE_SUBSCRIPTION_ID".
  520. * @param {function} [optionalCallback] The optional callback.
  521. *
  522. * @returns {function | Promise} If a callback was passed as the last parameter then it returns the callback else returns a Promise.
  523. *
  524. * {function} optionalCallback(err, credentials)
  525. * {Error} [err] - The Error object if an error occurred, null otherwise.
  526. * {ApplicationTokenCredentials} [credentials] - The ApplicationTokenCredentials object.
  527. * {Array} [subscriptions] - List of associated subscriptions across all the applicable tenants.
  528. * {Promise} A promise is returned.
  529. * @resolve {ApplicationTokenCredentials} The ApplicationTokenCredentials object.
  530. * @reject {Error} - The error object.
  531. */
  532. export function withAuthFile(): Promise<TokenCredentialsBase>;
  533. export function withAuthFile(options: LoginWithAuthFileOptions): Promise<TokenCredentialsBase>;
  534. export function withAuthFile(options: LoginWithAuthFileOptions, callback: { (err: Error, credentials: ApplicationTokenCredentials, subscriptions: Array<LinkedSubscription>): void }): void;
  535. export function withAuthFile(callback: any): void;
  536. export function withAuthFile(options?: LoginWithAuthFileOptions, callback?: { (err: Error, credentials: ApplicationTokenCredentials, subscriptions: Array<LinkedSubscription>): void }): any {
  537. if (!callback && typeof options === "function") {
  538. callback = options;
  539. options = undefined;
  540. }
  541. const cb = callback as Function;
  542. if (!callback) {
  543. return withAuthFileWithAuthResponse(options).then((authRes) => {
  544. return Promise.resolve(authRes.credentials);
  545. }).catch((err) => {
  546. return Promise.reject(err);
  547. });
  548. } else {
  549. msRest.promiseToCallback(withAuthFileWithAuthResponse(options))((err: Error, authRes: AuthResponse) => {
  550. if (err) {
  551. return cb(err);
  552. }
  553. return cb(undefined, authRes.credentials, authRes.subscriptions);
  554. });
  555. }
  556. }
  557. /**
  558. * Provides a url and code that needs to be copy and pasted in a browser and authenticated over there. If successful, the user will get a
  559. * DeviceTokenCredentials object and the list of subscriptions associated with that userId across all the applicable tenants.
  560. *
  561. * @param {object} [options] Object representing optional parameters.
  562. * @param {string} [options.clientId] The active directory application client id.
  563. * See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
  564. * for an example.
  565. * @param {string} [options.tokenAudience] The audience for which the token is requested. Valid value is "graph".If tokenAudience is provided
  566. * then domain should also be provided its value should not be the default "common" tenant. It must be a string (preferrably in a guid format).
  567. * @param {string} [options.domain] The domain or tenant id containing this application. Default value is "common".
  568. * @param {Environment} [options.environment] The azure environment to authenticate with. Default environment is "Public Azure".
  569. * @param {object} [options.tokenCache] The token cache. Default value is the MemoryCache object from adal.
  570. * @param {object} [options.language] The language code specifying how the message should be localized to. Default value "en-us".
  571. * @param {object|function} [options.userCodeResponseLogger] A logger that logs the user code response message required for interactive login. When
  572. * this option is specified the usercode response message will not be logged to console.
  573. * @param {function} [optionalCallback] The optional callback.
  574. *
  575. * @returns {function | Promise} If a callback was passed as the last parameter then it returns the callback else returns a Promise.
  576. *
  577. * {function} optionalCallback(err, credentials)
  578. * {Error} [err] - The Error object if an error occurred, null otherwise.
  579. * {DeviceTokenCredentials} [credentials] - The DeviceTokenCredentials object.
  580. * {Array} [subscriptions] - List of associated subscriptions across all the applicable tenants.
  581. * {Promise} A promise is returned.
  582. * @resolve {DeviceTokenCredentials} The DeviceTokenCredentials object.
  583. * @reject {Error} - The error object.
  584. */
  585. export function interactive(): Promise<DeviceTokenCredentials>;
  586. export function interactive(options: InteractiveLoginOptions): Promise<DeviceTokenCredentials>;
  587. export function interactive(options: InteractiveLoginOptions, callback: { (err: Error, credentials: DeviceTokenCredentials, subscriptions: Array<LinkedSubscription>): void }): void;
  588. export function interactive(callback: any): void;
  589. export function interactive(options?: InteractiveLoginOptions, callback?: { (err: Error, credentials: DeviceTokenCredentials, subscriptions: Array<LinkedSubscription>): void }): any {
  590. if (!callback && typeof options === "function") {
  591. callback = options;
  592. options = undefined;
  593. }
  594. const cb = callback as Function;
  595. if (!callback) {
  596. return withInteractiveWithAuthResponse(options).then((authRes) => {
  597. return Promise.resolve(authRes.credentials);
  598. }).catch((err) => {
  599. return Promise.reject(err);
  600. });
  601. } else {
  602. msRest.promiseToCallback(withInteractiveWithAuthResponse(options))((err: Error, authRes: AuthResponse) => {
  603. if (err) {
  604. return cb(err);
  605. }
  606. return cb(undefined, authRes.credentials, authRes.subscriptions);
  607. });
  608. }
  609. }
  610. /**
  611. * Provides an ApplicationTokenCredentials object and the list of subscriptions associated with that servicePrinicpalId/clientId across all the applicable tenants.
  612. *
  613. * @param {string} clientId The active directory application client id also known as the SPN (ServicePrincipal Name).
  614. * See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
  615. * for an example.
  616. * @param {string} secret The application secret for the service principal.
  617. * @param {string} domain The domain or tenant id containing this application.
  618. * @param {object} [options] Object representing optional parameters.
  619. * @param {string} [options.tokenAudience] The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
  620. * If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferrably in a guid format).
  621. * @param {Environment} [options.environment] The azure environment to authenticate with.
  622. * @param {object} [options.tokenCache] The token cache. Default value is the MemoryCache object from adal.
  623. * @param {function} [optionalCallback] The optional callback.
  624. *
  625. * @returns {function | Promise} If a callback was passed as the last parameter then it returns the callback else returns a Promise.
  626. *
  627. * {function} optionalCallback(err, credentials)
  628. * {Error} [err] - The Error object if an error occurred, null otherwise.
  629. * {ApplicationTokenCredentials} [credentials] - The ApplicationTokenCredentials object.
  630. * {Array} [subscriptions] - List of associated subscriptions across all the applicable tenants.
  631. * {Promise} A promise is returned.
  632. * @resolve {ApplicationTokenCredentials} The ApplicationTokenCredentials object.
  633. * @reject {Error} - The error object.
  634. */
  635. export function withServicePrincipalSecret(clientId: string, secret: string, domain: string): Promise<ApplicationTokenCredentials>;
  636. export function withServicePrincipalSecret(clientId: string, secret: string, domain: string, options: AzureTokenCredentialsOptions): Promise<ApplicationTokenCredentials>;
  637. export function withServicePrincipalSecret(clientId: string, secret: string, domain: string, options: AzureTokenCredentialsOptions, callback: { (err: Error, credentials: ApplicationTokenCredentials, subscriptions: Array<LinkedSubscription>): void }): void;
  638. export function withServicePrincipalSecret(clientId: string, secret: string, domain: string, callback: any): void;
  639. export function withServicePrincipalSecret(clientId: string, secret: string, domain: string, options?: AzureTokenCredentialsOptions, callback?: { (err: Error, credentials: ApplicationTokenCredentials, subscriptions: Array<LinkedSubscription>): void }): any {
  640. if (!callback && typeof options === "function") {
  641. callback = options;
  642. options = undefined;
  643. }
  644. const cb = callback as Function;
  645. if (!callback) {
  646. return withServicePrincipalSecretWithAuthResponse(clientId, secret, domain, options).then((authRes) => {
  647. return Promise.resolve(authRes.credentials);
  648. }).catch((err) => {
  649. return Promise.reject(err);
  650. });
  651. } else {
  652. msRest.promiseToCallback(withServicePrincipalSecretWithAuthResponse(clientId, secret, domain, options))((err: Error, authRes: AuthResponse) => {
  653. if (err) {
  654. return cb(err);
  655. }
  656. return cb(undefined, authRes.credentials, authRes.subscriptions);
  657. });
  658. }
  659. }
  660. /**
  661. * Provides an ApplicationTokenCertificateCredentials object and the list of subscriptions associated with that servicePrinicpalId/clientId across all the applicable tenants.
  662. *
  663. * @param {string} clientId The active directory application client id also known as the SPN (ServicePrincipal Name).
  664. * See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
  665. * for an example.
  666. * @param {string} certificateStringOrFilePath A PEM encoded certificate and private key OR an absolute filepath to the .pem file containing that information. For example:
  667. * - CertificateString: "-----BEGIN PRIVATE KEY-----\n<xxxxx>\n-----END PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\n<yyyyy>\n-----END CERTIFICATE-----\n"
  668. * - CertificateFilePath: **Absolute** file path of the .pem file.
  669. * @param {string} domain The domain or tenant id containing this application.
  670. * @param {object} [options] Object representing optional parameters.
  671. * @param {string} [options.tokenAudience] The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
  672. * If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferrably in a guid format).
  673. * @param {Environment} [options.environment] The azure environment to authenticate with.
  674. * @param {object} [options.tokenCache] The token cache. Default value is the MemoryCache object from adal.
  675. * @param {function} [optionalCallback] The optional callback.
  676. *
  677. * @returns {function | Promise} If a callback was passed as the last parameter then it returns the callback else returns a Promise.
  678. *
  679. * {function} optionalCallback(err, credentials)
  680. * {Error} [err] - The Error object if an error occurred, null otherwise.
  681. * {ApplicationTokenCertificateCredentials} [credentials] - The ApplicationTokenCertificateCredentials object.
  682. * {Array} [subscriptions] - List of associated subscriptions across all the applicable tenants.
  683. * {Promise} A promise is returned.
  684. * @resolve {ApplicationTokenCertificateCredentials} The ApplicationTokenCertificateCredentials object.
  685. * @reject {Error} - The error object.
  686. */
  687. export function withServicePrincipalCertificate(clientId: string, certificateStringOrFilePath: string, domain: string): Promise<ApplicationTokenCertificateCredentials>;
  688. export function withServicePrincipalCertificate(clientId: string, certificateStringOrFilePath: string, domain: string, options: AzureTokenCredentialsOptions): Promise<ApplicationTokenCredentials>;
  689. export function withServicePrincipalCertificate(clientId: string, certificateStringOrFilePath: string, domain: string, options: AzureTokenCredentialsOptions, callback: { (err: Error, credentials: ApplicationTokenCertificateCredentials, subscriptions: Array<LinkedSubscription>): void }): void;
  690. export function withServicePrincipalCertificate(clientId: string, certificateStringOrFilePath: string, domain: string, callback: any): void;
  691. export function withServicePrincipalCertificate(clientId: string, certificateStringOrFilePath: string, domain: string, options?: AzureTokenCredentialsOptions, callback?: { (err: Error, credentials: ApplicationTokenCertificateCredentials, subscriptions: Array<LinkedSubscription>): void }): any {
  692. if (!callback && typeof options === "function") {
  693. callback = options;
  694. options = undefined;
  695. }
  696. const cb = callback as Function;
  697. if (!callback) {
  698. return withServicePrincipalCertificateWithAuthResponse(clientId, certificateStringOrFilePath, domain, options).then((authRes) => {
  699. return Promise.resolve(authRes.credentials);
  700. }).catch((err) => {
  701. return Promise.reject(err);
  702. });
  703. } else {
  704. msRest.promiseToCallback(withServicePrincipalCertificateWithAuthResponse(clientId, certificateStringOrFilePath, domain, options))((err: Error, authRes: AuthResponse) => {
  705. if (err) {
  706. return cb(err);
  707. }
  708. return cb(undefined, authRes.credentials, authRes.subscriptions);
  709. });
  710. }
  711. }
  712. /**
  713. * Provides a UserTokenCredentials object and the list of subscriptions associated with that userId across all the applicable tenants.
  714. * This method is applicable only for organizational ids that are not 2FA enabled otherwise please use interactive login.
  715. *
  716. * @param {string} username The user name for the Organization Id account.
  717. * @param {string} password The password for the Organization Id account.
  718. * @param {object} [options] Object representing optional parameters.
  719. * @param {string} [options.clientId] The active directory application client id.
  720. * See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
  721. * for an example.
  722. * @param {string} [options.tokenAudience] The audience for which the token is requested. Valid values are 'graph', 'batch', or any other resource like 'https://vault.azure.net/'.
  723. * If tokenAudience is 'graph' then domain should also be provided and its value should not be the default 'common' tenant. It must be a string (preferrably in a guid format).
  724. * @param {string} [options.domain] The domain or tenant id containing this application. Default value "common".
  725. * @param {Environment} [options.environment] The azure environment to authenticate with.
  726. * @param {object} [options.tokenCache] The token cache. Default value is the MemoryCache object from adal.
  727. * @param {function} [optionalCallback] The optional callback.
  728. *
  729. * @returns {function | Promise} If a callback was passed as the last parameter then it returns the callback else returns a Promise.
  730. *
  731. * {function} optionalCallback(err, credentials)
  732. * {Error} [err] - The Error object if an error occurred, null otherwise.
  733. * {UserTokenCredentials} [credentials] - The UserTokenCredentials object.
  734. * {Array} [subscriptions] - List of associated subscriptions across all the applicable tenants.
  735. * {Promise} A promise is returned.
  736. * @resolve {UserTokenCredentials} The UserTokenCredentials object.
  737. * @reject {Error} - The error object.
  738. */
  739. export function withUsernamePassword(username: string, password: string): Promise<UserTokenCredentials>;
  740. export function withUsernamePassword(username: string, password: string, options: LoginWithUsernamePasswordOptions): Promise<UserTokenCredentials>;
  741. export function withUsernamePassword(username: string, password: string, callback: any): void;
  742. export function withUsernamePassword(username: string, password: string, options: LoginWithUsernamePasswordOptions, callback: { (err: Error, credentials: UserTokenCredentials, subscriptions: Array<LinkedSubscription>): void }): void;
  743. export function withUsernamePassword(username: string, password: string, options?: LoginWithUsernamePasswordOptions, callback?: { (err: Error, credentials: UserTokenCredentials, subscriptions: Array<LinkedSubscription>): void }): any {
  744. if (!callback && typeof options === "function") {
  745. callback = options;
  746. options = undefined;
  747. }
  748. const cb = callback as Function;
  749. if (!callback) {
  750. return withUsernamePasswordWithAuthResponse(username, password, options).then((authRes) => {
  751. return Promise.resolve(authRes.credentials);
  752. }).catch((err) => {
  753. return Promise.reject(err);
  754. });
  755. } else {
  756. msRest.promiseToCallback(withUsernamePasswordWithAuthResponse(username, password, options))((err: Error, authRes: AuthResponse) => {
  757. if (err) {
  758. return cb(err);
  759. }
  760. return cb(undefined, authRes.credentials, authRes.subscriptions);
  761. });
  762. }
  763. }
  764. /**
  765. * We only need to get the subscription list if the tokenAudience is for a management client.
  766. */
  767. function _getSubscriptions(
  768. creds: TokenCredentialsBase,
  769. tenants: string[],
  770. tokenAudience?: string): Promise<LinkedSubscription[]> {
  771. if (tokenAudience &&
  772. !managementPlaneTokenAudiences.some((item) => { return item === tokenAudience!.toLowerCase(); })) {
  773. return Promise.resolve(([]));
  774. }
  775. return getSubscriptionsFromTenants(creds, tenants);
  776. }
  777. /**
  778. * Initializes MSITokenCredentials class and calls getToken and returns a token response.
  779. *
  780. * @param {string} domain - required. The tenant id.
  781. * @param {object} options - Optional parameters
  782. * @param {string} [options.port] - port on which the MSI service is running on the host VM. Default port is 50342
  783. * @param {string} [options.resource] - The resource uri or token audience for which the token is needed. Default - "https://management.azure.com/"
  784. * @param {string} [options.aadEndpoint] - The add endpoint for authentication. default - "https://login.microsoftonline.com"
  785. * @param {any} callback - the callback function.
  786. */
  787. function _withMSI(options?: MSIVmOptions): Promise<MSIVmTokenCredentials> {
  788. if (!options) {
  789. options = {};
  790. }
  791. return new Promise((resolve, reject) => {
  792. const creds = new MSIVmTokenCredentials(options);
  793. creds.getToken().then((_tokenResponse) => {
  794. // We ignore the token response, it's put in the cache.
  795. return resolve(creds);
  796. }).catch(error => {
  797. reject(error);
  798. });
  799. });
  800. }
  801. /**
  802. * Before using this method please install az cli from https://github.com/Azure/azure-cli/releases.
  803. * If you have an Azure virtual machine provisioned with az cli and has MSI enabled,
  804. * you can then use this method to get auth tokens from the VM.
  805. *
  806. * To create a new VM, enable MSI, please execute this command:
  807. * az vm create -g <resource_group_name> -n <vm_name> --assign-identity --image <os_image_name>
  808. * Note: the above command enables a service endpoint on the host, with a default port 50342
  809. *
  810. * To enable MSI on a already provisioned VM, execute the following command:
  811. * az vm --assign-identity -g <resource_group_name> -n <vm_name> --port <custom_port_number>
  812. *
  813. * To know more about this command, please execute:
  814. * az vm --assign-identity -h
  815. *
  816. * Authenticates using the identity service running on an Azure virtual machine.
  817. * This method makes a request to the authentication service hosted on the VM
  818. * and gets back an access token.
  819. *
  820. * @param {object} [options] - Optional parameters
  821. * @param {string} [options.port] - port on which the MSI service is running on the host VM. Default port is 50342
  822. * @param {string} [options.resource] - The resource uri or token audience for which the token is needed.
  823. * For e.g. it can be:
  824. * - resourcemanagement endpoint "https://management.azure.com/"(default)
  825. * - management endpoint "https://management.core.windows.net/"
  826. * @param {function} [optionalCallback] The optional callback.
  827. *
  828. * @returns {function | Promise} If a callback was passed as the last parameter then it returns the callback else returns a Promise.
  829. *
  830. * {function} optionalCallback(err, credentials)
  831. * {Error} [err] - The Error object if an error occurred, null otherwise.
  832. * {object} [tokenResponse] - The tokenResponse (tokenType and accessToken are the two important properties)
  833. * {Promise} A promise is returned.
  834. * @resolve {object} - tokenResponse.
  835. * @reject {Error} - error object.
  836. */
  837. export function loginWithVmMSI(): Promise<MSIVmTokenCredentials>;
  838. export function loginWithVmMSI(options: MSIVmOptions): Promise<MSIVmTokenCredentials>;
  839. export function loginWithVmMSI(options: MSIVmOptions, callback: Callback<MSIVmTokenCredentials>): void;
  840. export function loginWithVmMSI(callback: Callback<MSIVmTokenCredentials>): void;
  841. export function loginWithVmMSI(options?: MSIVmOptions | Callback<MSIVmTokenCredentials>, callback?: Callback<MSIVmTokenCredentials>): void | Promise<MSIVmTokenCredentials> {
  842. if (!callback && typeof options === "function") {
  843. callback = options;
  844. options = {};
  845. }
  846. const cb = callback as Function;
  847. if (!callback) {
  848. return _withMSI(options as MSIVmOptions);
  849. } else {
  850. msRest.promiseToCallback(_withMSI(options as MSIVmOptions))((err: Error, tokenRes: MSITokenResponse) => {
  851. if (err) {
  852. return cb(err);
  853. }
  854. return cb(undefined, tokenRes);
  855. });
  856. }
  857. }
  858. /**
  859. * Private method
  860. */
  861. function _withAppServiceMSI(options: MSIAppServiceOptions): Promise<MSIAppServiceTokenCredentials> {
  862. if (!options) {
  863. options = {};
  864. }
  865. return new Promise((resolve, reject) => {
  866. const creds = new MSIAppServiceTokenCredentials(options);
  867. creds.getToken().then((_tokenResponse) => {
  868. // We ignore the token response, it's put in the cache.
  869. return resolve(creds);
  870. }).catch(error => {
  871. reject(error);
  872. });
  873. });
  874. }
  875. /**
  876. * Authenticate using the App Service MSI.
  877. * @param {object} [options] - Optional parameters
  878. * @param {string} [options.msiEndpoint] - The local URL from which your app can request tokens.
  879. * Either provide this parameter or set the environment variable `MSI_ENDPOINT`.
  880. * For example: `MSI_ENDPOINT="http://127.0.0.1:41741/MSI/token/"`
  881. * @param {string} [options.msiSecret] - The secret used in communication between your code and the local MSI agent.
  882. * Either provide this parameter or set the environment variable `MSI_SECRET`.
  883. * For example: `MSI_SECRET="69418689F1E342DD946CB82994CDA3CB"`
  884. * @param {string} [options.resource] - The resource uri or token audience for which the token is needed.
  885. * For example, it can be:
  886. * - resourcemanagement endpoint "https://management.azure.com/"(default)
  887. * - management endpoint "https://management.core.windows.net/"
  888. * @param {string} [options.msiApiVersion] - The api-version of the local MSI agent. Default value is "2017-09-01".
  889. * @param {function} [optionalCallback] - The optional callback.
  890. * @returns {function | Promise} If a callback was passed as the last parameter then it returns the callback else returns a Promise.
  891. *
  892. * {function} optionalCallback(err, credentials)
  893. * {Error} [err] - The Error object if an error occurred, null otherwise.
  894. * {object} [tokenResponse] - The tokenResponse (tokenType and accessToken are the two important properties)
  895. * {Promise} A promise is returned.
  896. * @resolve {object} - tokenResponse.
  897. * @reject {Error} - error object.
  898. */
  899. export function loginWithAppServiceMSI(): Promise<MSIAppServiceTokenCredentials>;
  900. export function loginWithAppServiceMSI(options: MSIAppServiceOptions): Promise<MSIAppServiceTokenCredentials>;
  901. export function loginWithAppServiceMSI(options: MSIAppServiceOptions, callback: Callback<MSIAppServiceTokenCredentials>): void;
  902. export function loginWithAppServiceMSI(callback: Callback<MSIAppServiceTokenCredentials>): void;
  903. export function loginWithAppServiceMSI(options?: MSIAppServiceOptions | Callback<MSIAppServiceTokenCredentials>, callback?: Callback<MSIAppServiceTokenCredentials>): void | Promise<MSIAppServiceTokenCredentials> {
  904. if (!callback && typeof options === "function") {
  905. callback = options;
  906. options = {};
  907. }
  908. const cb = callback as Function;
  909. if (!callback) {
  910. return _withAppServiceMSI(options as MSIAppServiceOptions);
  911. } else {
  912. msRest.promiseToCallback(_withAppServiceMSI(options as MSIAppServiceOptions))((err: Error, tokenRes: MSITokenResponse) => {
  913. if (err) {
  914. return cb(err);
  915. }
  916. return cb(undefined, tokenRes);
  917. });
  918. }
  919. }
  920. /**
  921. * Executes the azure cli command and returns the result. It will be `undefined` if the command did
  922. * not return anything or a `JSON object` if the command did return something.
  923. * @param cmd The az cli command to execute.
  924. */
  925. export async function execAz(cmd: string): Promise<any> {
  926. return new Promise<any>((resolve, reject) => {
  927. exec(`az ${cmd} --out json`, { encoding: "utf8" }, (error, stdout) => {
  928. if (error) {
  929. return reject(error);
  930. }
  931. if (stdout) {
  932. try {
  933. return resolve(JSON.parse(stdout));
  934. } catch (err) {
  935. const msg = `An error occured while parsing the output "${stdout}", of ` +
  936. `the cmd "${cmd}": ${err.stack}.`;
  937. return reject(new Error(msg));
  938. }
  939. }
  940. return resolve();
  941. });
  942. });
  943. }