Skip to content

Conversation

@MicroFish91
Copy link
Contributor

@MicroFish91 MicroFish91 commented Nov 6, 2025

Overview

This PR adds a secure auth layer that clients should interface with to obtain the Azure Resources core APIs (used for registering branch data resources). For full details on the new auth handshake, please see the auth readme included with this PR. We will continue to publicly provide the core APIs while we let clients onboard. Afterwards we will remove these APIs from the public exports and only expose them through the dedicated auth layer.

Setup

Here are the steps if you would like to try out the changes:

  1. Pull down this branch
  2. npm install & npm run compile in Azure Resources root
  3. npm install & npm pack in Azure Resources API root
  4. Pull down one of the client extension sample branches below
  5. npm install and add the Azure Resources API package built from step 3
  6. Launch Client Extension + Host, observe the onDidReceiveAzureResourcesApis callback to verify we are getting matching Azure Resources APIs

Client Extension Samples

Container Apps example (V2): microsoft/vscode-azurecontainerapps#992
Functions example (internal + V2): microsoft/vscode-azurefunctions#4777

Todos

To add in follow-up PRs.

UPDATE (11/20/2025):

I split up the PRs a bit better, now the client tooling lives in this PR. Therefore if you want to test the functionality described under Setup above, you'll now need to test off of that branch instead.

The order of the PRs to review should be:

  1. Internal Auth Layer - This PR
  2. Client Tools for automating the handshake - Add Azure Resources API (v4) client tooling #1313
  3. Internal Auth Tests - Azure Resources API (v4) internal tests #1288
  4. Client Tools Tests - Azure Resources API (v4) client tools tests #1308

@MicroFish91 MicroFish91 marked this pull request as ready for review November 11, 2025 00:22
@MicroFish91 MicroFish91 requested a review from a team as a code owner November 11, 2025 00:22
@MicroFish91 MicroFish91 changed the title Add an Azure Resources API authentication layer Add an Azure Resources API (v4) authentication layer Nov 19, 2025
@MicroFish91 MicroFish91 marked this pull request as ready for review November 19, 2025 20:12

const v4: string = '4.0.0';

