0
# Message Processing
1
2
OpenID protocol message handling with namespace management, encoding/decoding, and format conversion. The message system provides a unified interface for working with OpenID protocol messages across different formats and versions.
3
4
## Capabilities
5
6
### Message Objects
7
8
Core message representation with namespace-aware argument handling and format conversion.
9
10
```python { .api }
11
class Message:
12
"""OpenID protocol message with namespace support."""
13
14
def __init__(self, openid_namespace=None):
15
"""
16
Initialize message with optional OpenID namespace.
17
18
Parameters:
19
- openid_namespace: str, OpenID namespace URI (auto-detected if None)
20
"""
21
22
@classmethod
23
def fromPostArgs(cls, args):
24
"""
25
Create message from POST form arguments.
26
27
Parameters:
28
- args: dict, form arguments (typically request.POST)
29
30
Returns:
31
Message object
32
"""
33
34
@classmethod
35
def fromOpenIDArgs(cls, openid_args):
36
"""
37
Create message from OpenID-specific arguments.
38
39
Parameters:
40
- openid_args: dict, arguments with 'openid.' prefix stripped
41
42
Returns:
43
Message object
44
"""
45
46
@classmethod
47
def fromKVForm(cls, kvform_string):
48
"""
49
Create message from key-value form string.
50
51
Parameters:
52
- kvform_string: str, key-value form data
53
54
Returns:
55
Message object
56
"""
57
58
def copy(self):
59
"""
60
Create deep copy of this message.
61
62
Returns:
63
Message, copy of this message
64
"""
65
66
def toPostArgs(self):
67
"""
68
Convert message to POST form arguments.
69
70
Returns:
71
dict, form arguments with 'openid.' prefixes
72
"""
73
74
def toArgs(self):
75
"""
76
Convert message to basic argument dictionary.
77
78
Returns:
79
dict, message arguments
80
"""
81
82
def toFormMarkup(self, action_url, form_tag_attrs=None, submit_text="Continue"):
83
"""
84
Generate HTML form markup for message.
85
86
Parameters:
87
- action_url: str, form action URL
88
- form_tag_attrs: dict, additional form tag attributes
89
- submit_text: str, submit button text
90
91
Returns:
92
str, HTML form markup
93
"""
94
95
def toURL(self, base_url):
96
"""
97
Convert message to URL with query parameters.
98
99
Parameters:
100
- base_url: str, base URL
101
102
Returns:
103
str, URL with message encoded as query parameters
104
"""
105
106
def toKVForm(self):
107
"""
108
Convert message to key-value form string.
109
110
Returns:
111
str, key-value form representation
112
"""
113
114
def toURLEncoded(self):
115
"""
116
Convert message to URL-encoded form data.
117
118
Returns:
119
str, URL-encoded form data
120
"""
121
```
122
123
### Message Argument Handling
124
125
Access and modify message arguments with namespace support.
126
127
```python { .api }
128
def hasKey(self, namespace, ns_key):
129
"""
130
Check if message has argument in namespace.
131
132
Parameters:
133
- namespace: str, namespace URI or symbol
134
- ns_key: str, argument key within namespace
135
136
Returns:
137
bool, True if argument exists
138
"""
139
140
def getKey(self, namespace, ns_key):
141
"""
142
Get full argument key for namespace and key.
143
144
Parameters:
145
- namespace: str, namespace URI or symbol
146
- ns_key: str, argument key within namespace
147
148
Returns:
149
str, full argument key with namespace alias
150
"""
151
152
def getArg(self, namespace, key, default=None):
153
"""
154
Get argument value from namespace.
155
156
Parameters:
157
- namespace: str, namespace URI or symbol
158
- key: str, argument key
159
- default: default value if not found
160
161
Returns:
162
str or default, argument value
163
"""
164
165
def getArgs(self, namespace):
166
"""
167
Get all arguments in namespace.
168
169
Parameters:
170
- namespace: str, namespace URI or symbol
171
172
Returns:
173
dict, {key: value} arguments in namespace
174
"""
175
176
def setArg(self, namespace, key, value):
177
"""
178
Set argument value in namespace.
179
180
Parameters:
181
- namespace: str, namespace URI or symbol
182
- key: str, argument key
183
- value: str, argument value
184
"""
185
186
def updateArgs(self, namespace, updates):
187
"""
188
Update multiple arguments in namespace.
189
190
Parameters:
191
- namespace: str, namespace URI or symbol
192
- updates: dict, {key: value} updates
193
"""
194
195
def delArg(self, namespace, key):
196
"""
197
Delete argument from namespace.
198
199
Parameters:
200
- namespace: str, namespace URI or symbol
201
- key: str, argument key to delete
202
"""
203
204
def getAliasedArg(self, aliased_key, default=None):
205
"""
206
Get argument by aliased key (e.g., 'openid.mode').
207
208
Parameters:
209
- aliased_key: str, full aliased key
210
- default: default value if not found
211
212
Returns:
213
str or default, argument value
214
"""
215
```
216
217
### Namespace Management
218
219
Handle OpenID namespace detection and version compatibility.
220
221
```python { .api }
222
def setOpenIDNamespace(self, openid_ns_uri, implicit):
223
"""
224
Set OpenID namespace for this message.
225
226
Parameters:
227
- openid_ns_uri: str, OpenID namespace URI
228
- implicit: bool, whether namespace is implicit (OpenID 1.x)
229
"""
230
231
def getOpenIDNamespace(self):
232
"""
233
Get OpenID namespace URI for this message.
234
235
Returns:
236
str, OpenID namespace URI
237
"""
238
239
def isOpenID1(self):
240
"""
241
Check if message uses OpenID 1.x protocol.
242
243
Returns:
244
bool, True if OpenID 1.x
245
"""
246
247
def isOpenID2(self):
248
"""
249
Check if message uses OpenID 2.0 protocol.
250
251
Returns:
252
bool, True if OpenID 2.0
253
"""
254
```
255
256
### Namespace Mapping
257
258
Manage namespace URI to alias mappings for message encoding.
259
260
```python { .api }
261
class NamespaceMap:
262
"""Maps namespace URIs to short aliases for message encoding."""
263
264
def __init__(self):
265
"""Initialize empty namespace map."""
266
267
def getAlias(self, namespace_uri):
268
"""
269
Get alias for namespace URI.
270
271
Parameters:
272
- namespace_uri: str, namespace URI
273
274
Returns:
275
str, namespace alias or None if not defined
276
"""
277
278
def getNamespaceURI(self, alias):
279
"""
280
Get namespace URI for alias.
281
282
Parameters:
283
- alias: str, namespace alias
284
285
Returns:
286
str, namespace URI or None if not defined
287
"""
288
289
def addAlias(self, namespace_uri, desired_alias, implicit=False):
290
"""
291
Add namespace URI with desired alias.
292
293
Parameters:
294
- namespace_uri: str, namespace URI
295
- desired_alias: str, desired alias (may be modified if conflicts)
296
- implicit: bool, whether namespace is implicit
297
298
Returns:
299
str, actual alias assigned
300
"""
301
302
def add(self, namespace_uri):
303
"""
304
Add namespace URI with auto-generated alias.
305
306
Parameters:
307
- namespace_uri: str, namespace URI
308
309
Returns:
310
str, assigned alias
311
"""
312
313
def isDefined(self, namespace_uri):
314
"""
315
Check if namespace URI is defined.
316
317
Parameters:
318
- namespace_uri: str, namespace URI
319
320
Returns:
321
bool, True if defined
322
"""
323
324
def isImplicit(self, namespace_uri):
325
"""
326
Check if namespace is implicit (no explicit alias).
327
328
Parameters:
329
- namespace_uri: str, namespace URI
330
331
Returns:
332
bool, True if implicit
333
"""
334
335
def items(self):
336
"""
337
Get all namespace URI to alias mappings.
338
339
Returns:
340
list, [(namespace_uri, alias), ...] tuples
341
"""
342
```
343
344
### Global Namespace Utilities
345
346
Utilities for managing global namespace alias registrations.
347
348
```python { .api }
349
def registerNamespaceAlias(namespace_uri, alias):
350
"""
351
Register global namespace alias for automatic assignment.
352
353
Parameters:
354
- namespace_uri: str, namespace URI
355
- alias: str, preferred alias
356
357
Raises:
358
NamespaceAliasRegistrationError: if alias conflicts
359
"""
360
```
361
362
## Usage Examples
363
364
### Basic Message Creation and Access
365
366
```python
367
from openid.message import Message, OPENID2_NS
368
369
# Create new message
370
message = Message(OPENID2_NS)
371
372
# Set basic OpenID arguments
373
message.setArg('openid.ns', OPENID2_NS)
374
message.setArg('openid.mode', 'checkid_setup')
375
message.setArg('openid.identity', 'https://user.example.com')
376
message.setArg('openid.return_to', 'https://consumer.example.com/return')
377
378
# Access arguments
379
mode = message.getArg('openid.ns', 'mode')
380
identity = message.getArg('openid.ns', 'identity')
381
382
print(f"Mode: {mode}")
383
print(f"Identity: {identity}")
384
385
# Check OpenID version
386
if message.isOpenID2():
387
print("Using OpenID 2.0")
388
elif message.isOpenID1():
389
print("Using OpenID 1.x")
390
```
391
392
### Message Format Conversion
393
394
```python
395
# Convert to different formats
396
post_args = message.toPostArgs()
397
print("POST arguments:", post_args)
398
399
url = message.toURL('https://op.example.com/openid')
400
print("URL:", url)
401
402
kvform = message.toKVForm()
403
print("Key-value form:")
404
print(kvform)
405
406
# Generate HTML form
407
form_html = message.toFormMarkup(
408
action_url='https://op.example.com/openid',
409
submit_text='Continue to Identity Provider'
410
)
411
print("HTML form:")
412
print(form_html)
413
```
414
415
### Parsing Messages from Different Sources
416
417
```python
418
# From POST data (typical web framework usage)
419
post_data = {
420
'openid.ns': 'http://specs.openid.net/auth/2.0',
421
'openid.mode': 'id_res',
422
'openid.identity': 'https://user.example.com',
423
'openid.sig': 'signature_value'
424
}
425
message_from_post = Message.fromPostArgs(post_data)
426
427
# From key-value form (for check_authentication)
428
kvform_data = """ns:http://specs.openid.net/auth/2.0
429
mode:id_res
430
identity:https://user.example.com
431
sig:signature_value"""
432
message_from_kv = Message.fromKVForm(kvform_data)
433
434
# From OpenID args (without 'openid.' prefix)
435
openid_args = {
436
'ns': 'http://specs.openid.net/auth/2.0',
437
'mode': 'id_res',
438
'identity': 'https://user.example.com'
439
}
440
message_from_args = Message.fromOpenIDArgs(openid_args)
441
```
442
443
### Working with Extensions
444
445
```python
446
from openid.extensions import sreg
447
448
# Create message with extension
449
message = Message()
450
message.setArg('openid.ns', 'http://specs.openid.net/auth/2.0')
451
message.setArg('openid.mode', 'checkid_setup')
452
453
# Add SREG extension namespace
454
sreg_ns = 'http://openid.net/extensions/sreg/1.1'
455
message.setArg('openid.ns.sreg', sreg_ns)
456
message.setArg(sreg_ns, 'required', 'nickname,email')
457
message.setArg(sreg_ns, 'optional', 'fullname')
458
459
# Access extension arguments
460
sreg_args = message.getArgs(sreg_ns)
461
print("SREG arguments:", sreg_args)
462
463
# Check if extension is present
464
if message.hasKey(sreg_ns, 'required'):
465
required_fields = message.getArg(sreg_ns, 'required')
466
print(f"Required SREG fields: {required_fields}")
467
```
468
469
### Namespace Management
470
471
```python
472
from openid.message import NamespaceMap
473
474
# Create namespace map
475
ns_map = NamespaceMap()
476
477
# Add namespaces
478
openid_alias = ns_map.addAlias('http://specs.openid.net/auth/2.0', 'openid')
479
sreg_alias = ns_map.addAlias('http://openid.net/extensions/sreg/1.1', 'sreg')
480
ax_alias = ns_map.add('http://openid.net/srv/ax/1.0') # Auto-generated alias
481
482
print(f"OpenID alias: {openid_alias}")
483
print(f"SREG alias: {sreg_alias}")
484
print(f"AX alias: {ax_alias}")
485
486
# Check namespace definitions
487
if ns_map.isDefined('http://openid.net/extensions/sreg/1.1'):
488
alias = ns_map.getAlias('http://openid.net/extensions/sreg/1.1')
489
print(f"SREG namespace has alias: {alias}")
490
491
# Get all mappings
492
for namespace_uri, alias in ns_map.items():
493
print(f"{namespace_uri} -> {alias}")
494
```
495
496
### Message Copying and Modification
497
498
```python
499
# Create original message
500
original = Message()
501
original.setArg('openid.ns', 'http://specs.openid.net/auth/2.0')
502
original.setArg('openid.ns', 'mode', 'checkid_setup')
503
original.setArg('openid.ns', 'identity', 'https://user.example.com')
504
505
# Create copy
506
copy = original.copy()
507
508
# Modify copy without affecting original
509
copy.setArg('openid.ns', 'mode', 'checkid_immediate')
510
copy.setArg('openid.ns', 'return_to', 'https://consumer.example.com/return')
511
512
# Original is unchanged
513
print("Original mode:", original.getArg('openid.ns', 'mode'))
514
print("Copy mode:", copy.getArg('openid.ns', 'mode'))
515
```
516
517
### Global Namespace Registration
518
519
```python
520
from openid.message import registerNamespaceAlias
521
522
# Register commonly used namespace aliases
523
try:
524
registerNamespaceAlias('http://openid.net/extensions/sreg/1.1', 'sreg')
525
registerNamespaceAlias('http://openid.net/srv/ax/1.0', 'ax')
526
registerNamespaceAlias('http://schemas.openid.net/pape/policies/2007/06/phishing-resistant', 'pape')
527
print("Namespace aliases registered successfully")
528
except Exception as e:
529
print(f"Failed to register alias: {e}")
530
531
# Now these aliases will be used automatically when creating messages
532
message = Message()
533
# When adding SREG extension, 'sreg' alias will be preferred
534
```
535
536
## Types
537
538
```python { .api }
539
# OpenID namespace URIs
540
OPENID1_NS = 'http://openid.net/signon/1.0'
541
OPENID2_NS = 'http://specs.openid.net/auth/2.0'
542
543
# Special namespace symbols
544
NULL_NAMESPACE = object() # For arguments without namespace
545
OPENID_NS = object() # For current OpenID namespace
546
BARE_NS = object() # For bare arguments (no prefix)
547
548
# Standard OpenID arguments
549
IDENTIFIER_SELECT = 'http://specs.openid.net/auth/2.0/identifier_select'
550
SREG_URI = 'http://openid.net/sreg/1.0'
551
552
# URL length limits
553
OPENID1_URL_LIMIT = 2047 # Maximum URL length for OpenID 1.x
554
555
# Special sentinel for required parameters
556
no_default = object()
557
558
# Key-value form constants
559
KV_FORM_SEPARATOR = '\n'
560
KV_PAIR_SEPARATOR = ':'
561
562
# Exception types
563
class UndefinedOpenIDNamespace(Exception):
564
"""OpenID namespace not defined."""
565
566
class InvalidOpenIDNamespace(Exception):
567
"""Invalid OpenID namespace URI."""
568
569
class NamespaceAliasRegistrationError(Exception):
570
"""Namespace alias registration failed."""
571
```