Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds property baseUri on the ServiceClientOptions #457

Merged
merged 22 commits into from
Aug 18, 2021

Conversation

sadasant
Copy link
Contributor

Added a new property baseUri on the ServiceClientOptions, so that when a TokenCredential is sent to ServiceClient, it now automatically sets the scope of future token requests based on the options.baseUri property.

This should address Azure/azure-sdk-for-js#15945

@sadasant sadasant self-assigned this Jul 27, 2021
lib/serviceClient.ts Outdated Show resolved Hide resolved
@sadasant sadasant marked this pull request as ready for review July 27, 2021 23:34
Changelog.md Outdated Show resolved Hide resolved
lib/serviceClient.ts Outdated Show resolved Hide resolved
@ramya-rao-a
Copy link
Contributor

Any concerns with subsequent interfaces extending ServiceClientOptions and redeclaring the baseUri property?

@sadasant
Copy link
Contributor Author

sadasant commented Jul 28, 2021

@ramya-rao-a I don’t know of use cases like that. This approach is simpler than others we’ve talked, but it’s less considerate of edge cases. We could have something more automated, like a function on the adapter that gets called on getToken and lets you replace the scope cc: @chradek

Changelog.md Outdated Show resolved Hide resolved
Co-authored-by: Ramya Rao <ramya.rao.a@outlook.com>
@sadasant
Copy link
Contributor Author

Oh I understood now that you meant if nothing breaks if the baseUri property is re-declared on constructors (I thought you meant late assignments). Testing to be sure!

@chradek
Copy link

chradek commented Jul 29, 2021

Any concerns with subsequent interfaces extending ServiceClientOptions and redeclaring the baseUri property?

In our repo there are several instances where ServiceClientOptions is extended and the sub-interface defines baseUri?: string. This is compatible with the change.

  • ApplicationInsightsDataClientOptions
  • LocalSearchClientOptions
  • LogAnalyticsClientOptions
  • ServiceFabricClientOptions

However I did notice that more common that baseUri was endpoint. endpoint is used to help construct the baseUri for the services that use it (cognitiveservices-entitysearch is an example).
https://github.com/Azure/azure-sdk-for-js/blob/ce2c1f5310bf0e71dbf8bc2b29d5731a9dfd961f/sdk/cognitiveservices/cognitiveservices-entitysearch/src/entitySearchClientContext.ts#L40-L48

    super(credentials, options);

    this.endpoint = 'https://api.cognitive.microsoft.com';
    this.baseUri = "{Endpoint}/bing/v7.0";
    this.requestContentType = "application/json; charset=utf-8";
    this.credentials = credentials;
    if (options.endpoint !== null && options.endpoint !== undefined) {
      this.endpoint = options.endpoint;
    }

So @sadasant, I don't know if you'll also need to make more changes to support clients that expose endpoint. With regards to the IdentityAdapter, where does the scope come from if you don't override it in the constructor?

@sadasant
Copy link
Contributor Author

@chradek In the AzureIdentityCredentialAdapter (part of this repo) the scope is either the default one, or is assigned at the constructor.

On Identity, the scope is received on each getToken call, which is why the translation is a bit weird. I think that if we wanted to do something robust that could work for every possible use case, we would allow the adapter to overwrite the constructor scope on getToken. However, since our target audience for this adapter is the generated SDKs, I believe the current approach of this PR to be a reasonable one, since baseUri is always sent through by the children classes.

Let me know if I’m missing something 🤔

@sadasant
Copy link
Contributor Author

@chradek for the case you presented here: https://github.com/Azure/azure-sdk-for-js/blob/ce2c1f5310bf0e71dbf8bc2b29d5731a9dfd961f/sdk/cognitiveservices/cognitiveservices-entitysearch/src/entitySearchClientContext.ts#L40-L48

I believe that scope is specific to the service version they use. For them, the default scope is likely sufficient, unless they’re working on external clouds.

But to confirm I would need to test it, or speak with somebody owning the cognitive search client. Let me see if I can find someone.

@chradek
Copy link

chradek commented Jul 29, 2021

I believe that scope is specific to the service version they use. For them, the default scope is likely sufficient, unless they’re working on external clouds.

For what it's worth, I don't think what cognitive services is doing with endpoint precludes us from exposing baseUri.
It's possible that for cognitive services, we'd have to construct the baseUri to pass to the ServiceClient if the user specified a custom endpoint, but we'd need to know what the scopes look like for non-public clouds for those services. But that can be done if needed and I don't think has to be part of this PR.

lib/serviceClient.ts Outdated Show resolved Hide resolved
@sadasant
Copy link
Contributor Author

@ramya-rao-a sounds good! thank you!

@ramya-rao-a
Copy link
Contributor

ramya-rao-a commented Aug 12, 2021

On another set of second thoughts...

