0
# Device Code Flow
1
2
Specialized OAuth2 device code flow for applications running on devices without web browsers or with limited input capabilities. This flow is ideal for IoT devices, CLI tools, smart TVs, or other scenarios where traditional web-based authentication is not feasible.
3
4
## How Device Code Flow Works
5
6
1. Application requests a device code and user code from Azure AD
7
2. User is presented with a user code and verification URL
8
3. User navigates to the URL on a separate device and enters the code
9
4. Application polls Azure AD for token using the device code
10
5. Once user completes authentication, Azure AD returns tokens
11
12
## Capabilities
13
14
### Initiate Device Code Flow
15
16
Requests device and user codes from Azure Active Directory to begin the device authentication process.
17
18
```python { .api }
19
def acquire_user_code(self, resource, client_id, language=None):
20
"""
21
Get device code and user code for device flow authentication.
22
23
Parameters:
24
- resource (str): URI identifying the target resource
25
- client_id (str): OAuth2 client ID of the application
26
- language (str, optional): Language code for user messages (e.g., 'en-us')
27
28
Returns:
29
dict: Device code response containing:
30
- userCode (str): Code for user to enter at verification URL
31
- deviceCode (str): Device code for polling token endpoint
32
- verificationUrl (str): URL where user enters the code
33
- expiresIn (int): Seconds until codes expire (typically 900)
34
- interval (int): Recommended polling interval in seconds
35
- message (str): User-friendly instructions
36
"""
37
```
38
39
**Usage Example:**
40
41
```python
42
import adal
43
import time
44
45
context = adal.AuthenticationContext('https://login.microsoftonline.com/tenant-id')
46
47
# Get device code
48
user_code_info = context.acquire_user_code(
49
resource='https://management.azure.com/',
50
client_id='your-client-id'
51
)
52
53
print(f"Please visit {user_code_info['verificationUrl']}")
54
print(f"And enter this code: {user_code_info['userCode']}")
55
print(f"Message: {user_code_info['message']}")
56
```
57
58
### Complete Device Code Authentication
59
60
Polls Azure Active Directory for tokens using the device code after the user has completed authentication.
61
62
```python { .api }
63
def acquire_token_with_device_code(self, resource, user_code_info, client_id):
64
"""
65
Poll for token using device code after user authentication.
66
67
Parameters:
68
- resource (str): URI identifying the target resource
69
- user_code_info (dict): Device code response from acquire_user_code()
70
- client_id (str): OAuth2 client ID of the application
71
72
Returns:
73
dict: Authentication result with access token, refresh token, and metadata
74
75
Raises:
76
AdalError: If user denies consent, code expires, or other authentication errors
77
"""
78
```
79
80
**Usage Example:**
81
82
```python
83
# After displaying user code, poll for token
84
try:
85
token = context.acquire_token_with_device_code(
86
resource='https://management.azure.com/',
87
user_code_info=user_code_info,
88
client_id='your-client-id'
89
)
90
print("Authentication successful!")
91
print(f"Access token: {token['accessToken']}")
92
93
except adal.AdalError as e:
94
print(f"Device authentication failed: {e}")
95
```
96
97
### Cancel Device Code Request
98
99
Cancels an ongoing device code authentication request, useful when the application needs to abort the flow.
100
101
```python { .api }
102
def cancel_request_to_get_token_with_device_code(self, user_code_info):
103
"""
104
Cancel ongoing device code authentication request.
105
106
Parameters:
107
- user_code_info (dict): Device code response from acquire_user_code()
108
109
Returns:
110
None
111
"""
112
```
113
114
**Usage Example:**
115
116
```python
117
# Cancel if user requests abort or timeout occurs
118
context.cancel_request_to_get_token_with_device_code(user_code_info)
119
print("Device authentication cancelled")
120
```
121
122
## Complete Device Code Flow Example
123
124
```python
125
import adal
126
import time
127
import threading
128
129
def device_flow_authentication():
130
authority_url = 'https://login.microsoftonline.com/your-tenant-id'
131
context = adal.AuthenticationContext(authority_url)
132
133
resource = 'https://management.azure.com/'
134
client_id = 'your-client-id'
135
136
try:
137
# Step 1: Get device code
138
print("Initiating device code flow...")
139
user_code_info = context.acquire_user_code(resource, client_id)
140
141
# Step 2: Display instructions to user
142
print(f"\nTo sign in, use a web browser to open the page:")
143
print(f"{user_code_info['verificationUrl']}")
144
print(f"\nAnd enter the code: {user_code_info['userCode']}")
145
print(f"\nMessage: {user_code_info['message']}")
146
print(f"\nWaiting for authentication...")
147
148
# Optional: Set up cancellation after timeout
149
def cancel_after_timeout():
150
time.sleep(user_code_info['expiresIn'])
151
try:
152
context.cancel_request_to_get_token_with_device_code(user_code_info)
153
print("\nDevice code expired, authentication cancelled")
154
except:
155
pass # May already be completed
156
157
timeout_thread = threading.Thread(target=cancel_after_timeout)
158
timeout_thread.daemon = True
159
timeout_thread.start()
160
161
# Step 3: Poll for token
162
token = context.acquire_token_with_device_code(
163
resource, user_code_info, client_id
164
)
165
166
print("\nAuthentication successful!")
167
print(f"User: {token.get('userId', 'Unknown')}")
168
print(f"Token expires: {token.get('expiresOn', 'Unknown')}")
169
170
return token
171
172
except adal.AdalError as e:
173
print(f"\nDevice authentication failed: {e}")
174
return None
175
176
# Run the flow
177
token = device_flow_authentication()
178
```
179
180
## Error Handling
181
182
Device code flow can fail for several reasons:
183
184
- **User cancellation**: User denies consent or closes browser
185
- **Code expiration**: User doesn't complete authentication within time limit
186
- **Invalid client**: Client ID is not configured for device code flow
187
- **Network issues**: Connectivity problems during polling
188
189
```python
190
try:
191
token = context.acquire_token_with_device_code(
192
resource, user_code_info, client_id
193
)
194
except adal.AdalError as e:
195
if "authorization_pending" in str(e):
196
# Still waiting for user - continue polling
197
pass
198
elif "authorization_declined" in str(e):
199
print("User declined authorization")
200
elif "expired_token" in str(e):
201
print("Device code expired")
202
else:
203
print(f"Other error: {e}")
204
```
205
206
## Best Practices
207
208
1. **Display clear instructions**: Show both the URL and code prominently
209
2. **Handle timeouts gracefully**: Codes typically expire in 15 minutes
210
3. **Respect polling intervals**: Use the recommended interval from the response
211
4. **Provide cancellation**: Allow users to abort the flow
212
5. **Cache tokens**: Store refresh tokens for future use to avoid repeated device flows