Git Product home page Git Product logo

azuread / microsoft-authentication-extensions-for-python Goto Github PK

View Code? Open in Web Editor NEW
27.0 42.0 28.0 160 KB

Microsoft Authentication Library extensions (MSAL EX) provides a persistence API that can save your data on disk, encrypted on Windows, macOS and Linux. Concurrent data access will be coordinated by a file lock mechanism.

License: MIT License

Python 97.06% Dockerfile 2.08% Shell 0.86%

microsoft-authentication-extensions-for-python's Introduction

Microsoft Authentication Extensions for Python

The Microsoft Authentication Extensions for Python offers secure mechanisms for client applications to perform cross-platform token cache serialization and persistence. It gives additional support to the Microsoft Authentication Library for Python (MSAL).

MSAL Python supports an in-memory cache by default and provides the SerializableTokenCache to perform cache serialization. You can read more about this in the MSAL Python documentation. Developers are required to implement their own cache persistance across multiple platforms and Microsoft Authentication Extensions makes this simpler.

The supported platforms are Windows, Mac and Linux.

  • Windows - DPAPI is used for encryption.
  • MAC - The MAC KeyChain is used.
  • Linux - LibSecret is used for encryption.

Note: It is recommended to use this library for cache persistance support for Public client applications such as Desktop apps only. In web applications, this may lead to scale and performance issues. Web applications are recommended to persist the cache in session. Take a look at this webapp sample.

Installation

You can find Microsoft Authentication Extensions for Python on Pypi.

  1. If you haven't already, install and/or upgrade the pip of your Python environment to a recent version. We tested with pip 18.1.
  2. Run pip install msal-extensions.

Versions

This library follows Semantic Versioning.

You can find the changes for each version under Releases.

Usage

Creating an encrypted token cache file to be used by MSAL

The Microsoft Authentication Extensions library provides the PersistedTokenCache which accepts a platform-dependent persistence instance. This token cache can then be used to instantiate the PublicClientApplication in MSAL Python.

The token cache includes a file lock, and auto-reload behavior under the hood.

Here is an example of this pattern for multiple platforms (taken from the complete sample here):

def build_persistence(location, fallback_to_plaintext=False):
    """Build a suitable persistence instance based your current OS"""
    try:
        return build_encrypted_persistence(location)
    except:
        if not fallback_to_plaintext:
            raise
        logging.warning("Encryption unavailable. Opting in to plain text.")
        return FilePersistence(location)

persistence = build_persistence("token_cache.bin")
print("Type of persistence: {}".format(persistence.__class__.__name__))
print("Is this persistence encrypted?", persistence.is_encrypted)

cache = PersistedTokenCache(persistence)

Now you can use it in an MSAL application like this:

app = msal.PublicClientApplication("my_client_id", token_cache=cache)

Creating an encrypted persistence file to store your own data

Here is an example of this pattern for multiple platforms (taken from the complete sample here):

def build_persistence(location, fallback_to_plaintext=False):
    """Build a suitable persistence instance based your current OS"""
    try:
        return build_encrypted_persistence(location)
    except:  # pylint: disable=bare-except
        if not fallback_to_plaintext:
            raise
        logging.warning("Encryption unavailable. Opting in to plain text.")
        return FilePersistence(location)

persistence = build_persistence("storage.bin", fallback_to_plaintext=False)
print("Type of persistence: {}".format(persistence.__class__.__name__))
print("Is this persistence encrypted?", persistence.is_encrypted)

data = {  # It can be anything, here we demonstrate an arbitrary json object
    "foo": "hello world",
    "bar": "",
    "service_principle_1": "blah blah...",
    }

persistence.save(json.dumps(data))
assert json.loads(persistence.load()) == data

Python version support policy

Python versions which are 6 months older than their end-of-life cycle defined by Python Software Foundation (PSF) will not receive new feature updates from this library.

Community Help and Support

We leverage Stack Overflow to work with the community on supporting Azure Active Directory and its SDKs, including this one! We highly recommend you ask your questions on Stack Overflow (we're all on there!). Also browse existing issues to see if someone has had your question before.

We recommend you use the "msal" tag so we can see it! Here is the latest Q&A on Stack Overflow for MSAL: http://stackoverflow.com/questions/tagged/msal

Contributing

All code is licensed under the MIT license and we triage actively on GitHub.

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

We value and adhere to the Microsoft Open Source Code of Conduct

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

microsoft-authentication-extensions-for-python's People

Contributors

abhidnya13 avatar akx avatar bluca avatar chlowell avatar edgarrmondragon avatar greysteil avatar jhutchings1 avatar jiasli avatar marstr avatar microsoftopensource avatar mparry avatar msftgits avatar navyasric avatar rayluo avatar rlrossiter avatar tonybaloney avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

microsoft-authentication-extensions-for-python's Issues

API for testing whether persistence is available

It would be helpful to have an API for silently testing whether a persistence method is available, in particular LibsecretPersistence. The best way today is to attempt to instantiate the persistence, which logs an error on Linux when PyGObject isn't installed:

Runtime dependency of PyGObject is missing.
Depends on your Linux distro, you could install it system-wide by something like:
    sudo apt install python3-gi python3-gi-cairo gir1.2-secret-1
If necessary, please refer to PyGObject's doc:
https://pygobject.readthedocs.io/en/latest/getting_started.html

It's a reasonable thing to log but an application developer using msal-extensions through another library may interpret it to mean that mid-tier library requires PyGObject (see e.g. Azure/azure-sdk-for-python/issues/12152 and Azure/azure-sdk-for-python/issues/19857).

Investigate OS errors returned for encryption scenarios on Windows and MacOS

Cache file previously used with MSAL already exists before using MSAL Extensions for the very first time.

For macOS:

Keychain Error: ITEM_NOT_FOUND was returned as service name and account name entry is not found

For windows:

If the file has no data: WinError 87: The parameter is incorrect - is returned.
If the file has cache stored in previous MSAL cache format(not encrypted) - returns WinError 13- The data is invalid


There is another WinError that is currently raised at this point : OSError: [WinError -2146893813] : '' for which the cause is unknown. Have not been able to reproduce it yet. Adding the stack trace here:

Traceback (most recent call last):
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Users\kinshu\AppData\Local\Programs\Python\Python37\Scripts\bonsai.exe\__main__.py", line 7, in <module>
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\bonsai_cli\bonsai.py", line 3457, in main
    cli()
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\click-7.0-py3.7.egg\click\core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\click-7.0-py3.7.egg\click\core.py", line 717, in main
    rv = self.invoke(ctx)
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\click-7.0-py3.7.egg\click\core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\click-7.0-py3.7.egg\click\core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\click-7.0-py3.7.egg\click\core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\click-7.0-py3.7.egg\click\decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\bonsai_cli\bonsai.py", line 3254, in configure
    bonsai_config = Config(use_aad=True, require_workspace=False)
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\bonsai_cli\config.py", line 215, in __init__
    self.accesskey = self.aad_client.get_access_token()
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\bonsai_cli\aad.py", line 109, in get_access_token
    token = self._get_access_token_from_cache()
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\bonsai_cli\aad.py", line 96, in _get_access_token_from_cache
    accounts = self._app.get_accounts()
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\msal-1.0.0-py3.7.egg\msal\application.py", line 308, in get_accounts
    accounts = self._find_msal_accounts(environment=self.authority.instance)
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\msal-1.0.0-py3.7.egg\msal\application.py", line 328, in _find_msal_accounts
    TokenCache.CredentialType.ACCOUNT, query={"environment": environment})
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\msal_extensions\token_cache.py", line 55, in find
    self._reload_if_necessary()
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\msal_extensions\token_cache.py", line 36, in _reload_if_necessary
    self.deserialize(self._persistence.load())
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\msal_extensions\persistence.py", line 124, in load
    return self._dp_agent.unprotect(handle.read())
  File "c:\users\kinshu\appdata\local\programs\python\python37\lib\site-packages\msal_extensions\windows.py", line 111, in unprotect
    raise OSError(256, '', '', err_code)