export type AuthApiFactoryDependencies = {
Copy link
Contributor Author

@MicroFish91 MicroFish91 Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this type in preparation to leverage dependency injection during tests. It probably belongs in the follow-up test PR, but I'm hoping to not have to extract and re-add it again 😅

@bwateratmsft bwateratmsft self-requested a review December 8, 2025 14:47
@alexweininger
Copy link
Member

We need to write some documentation on how this new layer works. I'd like to help out writing the docs if you want my help, can you post the diagram you made @MicroFish91?

@MicroFish91
Copy link
Contributor Author

MicroFish91 commented Jan 13, 2026

We need to write some documentation on how this new layer works. I'd like to help out writing the docs if you want my help, can you post the diagram you made @MicroFish91?

Yup!

I actually already have some docs and diagrams added in this other PR. Let me know if you think anything else should be added / changed to either the main README or the auth one

README for Client Extensions

Handshake Diagram

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new authentication layer (v4) for the Azure Resources API, implementing a secure handshake mechanism for client extensions to access core Azure Resources APIs. The auth layer uses credential-based verification to ensure only authorized extensions can access the protected APIs.

Changes:

  • Added new v4 authentication API with session creation and verification
  • Implemented UUID-based credential manager for secure token generation and validation
  • Updated extension metadata to include publisher information for allowed extension list
  • Deprecated direct access to core APIs in favor of authenticated access

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
src/api/auth/createAuthApiFactory.ts Creates the v4 auth API factory with session management
src/api/auth/createApiSession/createApiSessionInternal.ts Handles session creation with extension whitelist validation
src/api/auth/createApiSession/CreateApiSessionInternalContext.ts Context interface for session creation
src/api/auth/verifyApiSession/verifyApiSessionInternal.ts Validates credentials during API access
src/api/auth/verifyApiSession/VerifyApiSessionInternalContext.ts Context interface for verification
api/src/auth/credentialManager/AzExtCredentialManager.ts Interface for credential management
api/src/auth/credentialManager/AzExtUUIDCredentialManager.ts UUID-based credential manager implementation
api/src/extensionApi.ts Adds new auth API interface definitions
api/src/utils/apiUtils.ts Adds optional receiveAzureResourcesApiSession method and refactors getExtensionExports
api/src/utils/getApi.ts Adds deprecation notice for direct API access
api/src/utils/maskValue.ts Utility for masking sensitive credential values
src/extension.ts Integrates auth factory into API provider structure
src/azureExtensions.ts Adds publisher field to all extension metadata for allowlist
src/hostapi.v4.internal.ts Type export for internal v4 auth API
src/hostapi.v2.internal.ts Adds copyright header
Comments suppressed due to low confidence (1)

src/azureExtensions.ts:155

  • There are two entries in the azureExtensions array for 'vscode-azureresourcegroups' (lines 64-69 for "Resource Groups" and lines 149-155 for "Managed Identity"). Since line 12 states these are "deduped and used to build the final list of allowed extensions", having duplicate extension names may cause confusion or unintended behavior. Consider consolidating these into a single entry with both labels or ensuring the deduplication logic handles this correctly.
        name: 'vscode-azureresourcegroups',
        publisher: msAzureToolsPublisher,
        label: 'Managed Identity',
        resourceTypes: [
            AzExtResourceType.ManagedIdentityUserAssignedIdentities
        ]
    },

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if (valueToMask) {
const formsOfValue: string[] = [valueToMask, encodeURIComponent(valueToMask)];
for (const v of formsOfValue) {
data = data.replace(new RegExp(escape(v), 'gi'), '---');
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The global escape function is deprecated and should not be used. Consider using a utility function from a well-maintained library (e.g., lodash.escapeRegExp) or implementing regex escaping manually by replacing special regex characters. For example: value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')

Suggested change
data = data.replace(new RegExp(escape(v), 'gi'), '---');
data = data.replace(new RegExp(v.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'), '---');

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

@MicroFish91 MicroFish91 Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyone have thoughts on this? I can look into this more tomorrow, but I literally just copied the maskValue function that we use in utils. If it's worth changing here, it's probably also worth changing there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation uses the escape function when building a regular expression to mask sensitive values. However, escape only performs URI encoding and does not account for special regex characters like ., [], (), and others. As a result, if the value to mask contains any of these special characters, the regular expression may match unintended patterns or too broadly. For example, if valueToMask is alice@example.com, using escape(valueToMask) in the regex would also match and mask variations like alice@example-com, since the dot in the pattern is not escaped and matches any character.

I think we should go with Copilot's suggestion here. And also change the maskValue util.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, Copilot is wrong here. It didn't look that we're using this package: https://github.com/sindresorhus/escape-string-regexp and not the global escape function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, it was correct. We are using the global escape here since when you copied over the code you didn't also copy over the import and now it's using global escape. Follow what I did here: microsoft/vscode-azuretools#2171

await apiUtils.getAzureExtensionApi(ext.context, context.clientExtensionId, context.clientExtensionVersion);

const azureResourcesCredential: string = await context.credentialManager.createCredential(context.clientExtensionId);
await clientApi.receiveAzureResourcesApiSession?.(azureResourcesCredential, context.clientExtensionCredential);
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code uses optional chaining receiveAzureResourcesApiSession?.() which will silently do nothing if the method doesn't exist on the client API. However, this may be intentional to support backward compatibility with client extensions that haven't implemented this method yet. Consider adding telemetry to track whether clients are implementing this method to aid in the migration process.

Suggested change
await clientApi.receiveAzureResourcesApiSession?.(azureResourcesCredential, context.clientExtensionCredential);
const hasReceiveAzureResourcesApiSession: boolean = typeof (clientApi as any).receiveAzureResourcesApiSession === 'function';
context.telemetry.properties.clientHasReceiveAzureResourcesApiSession = String(hasReceiveAzureResourcesApiSession);
if (hasReceiveAzureResourcesApiSession) {
await (clientApi as any).receiveAzureResourcesApiSession(azureResourcesCredential, context.clientExtensionCredential);
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

@MicroFish91 MicroFish91 Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vote no on this one, client tooling should automatically handle setting all of this up anyway

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be recording telemetry on extensions who haven't updated to use our new auth? Seems like a good way to help us track it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good idea, I'll add telemetry for that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve been thinking about this more, and I don’t think adding this specific piece of telemetry would give us the signal we’re looking for. Most existing client extensions aren’t routed through this auth layer yet, so this code path wouldn’t be triggered for them. Extensions that do migrate would likely use our client tooling, which would skew the data toward an apparent ~100% telemetry match for the receiver method.

Instead, it might be possible to infer this using the telemetry we already have. One option could be to look at which extensions are using the auth layer by examining the extensionIds recorded for these events:

  • api.getAzureResourcesApis

  • api.createAzureResourcesApiSession

We could then compare that against the set of extensions calling into the core APIs, where extensionIds appear to be recorded here:
image

Let me know what you think / whether this approach could makes sense.

If this sounds reasonable, I can spend some time this afternoon double-checking whether this is feasible with the telemetry we already have wired up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome 🙂

I verified via query that we can see the calling extension IDs for core API methods already. I also double-checked that output telemetry for the new code paths being introduced here are logging the extensionIds, verified via the debug console showing output telemetry during the internal auth tests. At that point, I assume it should be straightforward enough to cross-reference the diffs to find any extensions that still need to migrate.

@MicroFish91 MicroFish91 merged commit c6189d6 into main Jan 22, 2026
3 checks passed
@MicroFish91 MicroFish91 deleted the mwf/v4 branch January 22, 2026 23:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants