AzureAD / microsoft-authentication-library-for-python

Microsoft Authentication Library (MSAL) for Python makes it easy to authenticate to Microsoft Entra ID. General docs are available here https://learn.microsoft.com/entra/msal/python/ Stable APIs are documented here https://msal-python.readthedocs.io. Questions can be asked on www.stackoverflow.com with tag "msal" + "python".
https://stackoverflow.com/questions/tagged/azure-ad-msal+python
Other
788 stars 194 forks source link

Support auth code flow in docker container #422

Closed jiasli closed 11 months ago

jiasli commented 2 years ago

Goal

Support we want to support auth code flow in docker container, as opposed to https://github.com/AzureAD/microsoft-authentication-library-for-python/issues/421.

Solution 1: --net=host

--net=host is used to put the container directly in the host network. Unfortunately, as mentioned in https://github.com/AzureAD/microsoft-authentication-library-for-python/pull/309#discussion_r731469989, --net=host doesn't work on platforms other than Linux, like Windows or MacOS:

https://github.com/AzureAD/microsoft-authentication-library-for-python/blob/0deba2e88a9595986282b14ce03a57bc9dc0539c/msal/oauth2cli/authcode.py#L246

So we have to use other approaches.

Solution 2: --publish , -p

I tried to use --publish , -p to expose/map certain port like 8400 to the hosting Windows:

docker run -it -p 8400:8400 -v d:/cli/azure-cli:/root/azure-cli python:3.9 bash
cd /root
python -m venv pyenv
. pyenv/bin/activate
pip install azdev
azdev setup -c azure-cli

However, after running az login, the browser fails with

image

This page isn’t working
localhost didn’t send any data.
ERR_EMPTY_RESPONSE

This is because AuthCodeReceiver only listens to 127.0.0.1:

https://github.com/AzureAD/microsoft-authentication-library-for-python/blob/0deba2e88a9595986282b14ce03a57bc9dc0539c/msal/oauth2cli/authcode.py#L132

As the container runs in bridge network (docker inspect <name>), the IP address is actually

# hostname --ip-address
172.17.0.3

It fails because the IP doesn't match.

This can be cross-checked with

python -m http.server 8400 --bind 127.0.0.1

Now visiting http://127.0.0.1:8400 from the host shows the same error message.

But if we listen to all port:

python -m http.server 8400

http://127.0.0.1:8400 works as expected.

Experiment

After changing https://github.com/AzureAD/microsoft-authentication-library-for-python/blob/0deba2e88a9595986282b14ce03a57bc9dc0539c/msal/oauth2cli/authcode.py#L132

to "", I can successfully do an auth code flow in a docker container!

rayluo commented 2 years ago

Yes, our goal is to facilitate using auth code flow even inside a docker container. the acquire_token_interactive() (of MSAL Python) is specifically designed to allow that, so it is already doing its part. But then it is each different deployment scenario's challenge to somehow route the incoming auth code back to MSAL. The current unfortunate situation that the docker --net=host ... is not (yet?) supported on Windows/Mac is a feature request for docker in its own right.

MSAL Python is even implemented in a way that it would be very easy to expose that address parameter. The question remains as how the calling app would know which IP address to be listened to. We need to investigate whether there is a way for an app running inside docker to reliably detect and choose an ip (such as 172.17.0.3) to use. Unless/until we make some breakthrough in that detection, we don't think MSAL's exposing that address parameter would make any real difference.

jiasli commented 2 years ago

Maybe a parameter like listen_to_all_addresses? This would certainly makes MSAL easier to use in a docker container. Just a wild thought.

rayluo commented 2 years ago

MSAL could expose a parameter like listen_address, so that the MSAL caller can specify ideally one precise IP (such as 172.17.0.3), or specify a 0.0.0.0 which would mean all addresses. (The "listen_" prefix is used to differentiate from the address presented in redirect_uri: They could be confusingly different!)