OSError: [WinError -2146893813] : '' ```

Add license and SCM to setup.py

Could you please add a license and source code declaration to the setup.py file?

Currently msal-extensions causes a violation in our OSS check as these license and source code location fields are missing for this package on https://pypi.org/project/msal-extensions/.
Not a Python guy but believe this can be fixed by adding the following to https://github.com/AzureAD/microsoft-authentication-extensions-for-python/blob/dev/setup.py#L16-L35:

license='MIT',
classifiers=[
       'Development Status :: 2 - Pre-Alpha',
       'License :: OSI Approved :: MIT License '
],
project_urls={
        'Source': 'https://github.com/AzureAD/microsoft-authentication-extensions-for-python.git'
},

Please remove cryptography upper version dependency

msal-extensions has a requirement of cryptography <4, but currently cryptography changed the versioning strategy, latest version being 35.0.0.

Due to this my project always tries to install cryptography 3.4.8 and fails.

- msal-extensions [required: ~=0.3.0, installed: 0.3.0]
    - msal [required: >=0.4.1,<2.0.0, installed: 1.12.0]
      - cryptography [required: >=0.6,<4, installed: 3.4.7]
        - cffi [required: >=1.12, installed: 1.14.5]
          - pycparser [required: Any, installed: 2.20]
      - PyJWT [required: >=1.0.0,<3, installed: 2.1.0]
      - requests [required: >=2.0.0,<3, installed: 2.25.1]
        - certifi [required: >=2017.4.17, installed: 2021.5.30]
        - chardet [required: >=3.0.2,<5, installed: 4.0.0]
        - idna [required: >=2.5,<3, installed: 2.10]
        - urllib3 [required: >=1.21.1,<1.27, installed: 1.26.5]

Token cache persistence fails on Windows if home dir is drive root

If you have your home directory on Windows set to be the root of a drive, e.g. HOME=H:\, then the token cache persistence fails with an EACCES permissions error:

  File "lib\site-packages\msal_extensions\token_cache.py", line 75, in __init__
    FilePersistenceWithDataProtection(cache_location, entropy=entropy))
  File "lib\site-packages\msal_extensions\persistence.py", line 159, in __init__
    super(FilePersistenceWithDataProtection, self).__init__(location)
  File "lib\site-packages\msal_extensions\persistence.py", line 103, in __init__
    _mkdir_p(os.path.dirname(self._location))
  File "lib\site-packages\msal_extensions\persistence.py", line 40, in _mkdir_p
    os.makedirs(path)
  File "lib\os.py", line 223, in makedirs
    mkdir(name, mode)
PermissionError: [WinError 5] Access is denied: 'H:\\'

The simplest/best solution would be to update the persistence._mkdir_p() implementation to look like this instead:

def _mkdir_p(path):
    if path:
        os.makedirs(path, exist_ok=True)

However, this requires Python โ‰ฅ3.2. Considering that Python 2 was EOL at the start of 2020, this seems reasonable to me! Clearly, you might not wish to do so just for such a relatively small issue.

Alternatively, either the code could call the above only if Python is new enough (and so the bug persists under Python 2) or else it would presumably be necessary to implement roughly the same bug fix as they did in Python some years ago: https://bugs.python.org/issue25583

Lic not exposed in metadata in 0.3.0

Looks like the License info is not correctly exposed here.

[lic for lic in list(pkg_resources.require('msal')[0].get_metadata_lines('METADATA')) if lic.startswith('License')]

['License: MIT', 'License-File: LICENSE']

[lic for lic in list(pkg_resources.require('msal-extensions')[0].get_metadata_lines('METADATA')) if lic.startswith('License')]

['License: UNKNOWN']

Thanks

Create a GNOME Keyring Token Cache (Linux only)

Linux has a few solutions for protecting secrets. For people using the Desktop Environment(DE) GNOME, GNOME Keyring is the natural choice. While it is not as ubiquitous as DP API for Windows users or KeyChain for MacOS users, it is a good way to get fairly significant coverage of Linux developers.

Just like #1 and #3, we should implement a class to facilitate interactions with GNOME Keyring that extends SerializableTokenCache. However, unlike those platforms, as of time of writing there is no .NET support for this secret backend.

Researching a little this afternoon, it seems like it is not unprecedented for folks who are not using GNOME as their DE to take a dependency on just this package. This may prove useful for us when running in containers, or other Linux environments that have no DE whatsoever. Experimentally, I was able to install gnome-keyring with only standard repositories in both an ubuntu:disco and centos7 container.

Create MSI Token Provider

Managed Service Identity is a mechanism for establishing identity based on the infrastructure that an application is being run on. The off-shoot is that if we are inspecting the inspecting for ambient credentials (as in #7,) we should absolutely include looking for MSI as a token provider.

MSAL extensions fails in Anaconda linux environment

I filed an issue in the msal conda-forge feedstock about this
conda-forge/msal_extensions-feedstock#10 (comment)

We're seeing a failure on Azure Machine Learning computes. I can also repro this in other Ubuntu environments.
Trying to use msal_extensions with an encrypted token cache fails as follows:

  • initially it fails with the missing gi dependency
  • conda install pygobject fixes that but then fails on `gi.require_version("Secret", "1")
  • installing gir1.2-secret-1 has no effect on this.
  • conda install libsecret also doesn't fix this.
  • manually (apt) installing gir1.2-secret-1 allows this to work in the system python3 but not in conda

The following repros on Ubuntu 18.04.6 LTS and Ubuntu 22.04 LTS

Conda behavior

Python 3.10.4 | packaged by conda-forge | (main, Mar 24 2022, 17:39:04) [GCC 10.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import gi
>>> gi.require_version("Secret", "1")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ian/anaconda3/envs/libsecret/lib/python3.10/site-packages/gi/__init__.py", line 126, in require_version
    raise ValueError('Namespace %s not available' % namespace)
ValueError: Namespace Secret not available

non-conda Python behavior

(libsecret) ian@IanHelle-dev2:/mnt/e/src$ conda deactivate
(base) ian@IanHelle-dev2:/mnt/e/src$ conda deactivate
ian@IanHelle-dev2:/mnt/e/src$ python3
Python 3.10.4 (main, Apr  2 2022, 09:04:19) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import gi
>>> gi.require_version("Secret", "1")
>>>

KeyChainError while authenticating using MSAL in from Mac OS

I work for Microsoft for Project Bonsai(AI & Research) team. I get the below error on my mac, when I try to run "bonsai brain create" command. We use msal package.

virtualenv) apuvvadiworkmacs-MacBook-Pro:brain apuvvadi$ bonsai brain create -n aniltest
No access token found in cache, please sign in.
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code FTHHCMWHX to authenticate.
Traceback (most recent call last):
File "/Users/apuvvadi/work/virtualenv/bin/bonsai", line 33, in
sys.exit(load_entry_point('bonsai-cli', 'console_scripts', 'bonsai')())
File "/Users/apuvvadi/brain/src/Services/bonsaicli2/bonsai_cli/commands/bonsai.py", line 278, in main
cli()
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/click/core.py", line 829, in call
return self.main(*args, **kwargs)
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/click/core.py", line 782, in main
rv = self.invoke(ctx)
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/click/core.py", line 1259, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/click/core.py", line 1259, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/click/core.py", line 610, in invoke
return callback(*args, **kwargs)
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/click/decorators.py", line 21, in new_func
return f(get_current_context(), *args, **kwargs)
File "/Users/apuvvadi/brain/src/Services/bonsaicli2/bonsai_cli/commands/brain.py", line 72, in create_brain
response = api(use_aad=True).create_brain(
File "/Users/apuvvadi/brain/src/Services/bonsaicli2/bonsai_cli/utils.py", line 32, in api
bonsai_config = Config(argv=sys.argv, use_aad=use_aad)
File "/Users/apuvvadi/brain/src/Services/bonsaicli2/bonsai_cli/config.py", line 127, in init
self.accesskey = self.aad_client.get_access_token()
File "/Users/apuvvadi/brain/src/Services/bonsaicli2/bonsai_cli/aad.py", line 129, in get_access_token
self._log_in_with_device_code()
File "/Users/apuvvadi/brain/src/Services/bonsaicli2/bonsai_cli/aad.py", line 95, in _log_in_with_device_code
return self._app.acquire_token_by_device_flow(flow)
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal/application.py", line 902, in acquire_token_by_device_flow
return self.client.obtain_token_by_device_flow(
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal/oauth2cli/oauth2.py", line 348, in obtain_token_by_device_flow
result = self._obtain_token_by_device_flow(flow, **kwargs)
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal/oauth2cli/oauth2.py", line 310, in _obtain_token_by_device_flow
result = self._obtain_token(
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal/oauth2cli/oidc.py", line 89, in _obtain_token
ret = super(Client, self)._obtain_token(grant_type, *args, **kwargs)
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal/oauth2cli/oauth2.py", line 485, in _obtain_token
(on_obtaining_tokens or self.on_obtaining_tokens)({
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal/application.py", line 298, in
on_obtaining_tokens=lambda event: self.token_cache.add(dict(
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal/token_cache.py", line 290, in add
super(SerializableTokenCache, self).add(event, **kwargs)
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal/token_cache.py", line 113, in add
return self.__add(event, now=now)
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal/token_cache.py", line 173, in __add
self.modify(self.CredentialType.ACCESS_TOKEN, at, at)
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal_extensions/token_cache.py", line 49, in modify
self._persistence.save(self.serialize())
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal_extensions/persistence.py", line 208, in save
locker.set_generic_password(
File "/Users/apuvvadi/work/virtualenv/lib/python3.8/site-packages/msal_extensions/osx.py", line 216, in set_generic_password
raise KeychainError(exit_status=modify_exit_status)

Create Secret Backend Mux

Folks should have a platform-independent interface for getting the most appropriate Token Cache. This will save customers from needing to understand that they are choosing between implementations of types noted in #1, #3, or #4. This likely just looks like a function named get_protected_token_cache or similar.

The cache lock cannot be removed on Windows 10 if placed inside the user home directory.

I'm trying to create a TokenCache on Windows 10 and store a cache at

~/.myapp/token.cache

As a result the lockfile is created at:

~/.myapp/token.cache.lockfile

However this lockfile is not removed in CrossPlatLock.__exit__ and it is completely blocked by the operationg system (no visible process) untill the further reboot. It looks like the file is not yet unlocked when being removed (I may be wrong) creating some kind of a hanging file handle.

Here is a simple way to reproduce:

path = os.path.expanduser('~/.myapp/test.lockfile')
with CrossPlatLock(path):
    print('hi')
# One more time
with CrossPlatLock(path):
    print('hi')
    
<ipython-input-117-002d28c082a3> in <module>                                           
----> 1 with CrossPlatLock(path):                                                      
      2     print('hi')                                                                
      3                                                                                
                                                                                       
<ipython-input-116-89428a9dd969> in __enter__(self)                                    
     11         pid = os.getpid()                                                      
     12                                                                                
---> 13         self._fh = open(self._lockpath, 'wb+', buffering=0)                    
     14         portalocker.lock(self._fh, portalocker.LOCK_EX)                        
     15         self._fh.write('{} {}'.format(pid, sys.argv[0]).encode('utf-8'))       
                                                                                       
PermissionError: [Errno 13] Permission denied: 'C:\\Users\\XXXXYYY/.myapp/test.lockfile'

Explicitly unlocking the file before closing it solves the issue:

portalocker.unlock(self._fh)

I know that portalocker claims that the explicit unlock is not necessary, but would it be possible to add it to prevent the problem?

Non re-entrant lock blocking extension use with msal>0.4.0

Repro:

  1. Clone this repository on MacOS or Windows
  2. Create new virtual environment
  3. Run pip install, ensure pip list | grep msal (or similar command outside of bash) is greater or equal to 0.4.1.
  4. Run pytest

Expected:

  • Tests will pass

Actual:

  • Tests hang indefinitely.

What happened is that in 8a7495a006eafd75179c0975b7fc52fddf733ca5 the add function got refactored to call self.modify. In doing so, because of the inheritance involved, it is calling our FileTokenCache's implementation of these functions and causing us to try to take a second lock on a file we're already locked on. This is causing an indefinite hang.

Amusingly, this is the exact problem talked about here: https://www.youtube.com/watch?v=YXiaWtc0cgE

getting "distutils Version classes are deprecated" warnings

I am getting warning messages

  C:\python\lib\site-packages\msal_extensions\cache_lock.py:24: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    if LooseVersion(portalocker.__version__) >= LooseVersion("1.4.0") else {})

when using disutils '3.8.6'
would be great if the package could be changed to not get this warning anymore.

Error installing PyGObject

Hello

trying to install the species on an Azure Machine Learning compute and when trying to install PyGObject I'm getting the following error
Collecting PyGObject
Using cached PyGObject-3.42.0.tar.gz (716 kB)
Installing build dependencies ... done
Getting requirements to build wheel ... done
Preparing metadata (pyproject.toml) ... done
Collecting pycairo>=1.16.0
Using cached pycairo-1.20.1-cp38-cp38-linux_x86_64.whl
Building wheels for collected packages: PyGObject
Building wheel for PyGObject (pyproject.toml) ... error
ERROR: Command errored out with exit status 1:
command: /anaconda/envs/azureml_py38/bin/python /anaconda/envs/azureml_py38/lib/python3.8/site-packages/pip/_vendor/pep517/in_process/_in_process.py build_wheel /tmp/tmpd52f9ztn
cwd: /tmp/pip-install-iel8t1tb/pygobject_76eaba687ac2447b82495d5cbb6e1473
Complete output (90 lines):
running bdist_wheel
running build
running build_py
creating build
creating build/lib.linux-x86_64-3.8
creating build/lib.linux-x86_64-3.8/pygtkcompat
copying pygtkcompat/pygtkcompat.py -> build/lib.linux-x86_64-3.8/pygtkcompat
copying pygtkcompat/generictreemodel.py -> build/lib.linux-x86_64-3.8/pygtkcompat
copying pygtkcompat/init.py -> build/lib.linux-x86_64-3.8/pygtkcompat
creating build/lib.linux-x86_64-3.8/gi
copying gi/_constants.py -> build/lib.linux-x86_64-3.8/gi
copying gi/_propertyhelper.py -> build/lib.linux-x86_64-3.8/gi
copying gi/types.py -> build/lib.linux-x86_64-3.8/gi
copying gi/_error.py -> build/lib.linux-x86_64-3.8/gi
copying gi/_signalhelper.py -> build/lib.linux-x86_64-3.8/gi
copying gi/_gtktemplate.py -> build/lib.linux-x86_64-3.8/gi
copying gi/pygtkcompat.py -> build/lib.linux-x86_64-3.8/gi
copying gi/importer.py -> build/lib.linux-x86_64-3.8/gi
copying gi/_ossighelper.py -> build/lib.linux-x86_64-3.8/gi
copying gi/docstring.py -> build/lib.linux-x86_64-3.8/gi
copying gi/module.py -> build/lib.linux-x86_64-3.8/gi
copying gi/_option.py -> build/lib.linux-x86_64-3.8/gi
copying gi/init.py -> build/lib.linux-x86_64-3.8/gi
creating build/lib.linux-x86_64-3.8/gi/repository
copying gi/repository/init.py -> build/lib.linux-x86_64-3.8/gi/repository
creating build/lib.linux-x86_64-3.8/gi/overrides
copying gi/overrides/GObject.py -> build/lib.linux-x86_64-3.8/gi/overrides
copying gi/overrides/Gdk.py -> build/lib.linux-x86_64-3.8/gi/overrides
copying gi/overrides/keysyms.py -> build/lib.linux-x86_64-3.8/gi/overrides
copying gi/overrides/Gtk.py -> build/lib.linux-x86_64-3.8/gi/overrides
copying gi/overrides/GIMarshallingTests.py -> build/lib.linux-x86_64-3.8/gi/overrides
copying gi/overrides/Gio.py -> build/lib.linux-x86_64-3.8/gi/overrides
copying gi/overrides/GdkPixbuf.py -> build/lib.linux-x86_64-3.8/gi/overrides
copying gi/overrides/Pango.py -> build/lib.linux-x86_64-3.8/gi/overrides
copying gi/overrides/GLib.py -> build/lib.linux-x86_64-3.8/gi/overrides
copying gi/overrides/init.py -> build/lib.linux-x86_64-3.8/gi/overrides
running build_ext
pycairo: new API
Traceback (most recent call last):
File "/anaconda/envs/azureml_py38/lib/python3.8/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 363, in
main()
File "/anaconda/envs/azureml_py38/lib/python3.8/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 345, in main
json_out['return_val'] = hook(**hook_input['kwargs'])
File "/anaconda/envs/azureml_py38/lib/python3.8/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 261, in build_wheel
return _build_backend().build_wheel(wheel_directory, config_settings,
File "/tmp/pip-build-env-mgboatwc/overlay/lib/python3.8/site-packages/setuptools/build_meta.py", line 230, in build_wheel
return self._build_with_temp_dir(['bdist_wheel'], '.whl',
File "/tmp/pip-build-env-mgboatwc/overlay/lib/python3.8/site-packages/setuptools/build_meta.py", line 215, in _build_with_temp_dir
self.run_setup()
File "/tmp/pip-build-env-mgboatwc/overlay/lib/python3.8/site-packages/setuptools/build_meta.py", line 267, in run_setup
super(_BuildMetaLegacyBackend,
File "/tmp/pip-build-env-mgboatwc/overlay/lib/python3.8/site-packages/setuptools/build_meta.py", line 158, in run_setup
exec(compile(code, file, 'exec'), locals())
File "setup.py", line 1259, in
main()
File "setup.py", line 1219, in main
setup(
File "/tmp/pip-build-env-mgboatwc/overlay/lib/python3.8/site-packages/setuptools/init.py", line 159, in setup
return distutils.core.setup(**attrs)
File "/anaconda/envs/azureml_py38/lib/python3.8/distutils/core.py", line 148, in setup
dist.run_commands()
File "/anaconda/envs/azureml_py38/lib/python3.8/distutils/dist.py", line 966, in run_commands
self.run_command(cmd)
File "/anaconda/envs/azureml_py38/lib/python3.8/distutils/dist.py", line 985, in run_command
cmd_obj.run()
File "/tmp/pip-build-env-mgboatwc/overlay/lib/python3.8/site-packages/wheel/bdist_wheel.py", line 299, in run
self.run_command('build')
File "/anaconda/envs/azureml_py38/lib/python3.8/distutils/cmd.py", line 313, in run_command
self.distribution.run_command(command)
File "/anaconda/envs/azureml_py38/lib/python3.8/distutils/dist.py", line 985, in run_command
cmd_obj.run()
File "/anaconda/envs/azureml_py38/lib/python3.8/distutils/command/build.py", line 135, in run
self.run_command(cmd_name)
File "/anaconda/envs/azureml_py38/lib/python3.8/distutils/cmd.py", line 313, in run_command
self.distribution.run_command(command)
File "/anaconda/envs/azureml_py38/lib/python3.8/distutils/dist.py", line 985, in run_command
cmd_obj.run()
File "setup.py", line 1088, in run
self._setup_extensions()
File "setup.py", line 1083, in _setup_extensions
add_pycairo(gi_cairo_ext)
File "setup.py", line 1066, in add_pycairo
ext.include_dirs += [get_pycairo_include_dir()]
File "setup.py", line 893, in get_pycairo_include_dir
include_dir = find_path(find_new_api())
File "setup.py", line 848, in find_new_api
import cairo
File "/tmp/pip-build-env-mgboatwc/overlay/lib/python3.8/site-packages/cairo/init.py", line 1, in
from ._cairo import * # noqa: F401,F403
ImportError: /tmp/pip-build-env-mgboatwc/overlay/lib/python3.8/site-packages/cairo/_cairo.cpython-38-x86_64-linux-gnu.so: undefined symbol: cairo_svg_surface_set_document_unit

ERROR: Failed building wheel for PyGObject
Failed to build PyGObject
ERROR: Could not build wheels for PyGObject, which is required to install pyproject.toml-based projects

I have pycairo installed successfully. and I'm not sure what is causing the issue.

Provide a default persistence per OS

In 0.2.0 the simple TokenCache interface was replaced by newer API that now requires the user to write some code along these lines:

def build_persistence(location, fallback_to_plaintext=False):
    """Build a suitable persistence instance based your current OS"""
    if sys.platform.startswith('win'):
        return FilePersistenceWithDataProtection(location)
    if sys.platform.startswith('darwin'):
        return KeychainPersistence(location, "my_service_name", "my_account_name")
    if sys.platform.startswith('linux'):
        try:
            return LibsecretPersistence(
                location,
                schema_name="my_schema_name",
                attributes={"my_attr1": "foo", "my_attr2": "bar"},
                )
        except:  # pylint: disable=bare-except
            if not fallback_to_plaintext:
                raise
            logging.exception("Encryption unavailable. Opting in to plain text.")
    return FilePersistence(location)

cache = PersistedTokenCache(build_persistence(...))

(That's taken from the README, of course.)

Is there a reason why this logic needs to be reproduced in all user code that doesn't have more particular requirements? Or, conversely, are there many situation where this wouldn't be good enough?

If not, couldn't the library provide a get_default_persistence(location) method, that contains something along the lines of the above? I would be happy to raise a PR for this.

Windows-style line ending all over the place

The diff between 0.3.1 and 1.0.0 contains a lot of Windows-style line endings (CRLF) in many places.

Please consider enforcing LF-only line endings, eg, add a .gitattributes with:

* text=auto eol=lf

Developers on Windows who still need CRLF line endings can get them added automatically, and removed on commit, transparently for all repositories, by setting:

git config --global core.autocrlf true

Create Token Cache Provider

If an MSAL Token cache has been serialized into a well-known location, and the current user has access to it, this Token Provider should be able to reach-into that cache and get a token with the appropriate scopes for a user.

Python3 ODBC Driver 13 for SQL Server SSL Provider Error

Error response trying to use Azure Active Directory password with Azure SQLServer and looking forward could get some support.

  • System details: Ubuntu 16.04.3 LTS (GNU/Linux 4.15.0-1039-azure x86_64)

  • Error Response:

'08001', '[08001] [unixODBC][Microsoft][ODBC Driver 13 for SQL Server]SSL Provider: [error:140A90F1:lib(20):func(169):reason(241)] (-1) (SQLDriverConnect)')

Not seeing any further explicit error message, and below is our sample code used:

import pyodbc;
try:
    connection_string = 'Driver={ODBC Driver 13 for SQL Server};Server={Server},1433;Database={db};Uid={userName};Pwd={passWord};Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;Authentication=ActiveDirectoryPassword'
    cnxn = pyodbc.connect(connection_string)
    cursor = cnxn.cursor()
    cursor.execute('SELECT TOP 20 * from dbo.USERDETAIL');
    row = cursor.fetchone()
    while row:
         print(str(row[0]) + " " + str(row[1]))
         row = cursor.fetchone()
         print("INFORMATION")
    #except pyodbc.ProgrammingError as ex:
    except Exception as ex:
    #print("=====pyodbc.Error====",ex.args[1])
    print("=====Error ex.args[0]===",ex)

PersistedTokenCache.deserialize doesn't persist data

PersistedTokenCache.deserialize replaces an instance's in-memory data entirely but the new data isn't persisted until modify is called. This introduces a couple opportunities for data loss:

  1. if modify is never called, the data is never persisted
  2. if find is called immediately after deserialize, PersistedTokenCache will overwrite its deserialized data when the persistence class returns a modified time later than the last sync, or any time at all if the last sync time is still 0

For example:

from msal import SerializableTokenCache
from msal_extensions import FilePersistence, PersistedTokenCache

PATH = "file_persistence"
with open(PATH, "w"):
    pass

cache_a = SerializableTokenCache()
cache_a.add({"scope": ["scope"], "response": {"access_token": "...", "client_id": "..."}})

cache_b = PersistedTokenCache(persistence=FilePersistence(PATH))
cache_b.deserialize(cache_a.serialize())  # cache_b has new data in memory but doesn't persist it
print("after deserialize: {}".format(cache_b._cache))

# cache_b.find deserializes the empty cache file, replacing data deserialized from cache_a
cache_b.find("AccessToken")
print("after find: {}".format(cache_b._cache))  # cache_b is now empty

Create a Kernel Keyring Token Cache (Linux only)

When Linux Desktop is unavailable, in a nutshell, that dbus is missing, we should use kernel keyring e.g. generate a symmetric encryption key, save it into key-ring, and use it encrypt the tokens. Saving token directly into keyring is not feasible as the keyring has the size limitation

Create CrossPlatLock Analog

The .NET library relies on taking write-only locks on a file separate used as a semaphore to ensure that Cache operations do not race each other. That logic is found here: CrossPlatLock class

This is needed before any of the platform specific Token Caches, such as the one represented in #1, can be said to be truly done.

Libsecret encryption is not working on ubuntu

I installed the new AZ CLI tool version 2.32.0.
Linux version: Linux ubuntu 5.13.0-27-generic #29~20.04.1-Ubuntu SMP Fri Jan 14 00:32:30 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
Python version: 3.8.0
And I saw from the documentation that the LibSecret is now supported in Linux.
I used the AZ login to connect (username and password) to azure and it created the msal_tken_cache.json file within the .azure folder in the user's home folder, which has sensitive content in it (same as Accesstokens.json file had) and is not encrypted in any way (clear-text).

I then went into the python file in the repository and installed everything it asked for:
https://github.com/AzureAD/microsoft-authentication-extensions-for-python/blob/60c2a745e3bd8ae61e51b60cae541cae74053b99/msal_extensions/libsecret.py
pip install pygobject
Requirement already satisfied: pygobject in /usr/lib/python3/dist-packages (3.36.0)
Requirement already satisfied: pycairo>=1.11.1 in /usr/lib/python3/dist-packages (from pygobject) (1.16.2)

And saw a comment about installing the pygobject library and then retried again to log in and the same files were there and still not encrypted.

I then thought maybe, it will not be accessible from any other users in the OS, but I created a different user and I was able to access the files as before.

How does the encryption is being reflected here? Am is doing something wrong?

Thanks,
Bill

/// ---
Internal consistency tracking: https://dev.azure.com/IdentityDivision/Engineering/_workitems/edit/1760110

DP API Tests Fail in TravisCI

Some thing about the Windows Build Machines in TravisCI will not allow DP API calls. Each time, an OSError with errno=5 is returned.

These scenarios work on individual's machines, which means the core scenarios of this library are met. However, we need to figure out what it is about the TravisCI build machines is preventing this from working and either fix-it or understand the problem well enough to permanently skip these tests in CI. Until that investigation is done, a TODO is going in the code-base to fix this issue.

[Bug] `msal_extensions.token_cache.PersistedTokenCache` is bypassed by `ConfidentialClientApplication`

Describe the bug
AzureAD/microsoft-authentication-library-for-python#644 introduced a regression that msal_extensions.token_cache.PersistedTokenCache is bypassed by ConfidentialClientApplication.

To Reproduce

az login --service-principal --username ... --password ... --tenant ... --allow-no-subscriptions

az account get-access-token --scope https://management.azure.com//.default
...
  "expiresOn": "2024-04-11 19:25:43.000000",

az account get-access-token --scope https://management.azure.com//.default
...
  "expiresOn": "2024-04-11 19:26:03.000000",

Notice each time a new access token is retrieved, bypassing the token cache. Detailed analysis is provided at AzureAD/microsoft-authentication-library-for-python#644 (comment).

This causes a severe regression in OIDC authentication, so that no Azure CLI task can run longer than the OIDC token's 5-minute lifetime (Azure/azure-cli#28708 (comment)).

Expected behavior
Old access token from the token cache should be retrieved.

What you see instead
A new access token is retrieved.

The MSAL Python version you are using
1.28.0

Additional context
Add any other context about the problem here.

WindowsError: exception: access violation when using _MEMCPY

I get an access violation when using the following methods:

File "site-packages\msal\application.py", line 1168, in acquire_token_by_device_flow
File "site-packages\msal\oauth2cli\oauth2.py", line 384, in obtain_token_by_device_flow
File "site-packages\msal\oauth2cli\oauth2.py", line 347, in _obtain_token_by_device_flow
File "site-packages\msal\oauth2cli\oidc.py", line 102, in _obtain_token
File "site-packages\msal\oauth2cli\oauth2.py", line 765, in _obtain_token
File "site-packages\msal\application.py", line 308, in
File "site-packages\msal\token_cache.py", line 297, in add
File "site-packages\msal\token_cache.py", line 113, in add
File "site-packages\msal\token_cache.py", line 176, in __add
File "site-packages\msal_extensions\token_cache.py", line 49, in modify
File "site-packages\msal_extensions\persistence.py", line 163, in save
File "site-packages\msal_extensions\windows.py", line 80, in protect
File "site-packages\msal_extensions\windows.py", line 39, in raw
WindowsError: exception: access violation writing 0x0000028F4B081000

I use the device flow and it properly redirects me to the browser, I enter the credentials, everything works fine, but when returning to the Windows application written in python, I receive the error.

Edit: The Windows version doesn't affect this issue:
Note: This error does not occur on Windows 10 Version 2004 (May 2020 Update), 19041, but is present with the newer Windows 10 Version 20H2 (October 2020 Update), 19042.

I have been able to fix the issue for both Windows Versions by adding the following line in windows.py (0.3.0):
Line 8 (directly after: "_MEMCPY = ctypes.cdll.msvcrt.memcpy")
_MEMCPY.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t]

Workaround for encryption/decryption failure when ssh into a mac

It is observed (from elsewhere) that encryption/decryption attempt would fail when ssh into a mac.

Persistence check failed. Inspect inner exception for details
---> System.Exception: SecKeychainAddGenericPassword failed with error code: -25308

The solution here is to ask the end user to unlock their Mac first via the command:

security unlock-keychain -p $PASSWORD /Users/$USERNAME/Library/Keychains/login.keychain-db

Would need to ideally repro the issue, and then catch the exception and emit that workaround in the message.

test: create a test plan for creds sharing work

Based on the spec, let use create the test plan regarding how the creds sharing standard behavior would look like:

  1. When a new account is added (login)
  2. When an existing account is removed (logout)
  3. When the creds is corrupted/missing.
  4. (other scenarios)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.