For other Azure clouds, the authority host needs to be set when creating the TokenCredential from @azure/identity. If the authority host was made a read only property on the credential, then can we not do something like the below in the adapter?

switch (azureTokenCredential.authorityHost) {
      case "https://login.chinacloudapi.cn":
        this.scopes = "https://management.chinacloudapi.cn/.default"
        break;
      // and so on for other clouds
      default:
        break;
    }

@ramya-rao-a
Copy link
Contributor

@deyaaeldeen has been experimenting with the @azure/batch package and we concluded that we cannot reverse engineer the scope from the baseUri. Therefore, we are going to keep this @azure/identity support in Track 1 code gen only for the mgmt plane packages. This would mean that us hardcoding the mgmt related scope in the adapter in ms-rest-js is ok as long as we find a way to deal with different clouds. And so comes my suggestion in the previous comment on utilizing the authority host passed to the credential constructor to determine the right scope for mgmt plane packages

@sadasant
Copy link
Contributor Author

@ramya-rao-a Ok I’ll work on a PR for v1

@sadasant
Copy link
Contributor Author

@ramya-rao-a @deyaaeldeen on second thought

From the perspective of Identity, authorityHost does not fully overlap with scopes. Scopes are about what one wants to gain access to, and authorityHost is about who we want to authenticate with.

I think that’s part of the problem on ms-rest-js, baseUri is not a reliable scope for the same reason. It would be better if we could derive the scope during the authentication request, and not at the constructor.

@ramya-rao-a
Copy link
Contributor

Fair enough

In that case, lets guard the changes in this PR to only take affect if the baseUri matches the ARM scope
That way we atleast reduce the scope of this change to something we are sure should be updated.

We have logged Azure/autorest.typescript#1153 to ensure that only the ARM packages get the TokenCredential support in the client constructor so that we don't have to worry what scope to use for data plane packages

@sadasant sadasant force-pushed the fix/azure-sdk-for-js-issues-15945 branch from 3946f75 to dd32d33 Compare August 13, 2021 23:56
@sadasant
Copy link
Contributor Author

I pushed a change that makes it so the baseUri is used as the scope only if it matches the management clouds. Please let me know if there’s anything else I can do!

Comment on lines 215 to 218
if (
options?.baseUri &&
azureManagementClouds.find((cloud) => options!.baseUri!.indexOf(cloud) > -1)
) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we simplify this?

Suggested change
if (
options?.baseUri &&
azureManagementClouds.find((cloud) => options!.baseUri!.indexOf(cloud) > -1)
) {
if (
options?.baseUri &&
azureManagementClouds.includes(options?.baseUri)
) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Includes breaks in the browser, I think https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

Well, InternetExplorer, so I guess it’s ok to use 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.

I merged it, but keep in mind that includes will work only for exact matches.

Copy link
Contributor

Choose a reason for hiding this comment

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

And that is good right? We want exact matches

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My brain is kinda fried right now. 🙇‍♂️ 🙇‍♂️ slowly leaves

Copy link
Contributor

Choose a reason for hiding this comment

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

As far as we know, the baseuri indeed needs to match the resource manager endpoints we have listed here. If there are cases where this is not true, I would much rather hear that from a customer than make assumptions ourselves

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed here: 7224651

Changelog.md Outdated Show resolved Hide resolved
Changelog.md Outdated Show resolved Hide resolved
let serviceClientCredentials: ServiceClientCredentials | undefined;
if (isTokenCredential(credentials)) {
serviceClientCredentials = new AzureIdentityCredentialAdapter(credentials);
let scope: string | undefined = undefined;
const azureManagementClouds = [
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: This const can be defined outside of this class to avoid initialization every time a ServiceClient is created

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed here: 7224651

Co-authored-by: Ramya Rao <ramya.rao.a@outlook.com>
@ramya-rao-a
Copy link
Contributor

Another nit: Is the term resource management endpoint preferred over resource manager endpoints?
https://github.com/Azure/ms-rest-azure-env/blob/master/lib/azureEnvironment.ts#L271 uses the latter

And official docs refer to the latter as well. See https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/overview

@sadasant
Copy link
Contributor Author

@ramya-rao-a I think I’ve addressed all the feedback through this commit: 7224651

lib/serviceClient.ts Outdated Show resolved Hide resolved
Changelog.md Outdated Show resolved Hide resolved
sadasant and others added 2 commits August 18, 2021 15:31
Co-authored-by: Ramya Rao <ramya.rao.a@outlook.com>
Co-authored-by: Ramya Rao <ramya.rao.a@outlook.com>
@sadasant sadasant merged commit 45f89c9 into Azure:master Aug 18, 2021
@sadasant sadasant deleted the fix/azure-sdk-for-js-issues-15945 branch August 18, 2021 20:00
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.

3 participants