Skip to main content
Glama
by microsoft
azuretoken.ts7.54 kB
import debug from "debug" const dbg = debug("genaiscript:azuretoken") import { AZURE_TOKEN_EXPIRATION } from "../../core/src/constants" import { AuthenticationToken, AzureTokenResolver, isAzureTokenExpired, runtimeHost, } from "../../core/src/host" import { logError, logVerbose } from "../../core/src/util" import type { TokenCredential } from "@azure/identity" import { serializeError } from "../../core/src/error" import { CancellationOptions, CancellationToken, toSignal, } from "../../core/src/cancellation" import { AzureCredentialsType } from "../../core/src/server/messages" /** * This module provides functions to handle Azure authentication tokens, * including checking expiration and creating new tokens using Azure Identity SDK. */ /** * Creates a new Azure authentication token. * * @param signal - An AbortSignal to allow aborting the token creation process. * @returns A promise that resolves to an AuthenticationToken. * * Utilizes DefaultAzureCredential from the Azure Identity SDK to obtain the token. * Logs the expiration time of the token for debugging or informational purposes. */ async function createAzureToken( scopes: readonly string[], credentialsType: AzureCredentialsType, cancellationToken?: CancellationToken ): Promise<AuthenticationToken> { // Dynamically import DefaultAzureCredential from the Azure SDK dbg("dynamically importing Azure SDK credentials") const { DefaultAzureCredential, EnvironmentCredential, AzureCliCredential, ManagedIdentityCredential, AzurePowerShellCredential, AzureDeveloperCliCredential, WorkloadIdentityCredential, ChainedTokenCredential, } = await import("@azure/identity") let credential: TokenCredential switch (credentialsType) { case "cli": dbg("credentialsType is cli") credential = new AzureCliCredential() break case "env": dbg("credentialsType is env") credential = new EnvironmentCredential() break case "powershell": dbg("credentialsType is powershell") credential = new AzurePowerShellCredential() break case "devcli": dbg("credentialsType is devcli") credential = new AzureDeveloperCliCredential() break case "managedidentity": dbg("credentialsType is managedidentity") credential = new ManagedIdentityCredential() break case "workloadidentity": dbg("credentialsType is workloadidentity") credential = new WorkloadIdentityCredential() break case "default": dbg("credentialsType is default") credential = new DefaultAzureCredential() // CodeQL [SM05139] The user explicitly requested this credential type so the user has a good reason to use it. break default: // Check if the environment is local/development // also: https://nodejs.org/en/learn/getting-started/nodejs-the-difference-between-development-and-production if (process.env.NODE_ENV === "development") { dbg("node_env development: credentialsType is default") credential = new DefaultAzureCredential() // CodeQL [SM05139] Okay use of DefaultAzureCredential as it is only used in development........................................ } else { dbg( `node_env unspecified: credentialsType is env, cli, devcli, powershell` ) credential = new ChainedTokenCredential( new EnvironmentCredential(), new AzureCliCredential(), new AzureDeveloperCliCredential(), new AzurePowerShellCredential() ) } break } // Obtain the Azure token const abortSignal = toSignal(cancellationToken) dbg(`get token for %o`, scopes) const azureToken = await credential.getToken(scopes.slice(), { abortSignal, }) // Prepare the result token object with the token and expiration timestamp const res = { credential, token: azureToken.token, // Use provided expiration timestamp or default to a constant expiration time expiresOnTimestamp: azureToken.expiresOnTimestamp ? azureToken.expiresOnTimestamp : Date.now() + AZURE_TOKEN_EXPIRATION, } return res } class AzureTokenResolverImpl implements AzureTokenResolver { _token: AuthenticationToken _error: any _resolver: Promise<{ token?: AuthenticationToken; error?: SerializedError }> constructor( public readonly name: string, public readonly envName: string, public readonly scopes: readonly string[] ) {} get error(): SerializedError { return this._error } async token( credentialsType: AzureCredentialsType, options?: CancellationOptions ): Promise<{ token?: AuthenticationToken; error?: SerializedError }> { if (this._resolver) { return this._resolver } // cached const { cancellationToken } = options || {} if (isAzureTokenExpired(this._token)) { dbg("azure token expired") this._token = undefined this._error = undefined } if (this._token || this._error) { dbg("returning cached token or error") return { token: this._token, error: this._error } } if (!this._resolver) { const scope = await runtimeHost.readSecret(this.envName) dbg(`reading secret for envName: ${this.envName}`) const scopes = scope ? scope.split(",") : this.scopes this._resolver = createAzureToken( scopes, credentialsType, cancellationToken ) .then((res) => { this._token = res this._error = undefined this._resolver = undefined dbg( `${this.name}: ${credentialsType || ""} token (${scopes.join(",")}) expires on ${new Date(res.expiresOnTimestamp).toUTCString()}` ) return { token: this._token, error: this._error } }) .catch((err) => { dbg(`error occurred: ${err}`) logError(err) this._resolver = undefined this._token = undefined this._error = serializeError(err) return { token: this._token, error: this._error } }) } return this._resolver } } /** * Creates an AzureTokenResolver instance for handling Azure authentication tokens. * * @param name - The name of the resolver, used for logging or identification. * @param envName - The environment variable name containing authentication scopes or configuration data. * @param scopes - The default Azure resource scopes for authentication. * @returns An instance of AzureTokenResolver for managing token retrieval and caching. */ export function createAzureTokenResolver( name: string, envName: string, scopes: readonly string[] ): AzureTokenResolver { return new AzureTokenResolverImpl(name, envName, scopes) }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/microsoft/genaiscript'

If you have feedback or need assistance with the MCP directory API, please join our Discord server