Skip to content

Commit d325ef0

Browse files
authored
auth add override for auth at tenant (#1253)
* add override for auth at tenant, rather than subscription when both are known * revert validation change, just in case of edge cases unknown
1 parent 712a4bb commit d325ef0

File tree

4 files changed

+60
-11
lines changed

4 files changed

+60
-11
lines changed

sdk/auth/auth.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ func NewAuthorizerFromCredentials(ctx context.Context, c Credentials, api enviro
153153
TenantId: c.TenantID,
154154
AuxTenantIds: c.AuxiliaryTenantIDs,
155155
SubscriptionIdHint: c.AzureCliSubscriptionIDHint,
156+
ForceAuthAtTenant: c.ForceAuthAtTenant,
156157
}
157158
a, err := NewAzureCliAuthorizer(ctx, opts)
158159
if err != nil {

sdk/auth/azure_cli_authorizer.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,16 @@ type AzureCliAuthorizerOptions struct {
3131
// SubscriptionIdHint is the subscription to target when selecting an account with which to obtain an access token
3232
// Used to hint to Azure CLI which of its signed-in accounts it should select, based on apparent access to the subscription.
3333
SubscriptionIdHint string
34+
35+
// ForceAuthAtTenant skips the use of --subscription when authorising. This allows the CLI authorizer to obtain a tenant level token.
36+
// Use with caution. It is recommended to limit the token scope to a Subscription unless multiple subscriptions in the same tenant
37+
// need to be accessed.
38+
ForceAuthAtTenant bool
3439
}
3540

3641
// NewAzureCliAuthorizer returns an Authorizer which authenticates using the Azure CLI.
3742
func NewAzureCliAuthorizer(ctx context.Context, options AzureCliAuthorizerOptions) (Authorizer, error) {
38-
conf, err := newAzureCliConfig(options.Api, options.TenantId, options.AuxTenantIds, options.SubscriptionIdHint)
43+
conf, err := newAzureCliConfig(options)
3944
if err != nil {
4045
return nil, err
4146
}
@@ -96,7 +101,7 @@ func (a *AzureCliAuthorizer) Token(_ context.Context, _ *http.Request) (*oauth2.
96101

97102
// Prefer to specify subscription ID if provided, this hints to Azure CLI which account to use in the event
98103
// that multiple accounts are signed in, and each account has access to a subset of all subscriptions.
99-
if a.SubscriptionIDHint != "" {
104+
if a.SubscriptionIDHint != "" && !a.conf.ForceTenantAuth {
100105
azArgs = append(azArgs, "--subscription", a.conf.SubscriptionIDHint)
101106

102107
// Cannot specify both `--subscription` and `--tenant`
@@ -175,6 +180,9 @@ type azureCliConfig struct {
175180
// TenantID is the required tenant ID for the primary token
176181
TenantID string
177182

183+
// ForceTenantAuth enforces token acquisition at the Tenant, rather than the specified subscription.
184+
ForceTenantAuth bool
185+
178186
// AuxiliaryTenantIDs is an optional list of tenant IDs for which to obtain additional tokens
179187
AuxiliaryTenantIDs []string
180188

@@ -186,12 +194,13 @@ type azureCliConfig struct {
186194
}
187195

188196
// newAzureCliConfig validates the supplied tenant ID and returns a new azureCliConfig.
189-
func newAzureCliConfig(api environments.Api, tenantId string, auxiliaryTenantIds []string, subscriptionIdHint string) (*azureCliConfig, error) {
197+
func newAzureCliConfig(options AzureCliAuthorizerOptions) (*azureCliConfig, error) {
190198
// check az-cli version, ensure that MSAL is supported
191199
if err := azurecli.CheckAzVersion(); err != nil {
192200
return nil, err
193201
}
194202

203+
var tenantId = options.TenantId
195204
// obtain default tenant ID if no tenant ID was provided
196205
if strings.TrimSpace(tenantId) == "" {
197206
if defaultTenantId, err := azurecli.GetDefaultTenantID(); err != nil {
@@ -219,38 +228,39 @@ func newAzureCliConfig(api environments.Api, tenantId string, auxiliaryTenantIds
219228
}
220229

221230
// validate subscriptionIdHint, if applicable (currently only for Resource Manager)
222-
if environments.ApiIsKnownPublished(api, "AzureResourceManager") {
223-
if subscriptionIdHint != "" {
231+
if environments.ApiIsKnownPublished(options.Api, "AzureResourceManager") && !options.ForceAuthAtTenant {
232+
if options.SubscriptionIdHint != "" {
224233
if availableSubscriptionIds, err := azurecli.ListAvailableSubscriptionIDs(); err != nil {
225234
return nil, err
226235
} else if availableSubscriptionIds == nil {
227236
return nil, fmt.Errorf("no available subscription IDs returned by Azure CLI")
228237
} else {
229238
found := false
230239
for _, subId := range *availableSubscriptionIds {
231-
if strings.EqualFold(subId, subscriptionIdHint) {
240+
if strings.EqualFold(subId, options.SubscriptionIdHint) {
232241
found = true
233242
break
234243
}
235244
}
236245
if !found {
237-
return nil, fmt.Errorf("the provided subscription ID %q is not known by Azure CLI", subscriptionIdHint)
246+
return nil, fmt.Errorf("the provided subscription ID %q is not known by Azure CLI", options.SubscriptionIdHint)
238247
}
239248
}
240249
}
241250
}
242251

243252
return &azureCliConfig{
244-
Api: api,
253+
Api: options.Api,
245254
TenantID: tenantId,
246-
AuxiliaryTenantIDs: auxiliaryTenantIds,
255+
AuxiliaryTenantIDs: options.AuxTenantIds,
247256
DefaultSubscriptionID: subscriptionId,
248-
SubscriptionIDHint: strings.ToLower(subscriptionIdHint),
257+
SubscriptionIDHint: strings.ToLower(options.SubscriptionIdHint),
258+
ForceTenantAuth: options.ForceAuthAtTenant,
249259
}, nil
250260
}
251261

252262
// TokenSource provides a source for obtaining access tokens using AzureCliAuthorizer.
253-
func (c *azureCliConfig) TokenSource(ctx context.Context) (Authorizer, error) {
263+
func (c *azureCliConfig) TokenSource(_ context.Context) (Authorizer, error) {
254264
// Cache access tokens internally to avoid unnecessary `az` invocations
255265
return NewCachedAuthorizer(&AzureCliAuthorizer{
256266
TenantID: c.TenantID,

sdk/auth/azure_cli_authorizer_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,42 @@ func TestAccAzureCliAuthorizerWithTenant(t *testing.T) {
114114
}
115115
}
116116

117+
func TestAccAzureCliAuthorizerForceTenant(t *testing.T) {
118+
test.AccTest(t)
119+
120+
ctx := context.Background()
121+
122+
env, err := environments.FromName(test.Environment)
123+
if err != nil {
124+
t.Fatal(err)
125+
}
126+
127+
opts := auth.AzureCliAuthorizerOptions{
128+
Api: env.MicrosoftGraph,
129+
TenantId: test.TenantId,
130+
SubscriptionIdHint: test.SubscriptionId,
131+
ForceAuthAtTenant: true,
132+
}
133+
134+
authorizer, err := auth.NewAzureCliAuthorizer(ctx, opts)
135+
if err != nil {
136+
t.Fatalf("NewAzureCliAuthorizer(): %v", err)
137+
}
138+
139+
cliAuth, err := testCheckAzureCliAuthorizer(authorizer)
140+
if err != nil {
141+
t.Fatal(err)
142+
}
143+
144+
if cliAuth.TenantID != test.TenantId {
145+
t.Fatalf("cliAuth.TenantID has unexpected value %q", cliAuth.TenantID)
146+
}
147+
148+
if _, err = testObtainAccessToken(ctx, authorizer); err != nil {
149+
t.Fatal(err)
150+
}
151+
}
152+
117153
func testCheckAzureCliAuthorizer(authorizer auth.Authorizer) (*auth.AzureCliAuthorizer, error) {
118154
if authorizer == nil {
119155
return nil, fmt.Errorf("authorizer is nil, expected Authorizer")

sdk/auth/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ type Credentials struct {
1818
ClientID string
1919
// TenantID specifies the Azure Active Directory Tenant to connect to, which must be a valid UUID.
2020
TenantID string
21+
// ForceAuthAtTenant forces the Authoriser to obtain tokens at the Tenant scope rather than limiting to Subscription - Currently only meaningful for CLI based authentication
22+
ForceAuthAtTenant bool
2123

2224
// EnableAuthenticatingUsingAzureCLI specifies whether Azure CLI authentication should be checked.
2325
EnableAuthenticatingUsingAzureCLI bool

0 commit comments

Comments
 (0)