From a7c73783a30dc48eded0672bccd102e82b37f90b Mon Sep 17 00:00:00 2001 From: Matthew Podwysocki Date: Fri, 10 Nov 2023 13:59:43 -0500 Subject: [PATCH] [identity] Remove MSAL-Common --- sdk/identity/identity/package.json | 10 +- .../azureDeveloperCliCredential.ts | 23 +- .../samples-dev/clientSecretCredential.ts | 11 +- .../samples-dev/defaultAzureCredential.ts | 7 +- .../samples-dev/environmentCredential.ts | 11 +- .../samples-dev/workloadIdentityCredential.ts | 22 +- .../identity/src/client/identityClient.ts | 2 +- .../src/msal/nodeFlows/msalNodeCommon.ts | 11 +- .../identity/src/msal/utils.browser.ts | 293 ++++++++++++++++++ sdk/identity/identity/src/msal/utils.ts | 13 +- sdk/identity/identity/src/plugins/provider.ts | 2 +- .../node/workloadIdentityCredential.spec.ts | 2 +- .../identity/test/public/node/utils/utils.ts | 4 +- 13 files changed, 342 insertions(+), 69 deletions(-) create mode 100644 sdk/identity/identity/src/msal/utils.browser.ts diff --git a/sdk/identity/identity/package.json b/sdk/identity/identity/package.json index cdd8f2fcb0ee..edec7b9e1fdc 100644 --- a/sdk/identity/identity/package.json +++ b/sdk/identity/identity/package.json @@ -26,6 +26,7 @@ "./dist-esm/src/credentials/azureApplicationCredential.js": "./dist-esm/src/credentials/azureApplicationCredential.browser.js", "./dist-esm/src/credentials/onBehalfOfCredential.js": "./dist-esm/src/credentials/onBehalfOfCredential.browser.js", "./dist-esm/src/credentials/workloadIdentityCredential.js": "./dist-esm/src/credentials/workloadIdentityCredential.browser.js", + "./dist-esm/src/msal/utils.js": "./dist-esm/src/msal/utils.browser.js", "./dist-esm/src/util/authHostEnv.js": "./dist-esm/src/util/authHostEnv.browser.js", "./dist-esm/src/util/processMultiTenantRequest.js": "./dist-esm/src/util/processMultiTenantRequest.browser.js", "./dist-esm/src/tokenCache/TokenCachePersistence.js": "./dist-esm/src/tokenCache/TokenCachePersistence.browser.js", @@ -113,14 +114,12 @@ "@azure/core-util": "^1.6.1", "@azure/logger": "^1.0.0", "@azure/msal-browser": "^3.5.0", - "@azure/msal-common": "^13.1.0", "@azure/msal-node": "^2.5.1", "events": "^3.0.0", "jws": "^4.0.0", "open": "^8.0.0", "stoppable": "^1.1.0", - "tslib": "^2.2.0", - "uuid": "^8.3.0" + "tslib": "^2.2.0" }, "devDependencies": { "@azure-tools/test-recorder": "^3.0.0", @@ -137,7 +136,6 @@ "@types/node": "^14.0.0", "@types/sinon": "^10.0.0", "@types/stoppable": "^1.1.0", - "@types/uuid": "^8.0.0", "chai": "^4.2.0", "cross-env": "^7.0.2", "dotenv": "^16.0.0", @@ -161,8 +159,6 @@ "rimraf": "^3.0.0", "sinon": "^15.0.0", "ts-node": "^10.0.0", - "typescript": "~5.0.0", - "util": "^0.12.1", - "uuid": "^8.3.2" + "typescript": "~5.0.0" } } diff --git a/sdk/identity/identity/samples-dev/azureDeveloperCliCredential.ts b/sdk/identity/identity/samples-dev/azureDeveloperCliCredential.ts index 0cd7bab84101..7df4ba3916cc 100644 --- a/sdk/identity/identity/samples-dev/azureDeveloperCliCredential.ts +++ b/sdk/identity/identity/samples-dev/azureDeveloperCliCredential.ts @@ -8,33 +8,26 @@ import { AzureDeveloperCliCredential, DefaultAzureCredential } from "@azure/identity"; import dotenv from "dotenv"; +// Load the .env file if it exists dotenv.config(); -async function testDefaultCredential() { +async function testDefaultCredential(): Promise { const credential = new DefaultAzureCredential(); - try { - const token = await credential.getToken("https://storage.azure.com/.default"); - console.log(token); - } catch (err) { - console.log("Error with DefaultAzureCredential:", err); - } + const { token } = await credential.getToken("https://storage.azure.com/.default"); + console.log(`Token: ${token}`); } -async function testAzureDeveloperCliCredential() { +async function testAzureDeveloperCliCredential(): Promise { const credential = new AzureDeveloperCliCredential({ tenantId: process.env.AZURE_TENANT_ID, }); - try { - const token = await credential.getToken("https://storage.azure.com/.default"); - console.log(token); - } catch (err) { - console.log("Error with Credential:", err); - } + const { token } = await credential.getToken("https://storage.azure.com/.default"); + console.log(`Token: ${token}`); } -async function main() { +async function main(): Promise { await testDefaultCredential(); await testAzureDeveloperCliCredential(); } diff --git a/sdk/identity/identity/samples-dev/clientSecretCredential.ts b/sdk/identity/identity/samples-dev/clientSecretCredential.ts index 3bc505d37394..74f1186c5b9d 100644 --- a/sdk/identity/identity/samples-dev/clientSecretCredential.ts +++ b/sdk/identity/identity/samples-dev/clientSecretCredential.ts @@ -7,14 +7,15 @@ import { ClientSecretCredential } from "@azure/identity"; import { KeyClient } from "@azure/keyvault-keys"; +import dotenv from "dotenv"; // Load the .env file if it exists -require("dotenv").config(); +dotenv.config(); export async function main(): Promise { const credential = new ClientSecretCredential( - process.env.AZURE_TENANT_ID!, // The tenant ID in Azure Active Directory - process.env.AZURE_CLIENT_ID!, // The app registration client Id in the AAD tenant + process.env.AZURE_TENANT_ID!, // The tenant ID in Microsoft Entra ID + process.env.AZURE_CLIENT_ID!, // The app registration client Id in the Microsoft Entra tenant process.env.AZURE_CLIENT_SECRET! // The app registration secret for the registered application ); @@ -26,7 +27,5 @@ export async function main(): Promise { } main().catch((err) => { - console.log("error code: ", err.code); - console.log("error message: ", err.message); - console.log("error stack: ", err.stack); + console.error("The sample encountered an error:", err); }); diff --git a/sdk/identity/identity/samples-dev/defaultAzureCredential.ts b/sdk/identity/identity/samples-dev/defaultAzureCredential.ts index da77bbb04eb0..ce42f003ab35 100644 --- a/sdk/identity/identity/samples-dev/defaultAzureCredential.ts +++ b/sdk/identity/identity/samples-dev/defaultAzureCredential.ts @@ -7,9 +7,10 @@ import { DefaultAzureCredential } from "@azure/identity"; import { KeyClient } from "@azure/keyvault-keys"; +import dotenv from "dotenv"; // Load the .env file if it exists -require("dotenv").config(); +dotenv.config(); /** * The `DefaultAzureCredential` is appropriate for most scenarios where the application is intended to ultimately be run in the Azure Cloud. @@ -30,7 +31,5 @@ export async function main(): Promise { } main().catch((err) => { - console.log("error code: ", err.code); - console.log("error message: ", err.message); - console.log("error stack: ", err.stack); + console.error("The sample encountered an error:", err); }); diff --git a/sdk/identity/identity/samples-dev/environmentCredential.ts b/sdk/identity/identity/samples-dev/environmentCredential.ts index 4c65691c285a..bfd9bacc4ce3 100644 --- a/sdk/identity/identity/samples-dev/environmentCredential.ts +++ b/sdk/identity/identity/samples-dev/environmentCredential.ts @@ -7,14 +7,15 @@ import { EnvironmentCredential } from "@azure/identity"; import { KeyClient } from "@azure/keyvault-keys"; +import dotenv from "dotenv"; // Load the .env file if it exists -require("dotenv").config(); +dotenv.config(); export async function main(): Promise { // EnvironmentCredential expects the following three environment variables: - // - AZURE_TENANT_ID: The tenant ID in Azure Active Directory - // - AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant + // - AZURE_TENANT_ID: The tenant ID in Microsoft Entra ID + // - AZURE_CLIENT_ID: The application (client) ID registered in the Microsoft Entra tenant // - AZURE_CLIENT_SECRET: The client secret for the registered application const credential = new EnvironmentCredential(); @@ -26,7 +27,5 @@ export async function main(): Promise { } main().catch((err) => { - console.log("error code: ", err.code); - console.log("error message: ", err.message); - console.log("error stack: ", err.stack); + console.error("The sample encountered an error:", err); }); diff --git a/sdk/identity/identity/samples-dev/workloadIdentityCredential.ts b/sdk/identity/identity/samples-dev/workloadIdentityCredential.ts index 2972210c59a9..2e039c9afe1c 100644 --- a/sdk/identity/identity/samples-dev/workloadIdentityCredential.ts +++ b/sdk/identity/identity/samples-dev/workloadIdentityCredential.ts @@ -10,32 +10,24 @@ import dotenv from "dotenv"; dotenv.config(); -async function testDefaultCredential() { +async function testDefaultCredential(): Promise { const credential = new DefaultAzureCredential(); - try { - const token = await credential.getToken("https://storage.azure.com/.default"); - console.log(token); - } catch (err) { - console.log("Error with DefaultAzureCredential:", err); - } + const { token } = await credential.getToken("https://storage.azure.com/.default"); + console.log(`Token: ${token}`); } -async function testWorkloadCredential() { +async function testWorkloadCredential(): Promise { const credential = new WorkloadIdentityCredential({ tenantId: process.env.AZURE_TENANT_ID, clientId: process.env.AZURE_CLIENT_ID, }); - try { - const token = await credential.getToken("https://storage.azure.com/.default"); - console.log(token); - } catch (err) { - console.log("Error with WorkloadIdentityCredential:", err); - } + const result = await credential.getToken("https://storage.azure.com/.default"); + console.log(`Token: ${result?.token}`); } -async function main() { +async function main(): Promise { await testDefaultCredential(); await testWorkloadCredential(); } diff --git a/sdk/identity/identity/src/client/identityClient.ts b/sdk/identity/identity/src/client/identityClient.ts index 69d70246ba17..5e025fe7de61 100644 --- a/sdk/identity/identity/src/client/identityClient.ts +++ b/sdk/identity/identity/src/client/identityClient.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { INetworkModule, NetworkRequestOptions, NetworkResponse } from "@azure/msal-common"; +import type { INetworkModule, NetworkRequestOptions, NetworkResponse } from "@azure/msal-node"; import { AccessToken, GetTokenOptions } from "@azure/core-auth"; import { ServiceClient } from "@azure/core-client"; import { isNode } from "@azure/core-util"; diff --git a/sdk/identity/identity/src/msal/nodeFlows/msalNodeCommon.ts b/sdk/identity/identity/src/msal/nodeFlows/msalNodeCommon.ts index 5b81392e3355..d3c706aed9ee 100644 --- a/sdk/identity/identity/src/msal/nodeFlows/msalNodeCommon.ts +++ b/sdk/identity/identity/src/msal/nodeFlows/msalNodeCommon.ts @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import * as msalCommon from "@azure/msal-common"; import * as msalNode from "@azure/msal-node"; import { AccessToken, GetTokenOptions } from "@azure/core-auth"; import { getLogLevel } from "@azure/logger"; @@ -64,7 +63,7 @@ export interface MsalNodeOptions extends MsalFlowOptions { * @internal */ let persistenceProvider: - | ((options?: TokenCachePersistenceOptions) => Promise) + | ((options?: TokenCachePersistenceOptions) => Promise) | undefined = undefined; /** @@ -107,8 +106,8 @@ export abstract class MsalNode extends MsalBaseUtilities implements MsalFlow { protected identityClient?: IdentityClient; protected requiresConfidential: boolean = false; protected azureRegion?: string; - protected createCachePlugin: (() => Promise) | undefined; - protected createCachePluginCae: (() => Promise) | undefined; + protected createCachePlugin: (() => Promise) | undefined; + protected createCachePluginCae: (() => Promise) | undefined; /** * MSAL currently caches the tokens depending on the claims used to retrieve them. @@ -289,10 +288,10 @@ export abstract class MsalNode extends MsalBaseUtilities implements MsalFlow { * Allows the cancellation of a MSAL request. */ protected withCancellation( - promise: Promise, + promise: Promise, abortSignal?: AbortSignalLike, onCancel?: () => void - ): Promise { + ): Promise { return new Promise((resolve, reject) => { promise .then((msalToken) => { diff --git a/sdk/identity/identity/src/msal/utils.browser.ts b/sdk/identity/identity/src/msal/utils.browser.ts new file mode 100644 index 000000000000..2c31e99f6281 --- /dev/null +++ b/sdk/identity/identity/src/msal/utils.browser.ts @@ -0,0 +1,293 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msalCommon from "@azure/msal-browser"; + +import { AccessToken, GetTokenOptions } from "@azure/core-auth"; +import { AuthenticationRecord, MsalAccountInfo, MsalResult, MsalToken } from "./types"; +import { AuthenticationRequiredError, CredentialUnavailableError } from "../errors"; +import { CredentialLogger, formatError, formatSuccess } from "../util/logging"; +import { DefaultAuthorityHost, DefaultTenantId } from "../constants"; +import { AbortError } from "@azure/abort-controller"; +import { MsalFlowOptions } from "./flows"; +import { isNode, randomUUID } from "@azure/core-util"; +import { AzureLogLevel } from "@azure/logger"; + +export interface ILoggerCallback { + (level: msalCommon.LogLevel, message: string, containsPii: boolean): void; +} + +/** + * Latest AuthenticationRecord version + * @internal + */ +const LatestAuthenticationRecordVersion = "1.0"; + +/** + * Ensures the validity of the MSAL token + * @internal + */ +export function ensureValidMsalToken( + scopes: string | string[], + logger: CredentialLogger, + msalToken?: MsalToken, + getTokenOptions?: GetTokenOptions +): void { + const error = (message: string): Error => { + logger.getToken.info(message); + return new AuthenticationRequiredError({ + scopes: Array.isArray(scopes) ? scopes : [scopes], + getTokenOptions, + message, + }); + }; + if (!msalToken) { + throw error("No response"); + } + if (!msalToken.expiresOn) { + throw error(`Response had no "expiresOn" property.`); + } + if (!msalToken.accessToken) { + throw error(`Response had no "accessToken" property.`); + } +} + +/** + * Generates a valid authority by combining a host with a tenantId. + * @internal + */ +export function getAuthority(tenantId: string, host?: string): string { + if (!host) { + host = DefaultAuthorityHost; + } + if (new RegExp(`${tenantId}/?$`).test(host)) { + return host; + } + if (host.endsWith("/")) { + return host + tenantId; + } else { + return `${host}/${tenantId}`; + } +} + +/** + * Generates the known authorities. + * If the Tenant Id is `adfs`, the authority can't be validated since the format won't match the expected one. + * For that reason, we have to force MSAL to disable validating the authority + * by sending it within the known authorities in the MSAL configuration. + * @internal + */ +export function getKnownAuthorities( + tenantId: string, + authorityHost: string, + disableInstanceDiscovery?: boolean +): string[] { + if ((tenantId === "adfs" && authorityHost) || disableInstanceDiscovery) { + return [authorityHost]; + } + return []; +} + +/** + * Generates a logger that can be passed to the MSAL clients. + * @param logger - The logger of the credential. + * @internal + */ +export const defaultLoggerCallback: ( + logger: CredentialLogger, + platform?: "Node" | "Browser" +) => msalCommon.ILoggerCallback = + (logger: CredentialLogger, platform: "Node" | "Browser" = isNode ? "Node" : "Browser") => + (level, message, containsPii): void => { + if (containsPii) { + return; + } + switch (level) { + case msalCommon.LogLevel.Error: + logger.info(`MSAL ${platform} V2 error: ${message}`); + return; + case msalCommon.LogLevel.Info: + logger.info(`MSAL ${platform} V2 info message: ${message}`); + return; + case msalCommon.LogLevel.Verbose: + logger.info(`MSAL ${platform} V2 verbose message: ${message}`); + return; + case msalCommon.LogLevel.Warning: + logger.info(`MSAL ${platform} V2 warning: ${message}`); + return; + } + }; + +/** + * @internal + */ +export function getMSALLogLevel(logLevel: AzureLogLevel | undefined): msalCommon.LogLevel { + switch (logLevel) { + case "error": + return msalCommon.LogLevel.Error; + case "info": + return msalCommon.LogLevel.Info; + case "verbose": + return msalCommon.LogLevel.Verbose; + case "warning": + return msalCommon.LogLevel.Warning; + default: + // default msal logging level should be Info + return msalCommon.LogLevel.Info; + } +} + +/** + * The common utility functions for the MSAL clients. + * Defined as a class so that the classes extending this one can have access to its methods and protected properties. + * + * It keeps track of a logger and an in-memory copy of the AuthenticationRecord. + * + * @internal + */ +export class MsalBaseUtilities { + protected logger: CredentialLogger; + protected account: AuthenticationRecord | undefined; + + constructor(options: MsalFlowOptions) { + this.logger = options.logger; + this.account = options.authenticationRecord; + } + + /** + * Generates a UUID + */ + generateUuid(): string { + return randomUUID(); + } + + /** + * Handles the MSAL authentication result. + * If the result has an account, we update the local account reference. + * If the token received is invalid, an error will be thrown depending on what's missing. + */ + protected handleResult( + scopes: string | string[], + clientId: string, + result?: MsalResult, + getTokenOptions?: GetTokenOptions + ): AccessToken { + if (result?.account) { + this.account = msalToPublic(clientId, result.account); + } + ensureValidMsalToken(scopes, this.logger, result, getTokenOptions); + this.logger.getToken.info(formatSuccess(scopes)); + return { + token: result!.accessToken!, + expiresOnTimestamp: result!.expiresOn!.getTime(), + }; + } + + /** + * Handles MSAL errors. + */ + protected handleError(scopes: string[], error: Error, getTokenOptions?: GetTokenOptions): Error { + if ( + error.name === "AuthError" || + error.name === "ClientAuthError" || + error.name === "BrowserAuthError" + ) { + const msalError = error as msalCommon.AuthError; + switch (msalError.errorCode) { + case "endpoints_resolution_error": + this.logger.info(formatError(scopes, error.message)); + return new CredentialUnavailableError(error.message); + case "device_code_polling_cancelled": + return new AbortError("The authentication has been aborted by the caller."); + case "consent_required": + case "interaction_required": + case "login_required": + this.logger.info( + formatError(scopes, `Authentication returned errorCode ${msalError.errorCode}`) + ); + break; + default: + this.logger.info(formatError(scopes, `Failed to acquire token: ${error.message}`)); + break; + } + } + if ( + error.name === "ClientConfigurationError" || + error.name === "BrowserConfigurationAuthError" || + error.name === "AbortError" + ) { + return error; + } + return new AuthenticationRequiredError({ scopes, getTokenOptions, message: error.message }); + } +} + +// transformations.ts + +export function publicToMsal(account: AuthenticationRecord): msalCommon.AccountInfo { + const [environment] = account.authority.match(/([a-z]*\.[a-z]*\.[a-z]*)/) || [""]; + return { + ...account, + localAccountId: account.homeAccountId, + environment, + }; +} + +export function msalToPublic(clientId: string, account: MsalAccountInfo): AuthenticationRecord { + const record = { + authority: getAuthority(account.tenantId, account.environment), + homeAccountId: account.homeAccountId, + tenantId: account.tenantId || DefaultTenantId, + username: account.username, + clientId, + version: LatestAuthenticationRecordVersion, + }; + return record; +} + +/** + * Serializes an `AuthenticationRecord` into a string. + * + * The output of a serialized authentication record will contain the following properties: + * + * - "authority" + * - "homeAccountId" + * - "clientId" + * - "tenantId" + * - "username" + * - "version" + * + * To later convert this string to a serialized `AuthenticationRecord`, please use the exported function `deserializeAuthenticationRecord()`. + */ +export function serializeAuthenticationRecord(record: AuthenticationRecord): string { + return JSON.stringify(record); +} + +/** + * Deserializes a previously serialized authentication record from a string into an object. + * + * The input string must contain the following properties: + * + * - "authority" + * - "homeAccountId" + * - "clientId" + * - "tenantId" + * - "username" + * - "version" + * + * If the version we receive is unsupported, an error will be thrown. + * + * At the moment, the only available version is: "1.0", which is always set when the authentication record is serialized. + * + * @param serializedRecord - Authentication record previously serialized into string. + * @returns AuthenticationRecord. + */ +export function deserializeAuthenticationRecord(serializedRecord: string): AuthenticationRecord { + const parsed: AuthenticationRecord & { version?: string } = JSON.parse(serializedRecord); + + if (parsed.version && parsed.version !== LatestAuthenticationRecordVersion) { + throw Error("Unsupported AuthenticationRecord version"); + } + + return parsed; +} diff --git a/sdk/identity/identity/src/msal/utils.ts b/sdk/identity/identity/src/msal/utils.ts index 86ffb12d2c99..ace1bfece3ae 100644 --- a/sdk/identity/identity/src/msal/utils.ts +++ b/sdk/identity/identity/src/msal/utils.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import * as msalCommon from "@azure/msal-common"; +import * as msalCommon from "@azure/msal-node"; import { AccessToken, GetTokenOptions } from "@azure/core-auth"; import { AuthenticationRecord, MsalAccountInfo, MsalResult, MsalToken } from "./types"; @@ -10,10 +10,13 @@ import { CredentialLogger, formatError, formatSuccess } from "../util/logging"; import { DefaultAuthorityHost, DefaultTenantId } from "../constants"; import { AbortError } from "@azure/abort-controller"; import { MsalFlowOptions } from "./flows"; -import { isNode } from "@azure/core-util"; -import { v4 as uuidv4 } from "uuid"; +import { isNode, randomUUID } from "@azure/core-util"; import { AzureLogLevel } from "@azure/logger"; +export interface ILoggerCallback { + (level: msalCommon.LogLevel, message: string, containsPii: boolean): void; +} + /** * Latest AuthenticationRecord version * @internal @@ -93,7 +96,7 @@ export function getKnownAuthorities( export const defaultLoggerCallback: ( logger: CredentialLogger, platform?: "Node" | "Browser" -) => msalCommon.ILoggerCallback = +) => ILoggerCallback = (logger: CredentialLogger, platform: "Node" | "Browser" = isNode ? "Node" : "Browser") => (level, message, containsPii): void => { if (containsPii) { @@ -155,7 +158,7 @@ export class MsalBaseUtilities { * Generates a UUID */ generateUuid(): string { - return uuidv4(); + return randomUUID(); } /** diff --git a/sdk/identity/identity/src/plugins/provider.ts b/sdk/identity/identity/src/plugins/provider.ts index 4d08f953eb0c..a5f03602d499 100644 --- a/sdk/identity/identity/src/plugins/provider.ts +++ b/sdk/identity/identity/src/plugins/provider.ts @@ -17,7 +17,7 @@ export interface CachePluginControl { setPersistence( persistenceFactory: ( options?: TokenCachePersistenceOptions - ) => Promise + ) => Promise ): void; } diff --git a/sdk/identity/identity/test/internal/node/workloadIdentityCredential.spec.ts b/sdk/identity/identity/test/internal/node/workloadIdentityCredential.spec.ts index c7be0494fb75..e19faac67947 100644 --- a/sdk/identity/identity/test/internal/node/workloadIdentityCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/workloadIdentityCredential.spec.ts @@ -15,7 +15,7 @@ import { WorkloadIdentityCredential, WorkloadIdentityCredentialOptions, } from "../../../src"; -import { AuthenticationResult } from "@azure/msal-common"; +import { AuthenticationResult } from "@azure/msal-node"; import sinon from "sinon"; describe("WorkloadIdentityCredential", function () { diff --git a/sdk/identity/identity/test/public/node/utils/utils.ts b/sdk/identity/identity/test/public/node/utils/utils.ts index e0a9be91c65b..6ae1002ff002 100644 --- a/sdk/identity/identity/test/public/node/utils/utils.ts +++ b/sdk/identity/identity/test/public/node/utils/utils.ts @@ -5,8 +5,8 @@ import * as fs from "fs"; import * as jwt from "jsonwebtoken"; import * as net from "net"; import * as tls from "tls"; -import * as uuid from "uuid"; import ms from "ms"; +import { randomUUID } from "@azure/core-util"; export async function createJWTTokenFromCertificate( authorityHost: string, @@ -30,7 +30,7 @@ export async function createJWTTokenFromCertificate( }, algorithm: "RS256", audience: audience, - jwtid: uuid.v4(), + jwtid: randomUUID(), expiresIn: ms("1 h"), subject: clientId, issuer: clientId,