0
# Device Authentication
1
2
Device tracking and authentication support for AWS Cognito User Pools, enabling device-based security features including device confirmation, status management, and device-specific authentication flows. Device authentication adds an additional layer of security by tracking and managing trusted devices.
3
4
## Capabilities
5
6
### Device Confirmation
7
8
Confirm and register devices for trusted device tracking and enhanced security.
9
10
```python { .api }
11
def confirm_device(self, tokens: dict, device_name: str = None) -> tuple:
12
"""
13
Confirm a device for device tracking and authentication.
14
15
Args:
16
tokens (dict): Authentication result tokens containing device metadata
17
device_name (str, optional): Friendly name for device (defaults to hostname)
18
19
Returns:
20
tuple: (response, device_password)
21
- response: HTTP response from device confirmation
22
- device_password: Generated device password for future authentication
23
24
Actions:
25
- Registers device with Cognito
26
- Generates device-specific credentials
27
- Sets up device for remembered authentication
28
- Stores device keys and metadata
29
30
Sets instance attributes:
31
- access_token: Token from authentication result
32
- device_key: Unique device identifier
33
- device_group_key: Device group identifier
34
- device_name: Friendly device name
35
"""
36
```
37
38
**Usage Example:**
39
40
```python
41
from pycognito.aws_srp import AWSSRP
42
43
# Authenticate user and get tokens with device metadata
44
aws_srp = AWSSRP(
45
username='user@example.com',
46
password='password',
47
pool_id='us-east-1_example',
48
client_id='your-client-id'
49
)
50
51
# Authenticate and get tokens
52
tokens = aws_srp.authenticate_user()
53
54
# Check if device confirmation is needed
55
if 'NewDeviceMetadata' in tokens.get('AuthenticationResult', {}):
56
print("New device detected - confirming device")
57
58
# Confirm device with friendly name
59
response, device_password = aws_srp.confirm_device(
60
tokens,
61
device_name="John's iPhone"
62
)
63
64
print(f"Device confirmed! Device password: {device_password}")
65
print(f"Device key: {aws_srp.device_key}")
66
67
# Store device credentials securely for future use
68
store_device_credentials(aws_srp.device_key, device_password)
69
```
70
71
### Device Status Management
72
73
Update device status to control device behavior and memory settings.
74
75
```python { .api }
76
def update_device_status(self, is_remembered: bool, access_token: str, device_key: str) -> str:
77
"""
78
Update device remembered status.
79
80
Args:
81
is_remembered (bool): Whether device should be remembered
82
access_token (str): Valid access token
83
device_key (str): Device key from device confirmation
84
85
Returns:
86
str: Response status from update operation
87
88
Device status options:
89
- remembered: Device is trusted and remembers user
90
- not_remembered: Device requires full authentication each time
91
92
Use cases:
93
- Mark personal devices as remembered
94
- Mark public/shared devices as not remembered
95
- Revoke device trust due to security concerns
96
"""
97
```
98
99
**Usage Example:**
100
101
```python
102
# Mark device as remembered (trusted)
103
response = aws_srp.update_device_status(
104
is_remembered=True,
105
access_token=user.access_token,
106
device_key=device_key
107
)
108
print(f"Device marked as remembered: {response}")
109
110
# Later, mark device as not remembered (untrusted)
111
response = aws_srp.update_device_status(
112
is_remembered=False,
113
access_token=user.access_token,
114
device_key=device_key
115
)
116
print(f"Device marked as not remembered: {response}")
117
```
118
119
### Device Forgetting
120
121
Remove device trust and credentials from the system.
122
123
```python { .api }
124
def forget_device(self, access_token: str, device_key: str) -> str:
125
"""
126
Forget/remove a device from trusted devices.
127
128
Args:
129
access_token (str): Valid access token
130
device_key (str): Device key to forget
131
132
Returns:
133
str: Response status from forget operation
134
135
Actions:
136
- Removes device from trusted device list
137
- Invalidates device-specific credentials
138
- Forces full authentication on next login
139
- Clears device memory and tracking
140
141
Use cases:
142
- Device is lost or stolen
143
- User no longer uses the device
144
- Security incident requires device removal
145
- User explicitly removes device trust
146
"""
147
```
148
149
**Usage Example:**
150
151
```python
152
# Forget a lost or stolen device
153
response = aws_srp.forget_device(
154
access_token=user.access_token,
155
device_key=lost_device_key
156
)
157
print(f"Device forgotten: {response}")
158
159
# Device will require full authentication next time
160
```
161
162
### Device-Enhanced Authentication
163
164
Authenticate with device-specific credentials for enhanced security and user experience.
165
166
```python { .api }
167
class AWSSRP:
168
def __init__(self, username: str, password: str, pool_id: str, client_id: str,
169
device_key: str = None, device_group_key: str = None,
170
device_password: str = None, **kwargs):
171
"""
172
Initialize AWSSRP with device authentication support.
173
174
Args:
175
username (str): Username
176
password (str): User password
177
pool_id (str): User pool ID
178
client_id (str): Client ID
179
device_key (str, optional): Device key from device confirmation
180
device_group_key (str, optional): Device group key
181
device_password (str, optional): Device password
182
183
Note:
184
For device authentication, either all device parameters must be provided
185
or none at all. Partial device parameters will raise ValueError.
186
"""
187
188
def authenticate_user(self, client=None, client_metadata: dict = None) -> dict:
189
"""
190
Authenticate user with optional device authentication.
191
192
Returns:
193
dict: Authentication tokens and device metadata
194
195
Flow:
196
1. Standard SRP authentication
197
2. If device keys provided, performs device authentication
198
3. Returns tokens with device confirmation if new device
199
4. Handles device challenges automatically
200
"""
201
```
202
203
**Usage Example:**
204
205
```python
206
# Authenticate with remembered device
207
aws_srp = AWSSRP(
208
username='user@example.com',
209
password='password',
210
pool_id='us-east-1_example',
211
client_id='your-client-id',
212
device_key='stored-device-key',
213
device_group_key='stored-device-group-key',
214
device_password='stored-device-password'
215
)
216
217
try:
218
# Authentication with device credentials
219
tokens = aws_srp.authenticate_user()
220
print("Authentication successful with device credentials!")
221
222
except Exception as e:
223
print(f"Device authentication failed: {e}")
224
# Fall back to standard authentication
225
aws_srp_fallback = AWSSRP(
226
username='user@example.com',
227
password='password',
228
pool_id='us-east-1_example',
229
client_id='your-client-id'
230
)
231
tokens = aws_srp_fallback.authenticate_user()
232
```
233
234
## Usage Patterns
235
236
### Complete Device Registration Flow
237
238
```python
239
def register_device(username, password, device_name):
240
"""Complete device registration and confirmation flow."""
241
242
# Step 1: Initial authentication
243
aws_srp = AWSSRP(
244
username=username,
245
password=password,
246
pool_id='your-pool-id',
247
client_id='your-client-id'
248
)
249
250
# Step 2: Authenticate and check for new device
251
tokens = aws_srp.authenticate_user()
252
253
auth_result = tokens.get('AuthenticationResult', {})
254
255
if 'NewDeviceMetadata' in auth_result:
256
print("New device detected - setting up device authentication")
257
258
# Step 3: Confirm device
259
response, device_password = aws_srp.confirm_device(tokens, device_name)
260
261
if response.status_code == 200:
262
print("Device confirmed successfully!")
263
264
# Step 4: Mark as remembered
265
aws_srp.update_device_status(
266
is_remembered=True,
267
access_token=auth_result['AccessToken'],
268
device_key=aws_srp.device_key
269
)
270
271
# Step 5: Store device credentials securely
272
device_credentials = {
273
'device_key': aws_srp.device_key,
274
'device_group_key': aws_srp.device_group_key,
275
'device_password': device_password,
276
'device_name': device_name
277
}
278
279
# Store in secure storage (keychain, encrypted file, etc.)
280
save_device_credentials(username, device_credentials)
281
282
print(f"Device '{device_name}' registered and remembered")
283
return device_credentials
284
285
else:
286
print(f"Device confirmation failed: {response}")
287
return None
288
else:
289
print("No new device metadata - device may already be registered")
290
return None
291
292
# Usage
293
device_creds = register_device('user@example.com', 'password', 'My Laptop')
294
```
295
296
### Device-Aware Authentication Manager
297
298
```python
299
class DeviceAwareAuth:
300
"""Authentication manager with device support."""
301
302
def __init__(self, pool_id, client_id):
303
self.pool_id = pool_id
304
self.client_id = client_id
305
306
def authenticate(self, username, password, device_credentials=None):
307
"""Authenticate with optional device credentials."""
308
309
if device_credentials:
310
# Try device-enhanced authentication first
311
try:
312
aws_srp = AWSSRP(
313
username=username,
314
password=password,
315
pool_id=self.pool_id,
316
client_id=self.client_id,
317
device_key=device_credentials['device_key'],
318
device_group_key=device_credentials['device_group_key'],
319
device_password=device_credentials['device_password']
320
)
321
322
tokens = aws_srp.authenticate_user()
323
return tokens, "DEVICE_AUTH_SUCCESS"
324
325
except Exception as e:
326
print(f"Device authentication failed: {e}")
327
# Fall through to standard authentication
328
329
# Standard authentication
330
aws_srp = AWSSRP(
331
username=username,
332
password=password,
333
pool_id=self.pool_id,
334
client_id=self.client_id
335
)
336
337
tokens = aws_srp.authenticate_user()
338
339
# Check if device registration is needed
340
if 'NewDeviceMetadata' in tokens.get('AuthenticationResult', {}):
341
return tokens, "NEW_DEVICE_DETECTED"
342
else:
343
return tokens, "STANDARD_AUTH_SUCCESS"
344
345
def setup_device(self, tokens, device_name, remember=True):
346
"""Set up device after authentication."""
347
348
# This would typically be called by the same AWSSRP instance
349
# that performed authentication
350
aws_srp = AWSSRP(
351
username="", password="", # Not needed for device operations
352
pool_id=self.pool_id,
353
client_id=self.client_id
354
)
355
356
response, device_password = aws_srp.confirm_device(tokens, device_name)
357
358
if response.status_code == 200 and remember:
359
aws_srp.update_device_status(
360
is_remembered=True,
361
access_token=tokens['AuthenticationResult']['AccessToken'],
362
device_key=aws_srp.device_key
363
)
364
365
return {
366
'device_key': aws_srp.device_key,
367
'device_group_key': aws_srp.device_group_key,
368
'device_password': device_password,
369
'device_name': device_name
370
}
371
372
# Usage
373
auth_manager = DeviceAwareAuth('pool-id', 'client-id')
374
375
# Load stored device credentials
376
device_creds = load_device_credentials('user@example.com')
377
378
# Authenticate
379
tokens, status = auth_manager.authenticate(
380
'user@example.com',
381
'password',
382
device_creds
383
)
384
385
if status == "NEW_DEVICE_DETECTED":
386
# Set up new device
387
new_device_creds = auth_manager.setup_device(
388
tokens,
389
"My New Device",
390
remember=True
391
)
392
save_device_credentials('user@example.com', new_device_creds)
393
print("New device set up successfully!")
394
395
elif status == "DEVICE_AUTH_SUCCESS":
396
print("Authenticated with trusted device!")
397
398
elif status == "STANDARD_AUTH_SUCCESS":
399
print("Standard authentication successful!")
400
```
401
402
### Device Management Dashboard
403
404
```python
405
def device_management_interface(username, access_token):
406
"""Interactive device management interface."""
407
408
print(f"\n=== Device Management for {username} ===")
409
print("1. List trusted devices")
410
print("2. Forget a device")
411
print("3. Update device status")
412
print("4. Register new device")
413
414
choice = input("Select option (1-4): ")
415
416
if choice == "1":
417
# List devices (would require additional API calls)
418
print("Trusted devices:")
419
devices = get_user_devices(username) # Custom function
420
for device in devices:
421
print(f"- {device['name']} ({device['key']})")
422
423
elif choice == "2":
424
# Forget device
425
device_key = input("Enter device key to forget: ")
426
427
aws_srp = AWSSRP(
428
username="", password="",
429
pool_id='pool-id', client_id='client-id'
430
)
431
432
response = aws_srp.forget_device(access_token, device_key)
433
print(f"Device forgotten: {response}")
434
435
elif choice == "3":
436
# Update device status
437
device_key = input("Enter device key: ")
438
remember = input("Remember device? (y/n): ").lower() == 'y'
439
440
aws_srp = AWSSRP(
441
username="", password="",
442
pool_id='pool-id', client_id='client-id'
443
)
444
445
response = aws_srp.update_device_status(
446
is_remembered=remember,
447
access_token=access_token,
448
device_key=device_key
449
)
450
print(f"Device status updated: {response}")
451
452
elif choice == "4":
453
print("Please authenticate on the new device to register it")
454
455
# Usage after authentication
456
device_management_interface('user@example.com', user.access_token)
457
```
458
459
### Secure Device Storage
460
461
```python
462
import json
463
import keyring
464
from cryptography.fernet import Fernet
465
466
class SecureDeviceStorage:
467
"""Secure storage for device credentials."""
468
469
def __init__(self, service_name="MyApp"):
470
self.service_name = service_name
471
472
def _get_encryption_key(self, username):
473
"""Get or create encryption key for user."""
474
key_name = f"{username}_device_key"
475
476
# Try to get existing key
477
key = keyring.get_password(self.service_name, key_name)
478
479
if not key:
480
# Generate new key
481
key = Fernet.generate_key().decode()
482
keyring.set_password(self.service_name, key_name, key)
483
484
return key.encode()
485
486
def store_device_credentials(self, username, credentials):
487
"""Store device credentials securely."""
488
encryption_key = self._get_encryption_key(username)
489
fernet = Fernet(encryption_key)
490
491
# Encrypt credentials
492
credentials_json = json.dumps(credentials)
493
encrypted_credentials = fernet.encrypt(credentials_json.encode())
494
495
# Store in keyring
496
cred_name = f"{username}_device_credentials"
497
keyring.set_password(
498
self.service_name,
499
cred_name,
500
encrypted_credentials.decode()
501
)
502
503
def load_device_credentials(self, username):
504
"""Load device credentials securely."""
505
try:
506
encryption_key = self._get_encryption_key(username)
507
fernet = Fernet(encryption_key)
508
509
# Get encrypted credentials
510
cred_name = f"{username}_device_credentials"
511
encrypted_credentials = keyring.get_password(
512
self.service_name,
513
cred_name
514
)
515
516
if not encrypted_credentials:
517
return None
518
519
# Decrypt credentials
520
decrypted_data = fernet.decrypt(encrypted_credentials.encode())
521
credentials = json.loads(decrypted_data.decode())
522
523
return credentials
524
525
except Exception as e:
526
print(f"Failed to load device credentials: {e}")
527
return None
528
529
def remove_device_credentials(self, username):
530
"""Remove stored device credentials."""
531
cred_name = f"{username}_device_credentials"
532
key_name = f"{username}_device_key"
533
534
try:
535
keyring.delete_password(self.service_name, cred_name)
536
keyring.delete_password(self.service_name, key_name)
537
print("Device credentials removed")
538
except Exception as e:
539
print(f"Failed to remove credentials: {e}")
540
541
# Usage
542
storage = SecureDeviceStorage("MyCognitoApp")
543
544
# Store credentials after device registration
545
device_creds = {
546
'device_key': 'device-key-123',
547
'device_group_key': 'group-key-456',
548
'device_password': 'device-password-789',
549
'device_name': 'My Laptop'
550
}
551
552
storage.store_device_credentials('user@example.com', device_creds)
553
554
# Load credentials for authentication
555
loaded_creds = storage.load_device_credentials('user@example.com')
556
if loaded_creds:
557
print(f"Loaded device: {loaded_creds['device_name']}")
558
```