And then it would in turn become MSAL's downstream app (i.e. Az CLI)'s job to expose a --listen-address string parameter (or you could still choose to do a --listen-to-all-addresses boolean parameter, if you insist to). Perhaps that quirky user experience would be justified by the need to "support auth code flow inside docker container when running on Windows/Mac". We may build a prototype to try it out.

tevino commented 2 years ago

Having the exact situations here.

@jiasli @rayluo Is there a temporary workaround to make acquire_token_interactive() work in a docker container on macOS/Windows?

I would say that even if --net=host is supported on all platforms, an exposed listen_address from msal would be a more fine-grained solution than using a host network.

rayluo commented 2 years ago

I would say that even if --net=host is supported on all platforms, an exposed listen_address from msal would be a more fine-grained solution than using a host network.

If --net=host is supported on all platforms, the current MSAL Python would already work out of the box, and there would be no need to expose listen_address from msal, not until we can articulate a concrete scenario. (In other words, we tend to expose a fine-grained parameter only if it can be justified by enabling a new scenario.)

In reality, --net=host might not be available on macOS or Windows anytime soon, so, yeah, MSAL Python is open to that listen_address idea. Assuming MSAL exposes that parameter, @jiasli, how does Azure CLI plan to utilize it? Is the end user expected to run something like "docker run ... -p 8400:8400 ..." and then "az login --listen-address 0.0.0.0 --port 8400" when running inside a container? The user experience seems suboptimal, I doubt how many users would know it, even after we change the existing docs to point out the different docker commands.

Is there a temporary workaround to make acquire_token_interactive() work in a docker container on macOS/Windows?

For now, you would have to use the Device Flow in MSAL Python, or its application az login --use-device-code.

jiasli commented 2 years ago

@jiasli, how does Azure CLI plan to utilize it? Is the end user expected to run something like "docker run ... -p 8400:8400 ..." and then "az login --listen-address 0.0.0.0 --port 8400" when running inside a container?

Yes. This is the solution I can think of, and indeed, it is suboptimal...

tevino commented 2 years ago

My scenario is doing automated testing in a docker container that requires authentication for SharePoint Online, no need to az login.

This is what I ended up doing with msal==1.16.0, for people who have the same need:

from unittest.mock import patch
import msal.oauth2cli

def overrite_addr(original_class, new_addr):
    orig_init = original_class.__init__
    # Make copy of original __init__, so we can call it without recursion

    def __init__(self, address, *args, **kws):
        port = address[1]
        orig_init(self, (new_addr, port), *args, **kws) # Call the original __init__

    original_class.__init__ = __init__ # Set the class' __init__ to the new one
    return original_class

with patch(target='msal.oauth2cli.authcode._AuthCodeHttpServer',
           new=overrite_addr(msal.oauth2cli.authcode._AuthCodeHttpServer, '0.0.0.0')):
    # This is necessary as long as:
    # 1. msal hard-coded 127.0.0.1 in its source, see: https://github.com/AzureAD/microsoft-authentication-library-for-python/issues/422
    # 2. We are in a docker container on a non-Linux platform (so --net=host is not supported)
    result = app.acquire_token_interactive(scopes=scopes, port=REDIRECT_PORT)  # the port exported by `docker run -v`
rayluo commented 2 years ago

Thanks for sharing your usage, @tevino ! So, you did choose your specific port REDIRECT_PORT=xyz, and you did setup the port forwarding when you start your container "docker run ... -p xyz:xyz ...", didn't you?

Regardless, we will take a close look after holiday, and update this thread accordingly.

tevino commented 2 years ago

@rayluo That's exactly what I did, thanks for taking care of this scenario, and happy holiday! 🎉

rayluo commented 1 year ago

@enerdev2022, have you tried running MSAL's interactive sample inside your Docker which was running on Linux? If so, you should have already seen this built-in hint. Please let us know if that does not work.

bgavrilMS commented 11 months ago

This is a very old thread, marking as answered and will close. Please comment if it needs to be reopened.