Skip to content

Commit 8f3bad9

Browse files
Managed Identity in MSALJS (#6880)
Co-authored-by: Robbie Ginsburg <[email protected]>
1 parent e190037 commit 8f3bad9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+21784
-20951
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "Implemented Managed Identity in MSAL-Node",
4+
"packageName": "@azure/msal-common",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "Implemented Managed Identity in MSAL-Node",
4+
"packageName": "@azure/msal-node",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

lib/msal-common/src/authority/Authority.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ export class Authority {
8787
protected performanceClient: IPerformanceClient | undefined;
8888
// Correlation Id
8989
protected correlationId: string;
90+
// Indicates if the authority is fake, for the purpose of a Managed Identity Application
91+
private managedIdentity: boolean;
9092
// Reserved tenant domain names that will not be replaced with tenant id
9193
private static reservedTenantDomains: Set<string> = new Set([
9294
"{tenant}",
@@ -103,7 +105,8 @@ export class Authority {
103105
authorityOptions: AuthorityOptions,
104106
logger: Logger,
105107
correlationId: string,
106-
performanceClient?: IPerformanceClient
108+
performanceClient?: IPerformanceClient,
109+
managedIdentity?: boolean
107110
) {
108111
this.canonicalAuthority = authority;
109112
this._canonicalAuthority.validateAsUri();
@@ -118,6 +121,7 @@ export class Authority {
118121
this.logger = logger;
119122
this.performanceClient = performanceClient;
120123
this.correlationId = correlationId;
124+
this.managedIdentity = managedIdentity || false;
121125
this.regionDiscovery = new RegionDiscovery(
122126
networkInterface,
123127
this.logger,
@@ -383,7 +387,7 @@ export class Authority {
383387
}
384388

385389
/**
386-
* Boolean that returns whethr or not tenant discovery has been completed.
390+
* Boolean that returns whether or not tenant discovery has been completed.
387391
*/
388392
discoveryComplete(): boolean {
389393
return !!this.metadata;
@@ -1165,7 +1169,9 @@ export class Authority {
11651169
* helper function to generate environment from authority object
11661170
*/
11671171
getPreferredCache(): string {
1168-
if (this.discoveryComplete()) {
1172+
if (this.managedIdentity) {
1173+
return Constants.DEFAULT_AUTHORITY_HOST;
1174+
} else if (this.discoveryComplete()) {
11691175
return this.metadata.preferred_cache;
11701176
} else {
11711177
throw createClientAuthError(

lib/msal-common/src/config/ClientConfiguration.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
import { INetworkModule } from "../network/INetworkModule";
77
import { DEFAULT_CRYPTO_IMPLEMENTATION, ICrypto } from "../crypto/ICrypto";
88
import { ILoggerCallback, Logger, LogLevel } from "../logger/Logger";
9-
import { Constants } from "../utils/Constants";
9+
import {
10+
Constants,
11+
DEFAULT_TOKEN_RENEWAL_OFFSET_SEC,
12+
} from "../utils/Constants";
1013
import { version } from "../packageMetadata";
1114
import { Authority } from "../authority/Authority";
1215
import { AzureCloudInstance } from "../authority/AuthorityOptions";
@@ -21,9 +24,6 @@ import {
2124
createClientAuthError,
2225
} from "../error/ClientAuthError";
2326

24-
// Token renewal offset default in seconds
25-
const DEFAULT_TOKEN_RENEWAL_OFFSET_SEC = 300;
26-
2727
/**
2828
* Use the configuration object to configure MSAL Modules and initialize the base interfaces for MSAL.
2929
*

lib/msal-common/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ export {
204204
GrantType,
205205
AADAuthorityConstants,
206206
HttpStatus,
207+
DEFAULT_TOKEN_RENEWAL_OFFSET_SEC,
207208
JsonWebTokenTypes,
208209
} from "./utils/Constants";
209210
export * as AADServerParamKeys from "./constants/AADServerParamKeys";

lib/msal-common/src/utils/Constants.ts

+12
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,20 @@ export const Constants = {
6565

6666
export const HttpStatus = {
6767
SUCCESS_RANGE_START: 200,
68+
SUCCESS: 200,
6869
SUCCESS_RANGE_END: 299,
6970
REDIRECT: 302,
7071
CLIENT_ERROR_RANGE_START: 400,
72+
BAD_REQUEST: 400,
73+
UNAUTHORIZED: 401,
74+
NOT_FOUND: 404,
75+
REQUEST_TIMEOUT: 408,
76+
TOO_MANY_REQUESTS: 429,
7177
CLIENT_ERROR_RANGE_END: 499,
7278
SERVER_ERROR_RANGE_START: 500,
79+
INTERNAL_SERVER_ERROR: 500,
80+
SERVICE_UNAVAILABLE: 503,
81+
GATEWAY_TIMEOUT: 504,
7382
SERVER_ERROR_RANGE_END: 599,
7483
} as const;
7584
export type HttpStatus = (typeof HttpStatus)[keyof typeof HttpStatus];
@@ -370,3 +379,6 @@ export type JsonWebTokenTypes =
370379
(typeof JsonWebTokenTypes)[keyof typeof JsonWebTokenTypes];
371380

372381
export const ONE_DAY_IN_MS = 86400000;
382+
383+
// Token renewal offset default in seconds
384+
export const DEFAULT_TOKEN_RENEWAL_OFFSET_SEC = 300;

lib/msal-node/docs/configuration.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,13 @@ const msalInstance = new PublicClientApplication(msalConfig);
9494

9595
### System Config Options
9696

97-
| Option | Description | Format | Default Value |
98-
| -------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
99-
| `loggerOptions` | Config object for logger. | See [below](#logger-config-options). | See [below](#logger-config-options). |
100-
| `NetworkClient` | Custom HTTP implementation | INetworkModule | [HttpClient.ts](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/src/network/HttpClient.ts) |
101-
| `proxyUrl` | The URL of the proxy the app is running behind | string | Empty string `""` |
102-
| `customAgentOptions` | Set of configurable options to set on a http(s) agent | Object - [NodeJS documentation on alloweable options](https://nodejs.org/docs/latest-v16.x/api/http.html#new-agentoptions) | Empty Object `{}` |
97+
| Option | Description | Format | Default Value |
98+
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
99+
| `loggerOptions` | Config object for logger. | See [below](#logger-config-options). | See [below](#logger-config-options). |
100+
| `NetworkClient` | Custom HTTP implementation | INetworkModule | [HttpClient.ts](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/src/network/HttpClient.ts) |
101+
| `proxyUrl` | The URL of the proxy the app is running behind | string | Empty string `""` |
102+
| `customAgentOptions` | Set of configurable options to set on a http(s) agent | Object - [NodeJS documentation on alloweable options](https://nodejs.org/docs/latest-v16.x/api/http.html#new-agentoptions) | Empty Object `{}` |
103+
| `disableInternalRetries` | A flag that disables MSALJS's built-in retry policies, allowing the app developer to specify their own retry policy. Currently, only Managed Identity flows have a retry policy. | boolean | boolean `false` |
103104

104105
#### Logger Config Options
105106

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Managed Identity with MSALJS
2+
3+
### Note
4+
5+
> This feature is in preview and feedback is welcome. See the [preview package](https://www.npmjs.com/package/@azure/msal-node/v/2.6.5-alpha.0) that has been upload to npm.
6+
7+
A common challenge for developers is the management of secrets, credentials, certificates, and keys used to secure communication between services. [Managed identities](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview) in Azure eliminate the need for developers to handle these credentials manually. MSALJS's msal-node library supports acquiring tokens through the managed identity service when used with applications running inside Azure infrastructure, such as:
8+
9+
- [Azure App Service](https://azure.microsoft.com/products/app-service/) (API version `2019-08-01` and above)
10+
- [Azure VMs](https://azure.microsoft.com/free/virtual-machines/)
11+
- [Azure Arc](https://learn.microsoft.com/en-us/azure/azure-arc/overview)
12+
- [Azure Cloud Shell](https://learn.microsoft.com/en-us/azure/cloud-shell/overview)
13+
- [Azure Service Fabric](https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-overview)
14+
15+
For a complete list, refer to [Azure services that can use managed identities to access other services](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/managed-identities-status).
16+
17+
> Browser-based MSALJS libraries do not offer confidential flows because there is no confidentiality between the browser and the token issuer. Additionally, they do not offer managed identity because the browser is not hosted on Azure.
18+
19+
## Which SDK to use - Azure SDK or MSAL?
20+
21+
MSAL libraries provide lower level APIs that are closer to the OAuth2 and OIDC protocols.
22+
23+
Both MSALJS and [Azure SDK](https://learn.microsoft.com/en-us/javascript/api/overview/azure/identity-readme?view=azure-dotnet&preserve-view=true) allow tokens to be acquired via managed identity. Internally, Azure SDK uses MSALJS, and it provides a higher-level API via its `DefaultAzureCredential` and `ManagedIdentityCredential` abstractions.
24+
25+
If your application already uses one of the SDKs, continue using the same SDK. Use Azure SDK, if you are writing a new application and plan to call other Azure resources, as this SDK provides a better developer experience by allowing the app to run on private developer machines where managed identity doesn't exist. Consider using MSAL if you need to call other downstream web APIs like Microsoft Graph or your own web API.
26+
27+
## Quick start
28+
29+
To quickly get started and see Azure Managed Identity in action, you can use one of [the samples](../../../samples/msal-node-samples/Managed-Identity) the team has built for this purpose.
30+
31+
## How to use managed identities
32+
33+
There are two types of managed identities available to developers - **system-assigned** and **user-assigned**. You can learn more about the differences in the [Managed identity types](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview#managed-identity-types) article. MSALJS supports acquiring tokens with both. [MSALJS logging](https://learn.microsoft.com/en-us/entra/identity-platform/msal-logging-js) allows to keep track of requests and related metadata.
34+
35+
Prior to using managed identities from MSALJS, developers must enable them for the resources they want to use through Azure CLI or the Azure Portal.
36+
37+
## Examples
38+
39+
For both user-assigned and system-assigned identities, developers can use the [ManagedIdentityApplication](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/msi_feature_branch/lib/msal-node/src/client/ManagedIdentityApplication.ts) class.
40+
41+
### System-assigned managed identities
42+
43+
For system-assigned managed identities, the developer does not need to pass any additional information when creating an instance of ManagedIdentityApplication, as it will automatically infer the relevant metadata about the assigned identity.
44+
45+
[acquireToken](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/msi_feature_branch/lib/msal-node/src/client/ManagedIdentityApplication.ts#L122) is called with the resource to acquire a token for, such as `https://management.azure.com`.
46+
47+
```typescript
48+
// optional
49+
const config: ManagedIdentityConfiguration = {
50+
system: {
51+
loggerOptions: {
52+
logLevel: LogLevel.Verbose,
53+
} as LoggerOptions,
54+
} as NodeSystemOptions,
55+
};
56+
57+
const systemAssignedManagedIdentityApplication: ManagedIdentityApplication =
58+
new ManagedIdentityApplication(config);
59+
60+
const managedIdentityRequestParams: ManagedIdentityRequestParams = {
61+
resource: "https://management.azure.com",
62+
};
63+
64+
const response: AuthenticationResult =
65+
await systemAssignedManagedIdentityApplication.acquireToken(
66+
managedIdentityRequestParams
67+
);
68+
console.log(response);
69+
```
70+
71+
### User-assigned managed identities
72+
73+
For user-assigned managed identities, the developer needs to pass either the client ID, full resource identifier, or the object ID of the managed identity when creating ManagedIdentityApplication.
74+
75+
Like in the case for system-assigned managed identities, ` acquireToken` is called with the resource to acquire a token for, such as `https://management.azure.com`.
76+
77+
```typescript
78+
const managedIdentityIdParams: ManagedIdentityIdParams = {
79+
userAssignedClientId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
80+
};
81+
82+
const config: ManagedIdentityConfiguration = {
83+
managedIdentityIdParams,
84+
// optional
85+
system: {
86+
loggerOptions: {
87+
logLevel: LogLevel.Verbose,
88+
} as LoggerOptions,
89+
} as NodeSystemOptions,
90+
};
91+
92+
const userAssignedClientIdManagedIdentityApplication: ManagedIdentityApplication =
93+
new ManagedIdentityApplication(config);
94+
95+
const managedIdentityRequestParams: ManagedIdentityRequestParams = {
96+
resource: "https://management.azure.com",
97+
};
98+
99+
const response: AuthenticationResult =
100+
await userAssignedClientIdManagedIdentityApplication.acquireToken(
101+
managedIdentityRequestParams
102+
);
103+
console.log(response);
104+
```
105+
106+
## Caching
107+
108+
MSALJS caches tokens from managed identity in memory. There is no eviction, but memory is not a concern because a limited number of managed identities can be defined. Cache extensibility is not supported in this scenario because tokens should not be shared between machines.
109+
110+
## Troubleshooting
111+
112+
For failed requests the error response contains a correlation ID that can be used for further diagnostics and log analysis. Keep in mind that the correlation IDs generated in MSALJS or passed into MSAL are different than the one returned in server error responses, as MSALJS cannot pass the correlation ID to managed identity token acquisition endpoints.
113+
114+
### Potential errors
115+
116+
#### `ManagedIdentityError` Error Code: `invalid_resource` Error Message: The supplied resource is an invalid URL.
117+
118+
This exception might mean that the resource you are trying to acquire a token for is either not supported or is provided using the wrong resource ID format. Examples of correct resource ID formats include `https://management.azure.com/.default`, `https://management.azure.com`, and `https://graph.microsoft.com`.

0 commit comments

Comments
 (0)