Skip to content

Commit 3079040

Browse files
committed
Add documentation for MSAL Python with WSL and include example images
1 parent ecce97e commit 3079040

File tree

9 files changed

+302
-6
lines changed

9 files changed

+302
-6
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
---
2+
title: Using MSAL Python with Windows Subsystem for Linux
3+
description: Learn how to integrate Microsoft Entra ID authentication in WSL apps using MSAL Python and the Microsoft Single Sign-on for Linux broker.
4+
author: ploegert
5+
ms.author: jploegert
6+
ms.service: msal
7+
ms.topic: how-to
8+
ms.date: 05/08/2025
9+
---
10+
11+
## Enable SSO in WSL (Windows Subsystem for Linux) apps using MSAL Python and WAM
12+
13+
MSAL is able to call the Microsoft Single Sign-on to Linux, a Linux component that is shipped independent of the Linux Distribution, however it gets installed using a package manager using `sudo apt install microsoft-identity-broker` or `sudo dnf install microsoft-identity-broker`.
14+
15+
This component acts as an authentication broker allowing the users of your app to benefit from integration with accounts known to Linux, such as the account you signed into your Linux sessions for apps that consume from the broker. It's also bundled as a dependency of applications developed by Microsoft, such as [Company Portal](/mem/intune-service/user-help/enroll-device-linux). These applications are installed when a Linux computer is enrolled in a company's device fleet via an endpoint management solution like [Microsoft Intune](/mem/intune/fundamentals/what-is-intune).
16+
17+
Using an authentication broker on Linux enables you to simplify how your users authenticate with Microsoft Entra ID from your application, and take advantage of future functionality that protects Microsoft Entra ID refresh tokens from exfiltration and misuse.
18+
19+
To enable SSO in your WSL app using MSAL Python, you must ensure the keychain is set up and unlocked, as MSAL uses `libsecret` to communicate with the keyring daemon.
20+
21+
## WSL Authentication Flow Example
22+
23+
In a situation where you have a WSL app that needs to authenticate with Microsoft Entra ID, the authentication flow for an interactive request would look like this:
24+
25+
![Auth Flow from within WSL](../media/python-msal-wsl.gif)
26+
27+
## Update to the latest version of WSL
28+
29+
Ensure you have updated to the latest WSL release. The WAM Account Control dialog is supported in WSL versions 2.4.13 and above.
30+
31+
```powershell
32+
# To check what distros are available:
33+
wsl.exe --list --online
34+
35+
wsl.exe --install Ubuntu-22.04
36+
37+
# To check the WSL version:
38+
wsl --version
39+
40+
# To update WSL:
41+
wsl --update
42+
```
43+
44+
## Linux Package Dependencies
45+
46+
Install the following dependencies on your Linux platform:
47+
48+
- `libsecret-tools` is required to interface with the Linux keychain
49+
50+
### [Ubuntu](#tab/ubuntudep)
51+
52+
To install on debian/Ubuntu based Linux distribution:
53+
54+
```bash
55+
sudo apt install libsecret-1-0 -y
56+
57+
#from Powershell, run
58+
wsl.exe --shutdown
59+
```
60+
61+
### [Red Hat Enterprise Linux](#tab/rheldep)
62+
63+
To install on Red Hat/Fedora based Linux distribution:
64+
65+
```bash
66+
sudo dnf install libsecret-1-0 -y
67+
68+
#from Powershell, run
69+
wsl.exe --shutdown
70+
```
71+
72+
---
73+
74+
> [!IMPORTANT]
75+
> In order for the keychain to work as intended, you should make sure you 1. install the dependencies, 2. Reboot/restart wsl, 3. Configure the keychain. Failure to do the steps in the correct order will result with the keychain missing the option for "Password Keychain".
76+
77+
## Set up Keyring in WSL
78+
79+
MSAL uses `libsecret` on Linux. It's required to communicate with the `keyring` daemon. Users can use [Seahorse](https://wiki.gnome.org/Apps/Seahorse/) (a GNOME application for managing encryption keys and passwords) to manage the `keyring` contents through a Graphical User Interface (GUI).
80+
81+
On Debian-based distributions, you can install the package by running `sudo apt install seahorse` and then following these instructions:
82+
83+
1. Run `seahorse` in the terminal as a regular user (not as sudo)
84+
85+
![default keychain dialog](../media/wsl1.png)
86+
87+
2. In the top left corner, select **+** and create **Password** keyring.
88+
89+
![keychain dialog selecting password keyring](../media/wsl2.png)
90+
91+
3. Create a keyring named 'login'
92+
93+
![typing login to the prompt](../media/wsl3.png)
94+
95+
4. Set the password on the next dialog.
96+
![selecting a password and confirming](../media/wsl4.png)
97+
98+
5. Run `wsl.exe --shutdown` from your Windows Terminal.
99+
100+
6. Start a new WSL session and run the sample. You should be asked for the keyring password.
101+
102+
## Run a Sample App
103+
104+
To use a broker on the Linux platform, make sure you set the `BrokerOptions` to `OperatingSystems.Linux` as shown in the below code snippet:
105+
106+
Reference the [Enable SSO in native Linux apps using MSAL Python](./linux-broker-py.md) for information of how to configure the project.
107+
108+
### **Python Dependencies**
109+
110+
To use the broker, you will need to install the broker-related packages in addition to the core MSAL from PyPI:
111+
112+
```python
113+
#pip install msal[broker]>=1.31,<2
114+
pip install https://github.com/AzureAD/microsoft-authentication-library-for-python/archive/refs/heads/dev.zip
115+
pip install pymsalruntime
116+
```
117+
118+
### Run the Sample App
119+
120+
Once configured, you can call `acquire_token_interactive` to acquire a token. Save the following as `wsl_broker.py`:
121+
122+
```python
123+
import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1]
124+
import json
125+
import logging
126+
import requests
127+
import msal
128+
129+
# Optional logging
130+
# logging.basicConfig(level=logging.DEBUG)
131+
132+
var_authority = "https://login.microsoftonline.com/common"
133+
var_client_id = " your-client-id-here" # Replace with your app's client ID
134+
var_username = "your-username-here" # Replace with your username, e.g., "
135+
var_scope = ["User.ReadBasic.All"]
136+
137+
# Create a preferably long-lived app instance which maintains a token cache (Default cache is in memory only).
138+
app = msal.PublicClientApplication(
139+
var_client_id,
140+
authority=var_authority,
141+
enable_broker_on_windows=True,
142+
enable_broker_on_wsl=True
143+
)
144+
145+
# The pattern to acquire a token looks like this.
146+
result = None
147+
148+
# Firstly, check the cache to see if this end user has signed in before
149+
accounts = app.get_accounts(username=var_username)
150+
if accounts:
151+
logging.info("Account(s) exists in cache, probably with token too. Let's try.")
152+
result = app.acquire_token_silent(var_scope, account=accounts[0])
153+
154+
if not result:
155+
logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
156+
157+
result = app.acquire_token_interactive(var_scope,parent_window_handle=app.CONSOLE_WINDOW_HANDLE)
158+
159+
if "access_token" in result:
160+
print("Access token is: %s" % result['access_token'])
161+
162+
else:
163+
print(result.get("error"))
164+
print(result.get("error_description"))
165+
print(result.get("correlation_id")) # You may need this when reporting a bug
166+
if 65001 in result.get("error_codes", []): # Not mean to be coded programatically, but...
167+
# AAD requires user consent for U/P flow
168+
print("Visit this to consent:", app.get_authorization_request_url(config["scope"]))
169+
```
170+
171+
### Run the Sample
172+
173+
Run the sample app using the following command:
174+
175+
```bash
176+
python wsl_broker.py
177+
```
178+
179+
You should see a prompt to:
180+
181+
- enter your username/credentials
182+
- enter your keyring password
183+
- then the app will acquire a token and print it to the console

β€Žmsal-python-conceptual/advanced/linux-broker-py.mdβ€Ž

Lines changed: 119 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ An authentication broker is an application that runs on a user’s machine that
4040
| enable_broker_on_mac | Mac with Company Portal installed | msauth.com.msauth.unsignedapp://auth |
4141
| enable_broker_on_linux | Linux with Intune installed | `https://login.microsoftonline.com/common/oauth2/nativeclient`Β (MUST be enabled) |
4242

43-
2. As shown in the table above, your application needs to support broker-specific redirect URIs. For Linux, the URL must be:
43+
2. Your application needs to support broker-specific redirect URIs. For `Linux` specifically, the URL for the redirect URI must be:
4444

4545
```text
4646
https://login.microsoftonline.com/common/oauth2/nativeclient
@@ -50,6 +50,7 @@ An authentication broker is an application that runs on a user’s machine that
5050
5151
```python
5252
pip install msal[broker]>=1.31,<2
53+
pip install pymsalruntime
5354
```
5455
5556
4. Once configured, you can call `acquire_token_interactive` to acquire a token.
@@ -69,7 +70,19 @@ The following parameters are available to configure broker support in MSAL Pytho
6970
| enable_broker_on_wsl | `boolean` | This setting is only effective if your app is running on WSL. This parameter defaults to None, which means MSAL will not utilize a broker. </br></br>`New in MSAL Python 1.25.0`. |
7071
| enable_broker_on_mac | `boolean` | This setting is only effective if your app is running on Mac with Company Portal installed. This parameter defaults to None, which means MSAL will not utilize a broker. </br></br>`New in MSAL Python 1.31.0`.|
7172
| enable_broker_on_linux | `boolean` | This setting is only effective if your app is running on Linux with Intune installed. This parameter defaults to None, which means MSAL will not utilize a broker. </br></br>`New in MSAL Python 1.33.0`. |
72-
| parent_window_handle | `int` | <i>OPTIONAL</i></br> - If your app does not opt in to use broker, you do not need to provide a parent_window_handle here.</br>- If your app opts in to use broker, parent_window_handle is required.</br>If your app is a GUI app running on Windows or Mac system, you are required to also provide its window handle, so that the sign-in window will pop up on top of your window.</br>- If your app is a console app running on Windows or Mac system, you can use a placeholder `PublicClientApplication.CONSOLE_WINDOW_HANDLE`|
73+
| parent_window_handle | `int` | <i>OPTIONAL</i></br></br>
74+
75+
### Notes regarding parent_window_handle
76+
77+
The `parent_window_handle` parameter is required even though on Linux it is not used. For GUI applications, the login prompt location will be determined ad-hoc and currently cannot be bound to a specific window. In a future update, this parameter will be used to determine the _actual_ parent window.
78+
79+
| Condition | Description |
80+
|---|---|
81+
|App does not want to utilize a broker|no need to specify a parent_window_handle|
82+
|App opts to use a broker|parent_window_handle is required|
83+
|App is a GUI app running on Windows or Mac system|required to provide its window handle, so that the sign-in window will pop up on top of your window|
84+
|App is a console app running on Windows or Mac system|can use a placeholder `PublicClientApplication.CONSOLE_WINDOW_HANDLE`|
85+
|App is intended to be a cross-platform application| App needs to use `enable_broker_on_windows`, as outlined in the [Using MSAL Python with Web Account Manager](wam.md) article.|
7386
7487
## The fallback behaviors of MSAL Python’s broker support
7588
@@ -86,12 +99,112 @@ MSAL will either error out, or silently fallback to non-broker flows.
8699
>[!IMPORTANT]
87100
>If broker-related packages are not installed and you will try to use the authentication broker, you will get an error: `ImportError: You need to install dependency by: pip install "msal[broker]>=1.31,<2"`.
88101
89-
>[!IMPORTANT]
90-
>If you are writing a cross-platform application, you will also need to use `enable_broker_on_windows`, as outlined in the [Using MSAL Python with Web Account Manager](wam.md) article.
91-
92102
>[!NOTE]
93103
>The `parent_window_handle` parameter is required even though on Linux it is not used. For GUI applications, the login prompt location will be determined ad-hoc and currently cannot be bound to a specific window. In a future update, this parameter will be used to determine the _actual_ parent window.
94104
95105
## Token caching
96106
97-
The authentication broker handles refresh and access token caching. You do not need to set up custom caching.
107+
The authentication broker handles refresh and access token caching. You do not need to set up custom caching.
108+
109+
## Building an example app
110+
111+
You can find a sample app that demonstrates how to use MSAL Python with the authentication broker on Linux in the [MSAL Python GitHub repository](https://github.com/AzureAD/microsoft-authentication-library-for-python/). The sample app is located in the `samples/console_app` directory and includes examples of how to use the broker for authentication.
112+
113+
### **App Registration**
114+
115+
Update your App registration in the Azure portal to include the broker-specific redirect URI for Linux:
116+
117+
```text
118+
https://login.microsoftonline.com/common/oauth2/nativeclient
119+
```
120+
121+
### **Linux Dependencies**
122+
123+
First, check if you have python3 installed on your Linux distribution.
124+
125+
```bash
126+
python3 --version
127+
```
128+
129+
If not, install it using the package manager for your distribution.
130+
131+
#### [Ubuntu](#tab/ubuntudep)
132+
133+
To install on debian/Ubuntu based Linux distribution:
134+
135+
```bash
136+
sudo apt install python3 python3-pip -y
137+
```
138+
139+
#### [Red Hat Enterprise Linux](#tab/rheldep)
140+
141+
To install on Red Hat/Fedora based Linux distribution:
142+
143+
```bash
144+
sudo dnf install python3 python3-pip -y
145+
```
146+
147+
---
148+
149+
### **Python Dependencies**
150+
151+
To use the broker, you will need to install the broker-related packages in addition to the core MSAL from PyPI:
152+
153+
```python
154+
#pip install msal[broker]>=1.31,<2
155+
pip install https://github.com/AzureAD/microsoft-authentication-library-for-python/archive/refs/heads/dev.zip
156+
pip install pymsalruntime
157+
```
158+
159+
### Once configured, you can call `acquire_token_interactive` to acquire a token.
160+
161+
```python
162+
import sys # For simplicity, we'll read config file from 1st CLI param sys.argv[1]
163+
import json
164+
import logging
165+
import requests
166+
import msal
167+
168+
# Optional logging
169+
# logging.basicConfig(level=logging.DEBUG)
170+
171+
var_authority = "https://login.microsoftonline.com/common"
172+
var_client_id = "4b0db8c2-9f26-4417-8bde-3f0e3656f8e0"
173+
var_username = "[email protected]"
174+
var_scope = ["User.ReadBasic.All"]
175+
var_endpoint = "https://graph.microsoft.com/v1.0/users"
176+
177+
178+
# Create a preferably long-lived app instance which maintains a token cache (Default cache is in memory only).
179+
app = msal.PublicClientApplication(
180+
var_client_id,
181+
authority=var_authority,
182+
enable_broker_on_windows=True,
183+
enable_broker_on_wsl=True
184+
)
185+
186+
# The pattern to acquire a token looks like this.
187+
result = None
188+
189+
# Firstly, check the cache to see if this end user has signed in before
190+
accounts = app.get_accounts(username=var_username)
191+
if accounts:
192+
logging.info("Account(s) exists in cache, probably with token too. Let's try.")
193+
result = app.acquire_token_silent(var_scope, account=accounts[0])
194+
195+
if not result:
196+
logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
197+
198+
result = app.acquire_token_interactive(var_scope,parent_window_handle=app.CONSOLE_WINDOW_HANDLE)
199+
200+
if "access_token" in result:
201+
print("Access token is: %s" % result['access_token'])
202+
203+
else:
204+
print(result.get("error"))
205+
print(result.get("error_description"))
206+
print(result.get("correlation_id")) # You may need this when reporting a bug
207+
if 65001 in result.get("error_codes", []): # Not mean to be coded programatically, but...
208+
# AAD requires user consent for U/P flow
209+
print("Visit this to consent:", app.get_authorization_request_url(config["scope"]))
210+
```
2.03 MB
Loading
28 KB
Loading
28.3 KB
Loading
25.6 KB
Loading
16.6 KB
Loading
16.5 KB
Loading
18.2 KB
Loading

0 commit comments

Comments
Β (0)