This repository has been archived by the owner on May 5, 2023. It is now read-only.
/
msiTokenCredentials.js
127 lines (115 loc) · 5 KB
/
msiTokenCredentials.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// jshint ignore: start
// Note that this file is jshint clean except for properties inside token response
// which are not camel cased and they are issued by the MSI service,
// we do not control the shape of the response object.
'use strict';
const msrest = require('ms-rest');
const Constants = msrest.Constants;
/**
* Base class for MSI based credentials
*/
class MSITokenCredentials {
/**
* Creates an instance of MSITokenCredentials.
* @param {object} [options] - Optional parameters
* @param {string} [options.resource] - The resource uri or token audience for which the token is needed.
* For e.g. it can be:
* - resourcemanagement endpoint "https://management.azure.com"(default)
* - management endpoint "https://management.core.windows.net/"
*/
constructor(options) {
if (!options) options = {};
if (!options.resource) {
options.resource = 'https://management.azure.com/';
} else if (typeof options.resource.valueOf() !== 'string') {
throw new Error('resource must be a uri.');
}
this.resource = options.resource;
}
/**
* Parses a tokenResponse json string into a object, and converts properties on the first level to camelCase.
* This method tries to standardize the tokenResponse
* @param {string} body A json string
* @return {object} [tokenResponse] The tokenResponse (tokenType and accessToken are the two important properties).
*/
parseTokenResponse(body) {
// Docs show different examples of possible MSI responses for different services. https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/overview
// expires_on - is a Date like string in this doc
// - https://docs.microsoft.com/en-us/azure/app-service/app-service-managed-service-identity#rest-protocol-examples
// In other doc it is stringified number.
// - https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/tutorial-linux-vm-access-arm#get-an-access-token-using-the-vms-identity-and-use-it-to-call-resource-manager
let parsedBody = JSON.parse(body);
parsedBody.accessToken = parsedBody['access_token'];
delete parsedBody['access_token'];
parsedBody.tokenType = parsedBody['token_type'];
delete parsedBody['token_type'];
if (parsedBody['refresh_token']) {
parsedBody.refreshToken = parsedBody['refresh_token'];
delete parsedBody['refresh_token'];
}
if (parsedBody['expires_in']) {
parsedBody.expiresIn = parsedBody['expires_in'];
if (typeof parsedBody['expires_in'] === 'string') {
// normal number as a string '1504130527'
parsedBody.expiresIn = parseInt(parsedBody['expires_in'], 10);
}
delete parsedBody['expires_in'];
}
if (parsedBody['not_before']) {
parsedBody.notBefore = parsedBody['not_before'];
if (typeof parsedBody['not_before'] === 'string') {
// normal number as a string '1504130527'
parsedBody.notBefore = parseInt(parsedBody['not_before'], 10);
}
delete parsedBody['not_before'];
}
if (parsedBody['expires_on']) {
parsedBody.expiresOn = parsedBody['expires_on'];
if (typeof parsedBody['expires_on'] === 'string') {
// possibly a Date string '09/14/2017 00:00:00 PM +00:00'
if (parsedBody['expires_on'].includes(':') || parsedBody['expires_on'].includes('/')) {
parsedBody.expiresOn = new Date(parsedBody['expires_on']);
} else {
// normal number as a string '1504130527'
parsedBody.expiresOn = new Date(parseInt(parsedBody['expires_on'], 10));
}
}
delete parsedBody['expires_on'];
}
return parsedBody;
}
/**
* Prepares and sends a request to the MSI service endpoint which responds with the access token.
* @param {function} callback The callback in the form (err, result)
* @return {function} callback
* {Error} [err] The error if any
* {object} [tokenResponse] The tokenResponse (tokenType and accessToken are the two important properties).
*/
getToken(callback) {
return callback();
}
prepareRequestOptions() {
let reqOptions = {
headers: {},
body: ''
};
return reqOptions;
}
/**
* Signs a request with the Authentication header.
*
* @param {webResource} webResource The WebResource to be signed.
* @param {function(error)} callback The callback function.
* @return {undefined}
*/
signRequest(webResource, callback) {
this.getToken(function (err, result) {
if (err) return callback(err);
webResource.headers[Constants.HeaderConstants.AUTHORIZATION] = `${result.tokenType} ${result.accessToken}`;
return callback(null);
});
}
}
module.exports = MSITokenCredentials;