Skip to content

InteractiveBrowserCredential does not hand over to next credential in chain if no browser is supported #32276

@stviaene-si

Description

@stviaene-si
  • Package Name: azure.identity
  • Package Version: 1.14.0
  • Operating System: Linux
  • Python Version: 3.11.5

Describe the bug
We use a ChainedTokenCredential containing an InteractiveBrowserCredential and a DeviceCodeCredential to authenticate to Azure. We use this to authenticate on jupyterhub, allowing us to run code locally as well as on a remote kernel. In version 1.12.0, this worked beautifully, but since upgrading to v1.14.0, this now breaks on a remote host: the InteractiveBrowserCredential never seems to hand over to the DeviceCodeCredential if no browser is found, instead raising an exception.

To Reproduce
Run the following on a machine without browser available:

import azure.identity
import azure.storage.blob as az_blob
import datetime
cred = azure.identity.ChainedTokenCredential(
    azure.identity.InteractiveBrowserCredential(timeout=1),
    azure.identity.DeviceCodeCredential(),
)
client = az_blob.BlobServiceClient(
    account_url=f"https://storage_account.blob.core.windows.net",
    credential=cred
)
client.get_user_delegation_key(
    key_start_time=datetime.datetime.now(tz=datetime.UTC), 
    key_expiry_time=datetime.datetime.now(tz=datetime.UTC) + datetime.timedelta(minutes=5)
)

(Here I'm just getting a user delegation key to force authentication. I'm sure there's a simpler way to do this that I'm unaware of.)
This prints warnings about no browser being found, and eventually fails with an exception
(azure.core.exceptions.ClientAuthenticationError: ChainedTokenCredential failed to retrieve a token from the included credentials.).

Expected behavior
If authenticating interactively fails, I expect the ChainedTokenCredential to hand over to the next Credential in the chain. This did happen in v1.12.0, and the code above in this version yields the following output:

InteractiveBrowserCredential.get_token failed: Failed to open a browser
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code xxxxxxxxxx to authenticate.

almost immediately, as soon as no browser is found.

Additional context
The expected behaviour can be emulated by wrapping the InteractiveBrowserCredential in another class that raises a CredentialUnavailableError if authentication fails:

class InteractiveBrowserCredentialWrapper(azure.identity.InteractiveBrowserCredential):
    def get_token(self, *args, **kwargs):
        try:
            return super().get_token(*args, **kwargs)
        except azure.core.exceptions.ClientAuthenticationError as e:
            raise azure.identity._exceptions.CredentialUnavailableError() from e


cred = azure.identity.ChainedTokenCredential(
    InteractiveBrowserCredentialWrapper(timeout=1),
    azure.identity.DeviceCodeCredential(),
)
client = az_blob.BlobServiceClient(
    account_url=f"https://spaceintelliprojects.blob.core.windows.net",
    credential=cred
)
sign_key = client.get_user_delegation_key(
    key_start_time=datetime.datetime.now(tz=datetime.UTC), key_expiry_time=datetime.datetime.now(tz=datetime.UTC) + datetime.timedelta(minutes=5)
)

However, this still waits for the InteractiveBrowserCredential to time out, which isn't ideal.

If this is now the preferred behaviour, it would be nice to have the behaviour on no browser being available being configurable, so backward compatibility can be maintained.

Metadata

Metadata

Assignees

Labels

Azure.IdentityClientThis issue points to a problem in the data-plane of the library.customer-reportedIssues that are reported by GitHub users external to the Azure organization.issue-addressedWorkflow: The Azure SDK team believes it to be addressed and ready to close.questionThe issue doesn't require a change to the product in order to be resolved. Most issues start as that

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions