0
# Public Client Applications
1
2
Public client applications are designed for desktop and mobile applications where the client cannot securely store credentials. MSAL Python's `PublicClientApplication` supports interactive authentication through web browsers, device code flow for browserless environments, and optional broker integration for enhanced security.
3
4
## Capabilities
5
6
### Application Initialization
7
8
Creates a public client application instance with optional broker support for enhanced security through device identity and single sign-on capabilities.
9
10
```python { .api }
11
class PublicClientApplication(ClientApplication):
12
def __init__(
13
self,
14
client_id: str,
15
client_credential=None, # Must remain None for public clients
16
*,
17
enable_broker_on_windows=None,
18
enable_broker_on_mac=None,
19
enable_broker_on_linux=None,
20
enable_broker_on_wsl=None,
21
authority=None,
22
validate_authority=True,
23
token_cache=None,
24
http_client=None,
25
verify=True,
26
proxies=None,
27
timeout=None,
28
client_claims=None,
29
app_name=None,
30
app_version=None,
31
client_capabilities=None,
32
exclude_scopes=None,
33
http_cache=None,
34
instance_discovery=None,
35
allow_broker=None,
36
enable_pii_log=None,
37
oidc_authority=None,
38
**kwargs
39
):
40
"""
41
Create a public client application.
42
43
Parameters:
44
- client_id: Your app's client ID from Azure portal
45
- enable_broker_on_windows: Enable broker on Windows 10+ (requires redirect URI)
46
- enable_broker_on_mac: Enable broker on macOS with Company Portal
47
- enable_broker_on_linux: Enable broker on Linux
48
- enable_broker_on_wsl: Enable broker on Windows Subsystem for Linux
49
- authority: Authority URL (default: https://login.microsoftonline.com/common)
50
- token_cache: Custom token cache instance
51
- http_client: Custom HTTP client
52
- proxies: HTTP proxy configuration
53
- timeout: HTTP timeout in seconds
54
"""
55
```
56
57
Usage example:
58
59
```python
60
import msal
61
62
# Basic public client
63
app = msal.PublicClientApplication(
64
client_id="12345678-1234-1234-1234-123456789012",
65
authority="https://login.microsoftonline.com/common"
66
)
67
68
# With broker support (requires appropriate redirect URI registration)
69
app_with_broker = msal.PublicClientApplication(
70
client_id="12345678-1234-1234-1234-123456789012",
71
authority="https://login.microsoftonline.com/your-tenant-id",
72
enable_broker_on_windows=True,
73
enable_broker_on_mac=True
74
)
75
```
76
77
### Interactive Authentication
78
79
Performs interactive authentication by opening a web browser for user sign-in. Supports various prompt behaviors, login hints, and additional consent scopes.
80
81
```python { .api }
82
def acquire_token_interactive(
83
self,
84
scopes: list,
85
prompt=None,
86
login_hint=None,
87
domain_hint=None,
88
claims_challenge=None,
89
timeout=None,
90
port=None,
91
extra_scopes_to_consent=None,
92
max_age=None,
93
parent_window_handle=None,
94
on_before_launching_ui=None,
95
auth_scheme=None,
96
**kwargs
97
):
98
"""
99
Acquire token interactively via web browser.
100
101
Parameters:
102
- scopes: List of scopes to request (e.g., ["User.Read", "Mail.Send"])
103
- prompt: Prompt behavior (msal.Prompt.NONE, LOGIN, CONSENT, SELECT_ACCOUNT)
104
- login_hint: Email address to pre-populate sign-in form
105
- domain_hint: Domain hint to skip domain selection
106
- claims_challenge: Additional claims from resource provider
107
- timeout: Browser interaction timeout in seconds
108
- port: Local server port for auth response (default: random)
109
- extra_scopes_to_consent: Additional scopes for upfront consent
110
- max_age: Maximum authentication age in seconds
111
- parent_window_handle: Handle to parent window (Windows only)
112
- on_before_launching_ui: Callback before launching browser
113
- auth_scheme: Authentication scheme (e.g., PopAuthScheme for PoP tokens)
114
115
Returns:
116
Dictionary with 'access_token' on success, 'error' on failure
117
"""
118
```
119
120
Usage example:
121
122
```python
123
import msal
124
125
app = msal.PublicClientApplication(
126
client_id="your-client-id",
127
authority="https://login.microsoftonline.com/common"
128
)
129
130
# Basic interactive authentication
131
result = app.acquire_token_interactive(
132
scopes=["User.Read", "Mail.Read"]
133
)
134
135
# With specific prompt and login hint
136
result = app.acquire_token_interactive(
137
scopes=["User.Read"],
138
prompt=msal.Prompt.SELECT_ACCOUNT,
139
login_hint="user@example.com",
140
timeout=120
141
)
142
143
if "access_token" in result:
144
print("Authentication successful!")
145
access_token = result["access_token"]
146
expires_in = result["expires_in"]
147
else:
148
print(f"Error: {result.get('error')}")
149
print(f"Description: {result.get('error_description')}")
150
```
151
152
### Device Code Flow
153
154
Initiates and completes device code authentication flow for devices without web browsers or limited input capabilities. Users authenticate on a separate device using a provided code.
155
156
```python { .api }
157
def initiate_device_flow(
158
self,
159
scopes=None,
160
**kwargs
161
):
162
"""
163
Initiate device code flow.
164
165
Parameters:
166
- scopes: List of scopes to request
167
168
Returns:
169
Dictionary containing device_code, user_code, verification_uri,
170
verification_uri_complete, expires_in, interval, and message
171
"""
172
173
def acquire_token_by_device_flow(
174
self,
175
flow: dict,
176
claims_challenge=None,
177
**kwargs
178
):
179
"""
180
Complete device code flow with polling.
181
182
Parameters:
183
- flow: Flow dictionary from initiate_device_flow()
184
- claims_challenge: Additional claims from resource provider
185
186
Returns:
187
Dictionary with 'access_token' on success, 'error' on failure
188
"""
189
```
190
191
Usage example:
192
193
```python
194
import msal
195
import time
196
197
app = msal.PublicClientApplication(
198
client_id="your-client-id",
199
authority="https://login.microsoftonline.com/common"
200
)
201
202
# Initiate device flow
203
flow = app.initiate_device_flow(scopes=["User.Read"])
204
205
if "user_code" not in flow:
206
raise ValueError(f"Failed to create device flow: {flow.get('error_description')}")
207
208
# Display instructions to user
209
print(flow["message"])
210
211
# Poll for completion
212
result = app.acquire_token_by_device_flow(flow)
213
214
if "access_token" in result:
215
print("Device authentication successful!")
216
access_token = result["access_token"]
217
else:
218
print(f"Device authentication failed: {result.get('error_description')}")
219
```
220
221
### Broker Integration
222
223
When broker is enabled, authentication goes through the platform's authentication broker (e.g., Web Account Manager on Windows, Company Portal on macOS) for enhanced security and single sign-on capabilities.
224
225
Required redirect URIs for broker support:
226
- **Windows/WSL**: `ms-appx-web://Microsoft.AAD.BrokerPlugin/your_client_id`
227
- **macOS**: `msauth.com.msauth.unsignedapp://auth`
228
- **Linux**: `http://localhost`
229
230
Usage considerations:
231
- Broker provides device identity as an additional authentication factor
232
- Enables automatic SSO from previously established sessions
233
- May be required by Conditional Access policies
234
- Requires appropriate redirect URI registration in Azure portal
235
236
```python
237
# Broker-enabled application
238
app = msal.PublicClientApplication(
239
client_id="your-client-id",
240
authority="https://login.microsoftonline.com/your-tenant-id",
241
enable_broker_on_windows=True,
242
enable_broker_on_mac=True,
243
enable_broker_on_linux=True
244
)
245
246
# Interactive auth will use broker when available
247
result = app.acquire_token_interactive(scopes=["User.Read"])
248
```
249
250
## Error Handling
251
252
Common error scenarios and handling patterns:
253
254
```python
255
result = app.acquire_token_interactive(scopes=["User.Read"])
256
257
if "access_token" in result:
258
# Success
259
access_token = result["access_token"]
260
expires_in = result["expires_in"]
261
token_type = result.get("token_type", "Bearer")
262
elif result.get("error") == "access_denied":
263
# User cancelled or denied consent
264
print("User cancelled authentication")
265
elif result.get("error") == "invalid_scope":
266
# Invalid or unauthorized scope requested
267
print(f"Invalid scope: {result.get('error_description')}")
268
elif result.get("error") == "interaction_required":
269
# Silent authentication failed, interaction needed
270
print("Interactive authentication required")
271
else:
272
# Other error
273
print(f"Authentication failed: {result.get('error_description')}")
274
```