or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

association-management.mdconsumer-auth.mddiscovery.mdextensions.mdindex.mdmessage-processing.mdserver-implementation.mdstorage-backends.mdutilities.md

message-processing.mddocs/

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

```