Skip to content

Add sample for auxiliary header#35097

Merged
HarveyLink merged 7 commits intoAzure:mainfrom
HarveyLink:minghc-multiTenant
Apr 4, 2023
Merged

Add sample for auxiliary header#35097
HarveyLink merged 7 commits intoAzure:mainfrom
HarveyLink:minghc-multiTenant

Conversation

@HarveyLink
Copy link
Copy Markdown
Member

Contributing to the Azure SDK

Please see our CONTRIBUTING.md if you are not familiar with contributing to this repository or have questions.

For specific information about pull request etiquette and best practices, see this section.

@HarveyLink
Copy link
Copy Markdown
Member Author

Sample for #26719

@azure-sdk
Copy link
Copy Markdown
Collaborator

API change check

API changes are not detected in this pull request.

Copy link
Copy Markdown
Member

@ArcturusZhang ArcturusZhang left a comment

Choose a reason for hiding this comment

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

We should not use var in examples. Please use its actual types or proper interfaces as variable types.

HarveyLink and others added 2 commits April 4, 2023 14:07
@HarveyLink HarveyLink merged commit 58f03c6 into Azure:main Apr 4, 2023
MaryGao added a commit to Azure/azure-sdk-for-js that referenced this pull request May 10, 2023
### Background

Add a policy for external tokens to `x-ms-authorization-auxiliary`
header in core lib. This header will be used when creating a
cross-tenant application we may need to handle authentication requests
for resources that are in different tenants. You can learn [more
here](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/authenticate-multi-tenant).
Here I collect two use cases:

- Create a virtual network peering between virtual networks across
tenants ([see
here](https://learn.microsoft.com/en-us/azure/virtual-network/create-peering-different-subscriptions?tabs=create-peering-portal#cli))
- Share images across tenants ([see
here](https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/share-images-across-tenants))

### Usecase - create a virtual network peering across tenants

We have two subscriptions cross two tenants:
```
subscriptionA = "75d6dc7b-9a8d-4f94-81ce-8a9437f3ce2c" in tenantA
subscriptionB = "92f95d8f-3c67-4124-91c7-8cf07cdbf241" in tenantB 
```
Prepare the app register and grant permission in both subscriptions,
please note we'll have one app register with two service principals in
two tenants.

```
# Create app registration named `appRegisterB` which allows to be used in any orgnaizational directory located in `tenantB`

# Create a service principal for `appRegisterB` in `tenantA` by login url: [https://login.microsoftonline.com/{tenant-id}/adminconsent?client_id={client-id}](https://login.microsoftonline.com/%7Btenant-id%7D/adminconsent?client_id=%7Bclient-id%7D)

# Add roles for `appRegisterB` in both `subscriptionA` and `subscriptionB`
```
Prepare the virtual network in both subscriptions:

```
# Switch to subscription A
az account set -s $subscriptionA 

# Create resource group A
az group create --name myResourceGroupA --location eastus

# Create virtual network A
az network vnet create --name myVnetA --resource-group myResourceGroupA --location eastus --address-prefix 10.0.0.0/16

# Switch to subscription B
az account set -s $subscriptionB 

# Create resource group B
az group create --name myResourceGroupB --location eastus

# Create virtual network B
az network vnet create --name myVnetB --resource-group myResourceGroupB --location eastus --address-prefix 10.1.0.0/16
```

Create a virtual network peering between these two virtual networks. We
could build this peer relationship from myVnetA to myVnetB, or from
myVnetB to myVnetA.

If we build a client under subscriptionB then we could create this peer
from myVnetB to myVnetA with below headers:

| Header name | Description | Example value |
| ----------- | ----------- | ------------ |
| Authorization | Primary token, token got from credentialB | Bearer
<primary-token> |
| x-ms-authorization-auxiliary | Auxiliary tokens, token got from
credentialA | Bearer <auxiliary-token1> |

```typescript
  const tenantA = "c029c2bd-5f77-48fd-b9b8-6dbc7c475125";
  const tenantB = "72f988bf-86f1-41af-91ab-2d7cd011db47";
  const subscriptionB = "92f95d8f-3c67-4124-91c7-8cf07cdbf241";
  const myResourceGroupB = "myResourceGroupB";
  const myVnetB = "myVnetB";
  const virtualNetworkPeeringName = "myVnetA";
  const virtualNetworkPeeringParameters: VirtualNetworkPeering = {
    allowForwardedTraffic: false,
    allowGatewayTransit: false,
    allowVirtualNetworkAccess: true,
    remoteVirtualNetwork: {
      id:
        "/subscriptions/75d6dc7b-9a8d-4f94-81ce-8a9437f3ce2c/resourceGroups/myResourceGroupA/providers/Microsoft.Network/virtualNetworks/myVnetA"
    },
    useRemoteGateways: false
  };
```
### [Preferred] Option 1: Provide an extra policy
`auxiliaryAuthenticationHeaderPolicy`

Provide a new policy `auxiliaryAuthenticationHeaderPolicy` in core, then
customer code could leverage that policy to add auxilary header.

```typescript
async function createPeeringWithPolicy() {
  const credentialA = new DefaultAzureCredential({tenantId: tenantA});
  const credentialB = new DefaultAzureCredential({tenantId: tenantB});
  const client = new NetworkManagementClient(credentialB, subscriptionB,
    {
      // Add the extra policy when building client
      additionalPolicies: [{
        policy: auxiliaryAuthenticationHeaderPolicy({
          credentials: [credentialA],
          scopes: "https://management.core.windows.net//.default"
        }),
        position: "perRetry",
      }]
    });
  const result = await client.virtualNetworkPeerings.beginCreateOrUpdateAndWait(
    myResourceGroupB,
    myVnetB,
    virtualNetworkPeeringName,
    virtualNetworkPeeringParameters,  
  );
  console.log(result);
}
```

### Option 2: Add `auxiliaryTenants` as a client option

Similar the implementation in
[Go](Azure/azure-sdk-for-go#19309), we could
provide an option in `CommonClientOptions`.
```typescript
/**
 * Auxiliary tenant ids which will be used to get token from
 */
auxiliaryTenants?: string[];
```

And then enhance the current bearerTokenAuthenticationPolicy logic to
detect if we have the `auxiliaryTenants` provided, if yes we could
automatically get tokens and add `x-ms-authorization-auxiliary` header
in request. And the customer code would be like:
```typescript
async function createPeeringWithParam() {  
  const credential = new ClientSecretCredential(tenantB, env.clientB, env.secretB, {
    // We would also add allowed tenant list into current credential so that we could get relevant tenant tokens
    additionallyAllowedTenants: [tenantA]
  });
  const client = new NetworkManagementClient(credential, subscriptionB, {
      // If the parameter is provided the bearer policy would append the extra header
      auxiliaryTenants: [tenantA]
    });
  const result = await client.virtualNetworkPeerings.beginCreateOrUpdateAndWait(
    myResourceGroupB,
    myVnetB,
    virtualNetworkPeeringName,
    virtualNetworkPeeringParameters
  );
  console.log(result);
}
```

### Option 3: Add `auxiliaryCredentials` option in
BearerTokenAuthenticationPolicyOptions

Instead of providing new policy we could add a new option in
`BearerTokenAuthenticationPolicyOptions` in original
bearerTokenAuthenticationPolicy. Then in that policy we could detect if
the parameter `auxiliaryCredentials` is provided, if yes append the
header accordingly.

```typescript
/**
 * Provide the auxiliary credentials to get tokens in header x-ms-authorization-auxiliary
 */
auxiliaryCredentials: TokenCredential[];
```

But it would be more complex from customer side, because we add bearer
policy by default so we have to remove that one first and then re-add a
new one.

```typescript
async function createPeeringWithNewBearerPolicy() {
  const credentialA = new ClientSecretCredential(tenantA, clientB, secretB);
  const credentialB = new ClientSecretCredential(tenantB, clientB, secretB);
  const client = new NetworkManagementClient(credentialB, subscriptionB);
  // Build a new policy with auxiliaryCredentials provide
  const customizedBearerPolicy = bearerTokenAuthenticationPolicy({
    credential: credentialB,
    scopes: "https://management.core.windows.net//.default",
    auxiliaryCredentials: [credentialA]
  });
  // Remove the original one
  client.pipeline.removePolicy({
    name: bearerTokenAuthenticationPolicyName
  });
  // Add our new policy
  client.pipeline.addPolicy(customizedBearerPolicy);
  const result = await client.virtualNetworkPeerings.beginCreateOrUpdateAndWait(
    myResourceGroupB,
    myVnetB,
    virtualNetworkPeeringName,
    virtualNetworkPeeringParameters
  );
  console.log(result);
}
```

Simply speaking I prefer the option 1, you could know more
[here](#25270 (comment)).

### Reference
Java: Azure/azure-sdk-for-java#14336
Python: Azure/azure-sdk-for-python#24585
Go: Azure/azure-sdk-for-go#19309
.Net: Azure/azure-sdk-for-net#35097 // Only add
sample, didn't implement in core

---------

Co-authored-by: Jeff Fisher <xirzec@xirzec.com>
minhanh-phan pushed a commit to minhanh-phan/azure-sdk-for-js that referenced this pull request Jun 12, 2023
)

### Background

Add a policy for external tokens to `x-ms-authorization-auxiliary`
header in core lib. This header will be used when creating a
cross-tenant application we may need to handle authentication requests
for resources that are in different tenants. You can learn [more
here](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/authenticate-multi-tenant).
Here I collect two use cases:

- Create a virtual network peering between virtual networks across
tenants ([see
here](https://learn.microsoft.com/en-us/azure/virtual-network/create-peering-different-subscriptions?tabs=create-peering-portal#cli))
- Share images across tenants ([see
here](https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/share-images-across-tenants))

### Usecase - create a virtual network peering across tenants

We have two subscriptions cross two tenants:
```
subscriptionA = "75d6dc7b-9a8d-4f94-81ce-8a9437f3ce2c" in tenantA
subscriptionB = "92f95d8f-3c67-4124-91c7-8cf07cdbf241" in tenantB 
```
Prepare the app register and grant permission in both subscriptions,
please note we'll have one app register with two service principals in
two tenants.

```
# Create app registration named `appRegisterB` which allows to be used in any orgnaizational directory located in `tenantB`

# Create a service principal for `appRegisterB` in `tenantA` by login url: [https://login.microsoftonline.com/{tenant-id}/adminconsent?client_id={client-id}](https://login.microsoftonline.com/%7Btenant-id%7D/adminconsent?client_id=%7Bclient-id%7D)

# Add roles for `appRegisterB` in both `subscriptionA` and `subscriptionB`
```
Prepare the virtual network in both subscriptions:

```
# Switch to subscription A
az account set -s $subscriptionA 

# Create resource group A
az group create --name myResourceGroupA --location eastus

# Create virtual network A
az network vnet create --name myVnetA --resource-group myResourceGroupA --location eastus --address-prefix 10.0.0.0/16

# Switch to subscription B
az account set -s $subscriptionB 

# Create resource group B
az group create --name myResourceGroupB --location eastus

# Create virtual network B
az network vnet create --name myVnetB --resource-group myResourceGroupB --location eastus --address-prefix 10.1.0.0/16
```

Create a virtual network peering between these two virtual networks. We
could build this peer relationship from myVnetA to myVnetB, or from
myVnetB to myVnetA.

If we build a client under subscriptionB then we could create this peer
from myVnetB to myVnetA with below headers:

| Header name | Description | Example value |
| ----------- | ----------- | ------------ |
| Authorization | Primary token, token got from credentialB | Bearer
&lt;primary-token&gt; |
| x-ms-authorization-auxiliary | Auxiliary tokens, token got from
credentialA | Bearer &lt;auxiliary-token1&gt; |

```typescript
  const tenantA = "c029c2bd-5f77-48fd-b9b8-6dbc7c475125";
  const tenantB = "72f988bf-86f1-41af-91ab-2d7cd011db47";
  const subscriptionB = "92f95d8f-3c67-4124-91c7-8cf07cdbf241";
  const myResourceGroupB = "myResourceGroupB";
  const myVnetB = "myVnetB";
  const virtualNetworkPeeringName = "myVnetA";
  const virtualNetworkPeeringParameters: VirtualNetworkPeering = {
    allowForwardedTraffic: false,
    allowGatewayTransit: false,
    allowVirtualNetworkAccess: true,
    remoteVirtualNetwork: {
      id:
        "/subscriptions/75d6dc7b-9a8d-4f94-81ce-8a9437f3ce2c/resourceGroups/myResourceGroupA/providers/Microsoft.Network/virtualNetworks/myVnetA"
    },
    useRemoteGateways: false
  };
```
### [Preferred] Option 1: Provide an extra policy
`auxiliaryAuthenticationHeaderPolicy`

Provide a new policy `auxiliaryAuthenticationHeaderPolicy` in core, then
customer code could leverage that policy to add auxilary header.

```typescript
async function createPeeringWithPolicy() {
  const credentialA = new DefaultAzureCredential({tenantId: tenantA});
  const credentialB = new DefaultAzureCredential({tenantId: tenantB});
  const client = new NetworkManagementClient(credentialB, subscriptionB,
    {
      // Add the extra policy when building client
      additionalPolicies: [{
        policy: auxiliaryAuthenticationHeaderPolicy({
          credentials: [credentialA],
          scopes: "https://management.core.windows.net//.default"
        }),
        position: "perRetry",
      }]
    });
  const result = await client.virtualNetworkPeerings.beginCreateOrUpdateAndWait(
    myResourceGroupB,
    myVnetB,
    virtualNetworkPeeringName,
    virtualNetworkPeeringParameters,  
  );
  console.log(result);
}
```

### Option 2: Add `auxiliaryTenants` as a client option

Similar the implementation in
[Go](Azure/azure-sdk-for-go#19309), we could
provide an option in `CommonClientOptions`.
```typescript
/**
 * Auxiliary tenant ids which will be used to get token from
 */
auxiliaryTenants?: string[];
```

And then enhance the current bearerTokenAuthenticationPolicy logic to
detect if we have the `auxiliaryTenants` provided, if yes we could
automatically get tokens and add `x-ms-authorization-auxiliary` header
in request. And the customer code would be like:
```typescript
async function createPeeringWithParam() {  
  const credential = new ClientSecretCredential(tenantB, env.clientB, env.secretB, {
    // We would also add allowed tenant list into current credential so that we could get relevant tenant tokens
    additionallyAllowedTenants: [tenantA]
  });
  const client = new NetworkManagementClient(credential, subscriptionB, {
      // If the parameter is provided the bearer policy would append the extra header
      auxiliaryTenants: [tenantA]
    });
  const result = await client.virtualNetworkPeerings.beginCreateOrUpdateAndWait(
    myResourceGroupB,
    myVnetB,
    virtualNetworkPeeringName,
    virtualNetworkPeeringParameters
  );
  console.log(result);
}
```

### Option 3: Add `auxiliaryCredentials` option in
BearerTokenAuthenticationPolicyOptions

Instead of providing new policy we could add a new option in
`BearerTokenAuthenticationPolicyOptions` in original
bearerTokenAuthenticationPolicy. Then in that policy we could detect if
the parameter `auxiliaryCredentials` is provided, if yes append the
header accordingly.

```typescript
/**
 * Provide the auxiliary credentials to get tokens in header x-ms-authorization-auxiliary
 */
auxiliaryCredentials: TokenCredential[];
```

But it would be more complex from customer side, because we add bearer
policy by default so we have to remove that one first and then re-add a
new one.

```typescript
async function createPeeringWithNewBearerPolicy() {
  const credentialA = new ClientSecretCredential(tenantA, clientB, secretB);
  const credentialB = new ClientSecretCredential(tenantB, clientB, secretB);
  const client = new NetworkManagementClient(credentialB, subscriptionB);
  // Build a new policy with auxiliaryCredentials provide
  const customizedBearerPolicy = bearerTokenAuthenticationPolicy({
    credential: credentialB,
    scopes: "https://management.core.windows.net//.default",
    auxiliaryCredentials: [credentialA]
  });
  // Remove the original one
  client.pipeline.removePolicy({
    name: bearerTokenAuthenticationPolicyName
  });
  // Add our new policy
  client.pipeline.addPolicy(customizedBearerPolicy);
  const result = await client.virtualNetworkPeerings.beginCreateOrUpdateAndWait(
    myResourceGroupB,
    myVnetB,
    virtualNetworkPeeringName,
    virtualNetworkPeeringParameters
  );
  console.log(result);
}
```

Simply speaking I prefer the option 1, you could know more
[here](Azure#25270 (comment)).

### Reference
Java: Azure/azure-sdk-for-java#14336
Python: Azure/azure-sdk-for-python#24585
Go: Azure/azure-sdk-for-go#19309
.Net: Azure/azure-sdk-for-net#35097 // Only add
sample, didn't implement in core

---------

Co-authored-by: Jeff Fisher <xirzec@xirzec.com>
@ArthurMa1978 ArthurMa1978 added the Mgmt This issue is related to a management package. label Jul 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Mgmt This issue is related to a management package.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants