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

discovery.mddocs/

0

# OpenID Discovery

1

2

Complete OpenID service discovery implementation that enables consumers to discover OpenID service endpoints from user identifiers. The discovery system supports both Yadis-based XRDS discovery and HTML-based discovery with automatic fallback mechanisms, handling OpenID 1.0, 1.1, and 2.0 protocols.

3

4

## Core Imports

5

6

```python

7

from openid.consumer.discover import (

8

discover,

9

OpenIDServiceEndpoint,

10

normalizeURL,

11

normalizeXRI,

12

discoverYadis,

13

discoverXRI,

14

discoverURI,

15

discoverNoYadis,

16

findOPLocalIdentifier,

17

arrangeByType,

18

getOPOrUserServices,

19

OPENID_1_0_NS,

20

OPENID_IDP_2_0_TYPE,

21

OPENID_2_0_TYPE,

22

OPENID_1_1_TYPE,

23

OPENID_1_0_TYPE

24

)

25

```

26

27

## Capabilities

28

29

### Primary Discovery Interface

30

31

The main discovery function that handles both XRI and URL identifiers with automatic protocol detection.

32

33

```python { .api }

34

def discover(identifier):

35

"""

36

Discover OpenID service endpoints for a user identifier.

37

38

Parameters:

39

- identifier: str, user's OpenID identifier (URL or XRI)

40

41

Returns:

42

tuple: (claimed_id, services) where:

43

- claimed_id: str, normalized claimed identifier

44

- services: list of OpenIDServiceEndpoint objects, ordered by preference

45

46

Raises:

47

DiscoveryFailure: when discovery fails for any reason

48

"""

49

```

50

51

### Service Endpoint Representation

52

53

Core class representing an OpenID service endpoint with methods for protocol compatibility and extension support.

54

55

```python { .api }

56

class OpenIDServiceEndpoint:

57

def __init__(self):

58

"""

59

Initialize an empty OpenID service endpoint.

60

61

Attributes:

62

- claimed_id: str or None, the claimed identifier

63

- server_url: str or None, the OpenID server endpoint URL

64

- type_uris: list, supported OpenID type URIs

65

- local_id: str or None, the local identifier at the server

66

- canonicalID: str or None, canonical XRI identifier

67

- used_yadis: bool, whether this endpoint was discovered via Yadis

68

- display_identifier: str or None, identifier for display purposes

69

"""

70

71

def usesExtension(self, extension_uri):

72

"""

73

Check if this endpoint supports a specific extension.

74

75

Parameters:

76

- extension_uri: str, extension type URI to check

77

78

Returns:

79

bool: True if extension is supported

80

"""

81

82

def preferredNamespace(self):

83

"""

84

Get the preferred OpenID namespace for this endpoint.

85

86

Returns:

87

str: OPENID_2_0_MESSAGE_NS or OPENID_1_0_MESSAGE_NS

88

"""

89

90

def supportsType(self, type_uri):

91

"""

92

Check if this endpoint supports a specific OpenID type.

93

94

Parameters:

95

- type_uri: str, OpenID type URI to check

96

97

Returns:

98

bool: True if type is supported (considers server endpoints as supporting signon)

99

"""

100

101

def compatibilityMode(self):

102

"""

103

Check if this endpoint requires OpenID 1.x compatibility mode.

104

105

Returns:

106

bool: True if OpenID 1.x compatibility is needed

107

"""

108

109

def isOPIdentifier(self):

110

"""

111

Check if this is an OP Identifier endpoint (OpenID 2.0 server type).

112

113

Returns:

114

bool: True if this is an OP Identifier endpoint

115

"""

116

117

def getLocalID(self):

118

"""

119

Get the identifier to send as openid.identity parameter.

120

121

Returns:

122

str: local identifier or claimed identifier if no local ID

123

"""

124

125

def getDisplayIdentifier(self):

126

"""

127

Get the identifier for display purposes.

128

129

Returns:

130

str: display identifier or claimed identifier with fragment removed

131

"""

132

```

133

134

### Service Endpoint Creation Methods

135

136

Factory methods for creating OpenID service endpoints from various sources.

137

138

```python { .api }

139

@classmethod

140

def fromBasicServiceEndpoint(cls, endpoint):

141

"""

142

Create OpenIDServiceEndpoint from a basic Yadis service endpoint.

143

144

Parameters:

145

- endpoint: BasicServiceEndpoint object from Yadis discovery

146

147

Returns:

148

OpenIDServiceEndpoint or None if endpoint is not OpenID-compatible

149

"""

150

151

@classmethod

152

def fromHTML(cls, uri, html):

153

"""

154

Parse HTML document for OpenID link rel discovery.

155

156

Parameters:

157

- uri: str, the document URI

158

- html: str, HTML document content

159

160

Returns:

161

list: OpenIDServiceEndpoint objects found in HTML

162

"""

163

164

@classmethod

165

def fromXRDS(cls, uri, xrds):

166

"""

167

Parse XRDS document for OpenID service endpoints.

168

169

Parameters:

170

- uri: str, the document URI

171

- xrds: str, XRDS document content

172

173

Returns:

174

list: OpenIDServiceEndpoint objects found in XRDS

175

176

Raises:

177

XRDSError: when XRDS document cannot be parsed

178

"""

179

180

@classmethod

181

def fromDiscoveryResult(cls, discoveryResult):

182

"""

183

Create endpoints from a Yadis DiscoveryResult object.

184

185

Parameters:

186

- discoveryResult: DiscoveryResult object from Yadis discovery

187

188

Returns:

189

list: OpenIDServiceEndpoint objects

190

191

Raises:

192

XRDSError: when XRDS document cannot be parsed

193

"""

194

195

@classmethod

196

def fromOPEndpointURL(cls, op_endpoint_url):

197

"""

198

Create OP Identifier endpoint for a known OP endpoint URL.

199

200

Parameters:

201

- op_endpoint_url: str, the OP endpoint URL

202

203

Returns:

204

OpenIDServiceEndpoint: OP Identifier endpoint

205

"""

206

```

207

208

### URL and XRI Normalization

209

210

Utility functions for normalizing identifiers before discovery.

211

212

```python { .api }

213

def normalizeURL(url):

214

"""

215

Normalize a URL identifier for OpenID discovery.

216

217

Parameters:

218

- url: str, URL to normalize

219

220

Returns:

221

str: normalized URL with fragment removed

222

223

Raises:

224

DiscoveryFailure: when URL normalization fails

225

"""

226

227

def normalizeXRI(xri):

228

"""

229

Normalize an XRI identifier by removing xri:// scheme if present.

230

231

Parameters:

232

- xri: str, XRI identifier to normalize

233

234

Returns:

235

str: normalized XRI without scheme

236

"""

237

```

238

239

### Discovery Protocol Implementations

240

241

Specific discovery methods for different protocols and fallback scenarios.

242

243

```python { .api }

244

def discoverYadis(uri):

245

"""

246

Perform Yadis-based OpenID discovery with HTML fallback.

247

248

Parameters:

249

- uri: str, normalized identity URL

250

251

Returns:

252

tuple: (claimed_id, services) where:

253

- claimed_id: str, discovered claimed identifier

254

- services: list of OpenIDServiceEndpoint objects

255

256

Raises:

257

DiscoveryFailure: when discovery fails

258

"""

259

260

def discoverXRI(iname):

261

"""

262

Perform XRI resolution for OpenID discovery.

263

264

Parameters:

265

- iname: str, XRI identifier to resolve

266

267

Returns:

268

tuple: (claimed_id, services) where:

269

- claimed_id: str, the normalized XRI

270

- services: list of OpenIDServiceEndpoint objects with canonicalID set

271

272

Raises:

273

XRDSError: when XRI resolution fails

274

"""

275

276

def discoverURI(uri):

277

"""

278

Perform OpenID discovery for URI identifiers.

279

280

Parameters:

281

- uri: str, URI identifier (may need http:// prefix)

282

283

Returns:

284

tuple: (claimed_id, services) where:

285

- claimed_id: str, normalized claimed identifier

286

- services: list of OpenIDServiceEndpoint objects

287

288

Raises:

289

DiscoveryFailure: when URI scheme is invalid or discovery fails

290

"""

291

292

def discoverNoYadis(uri):

293

"""

294

Perform HTML-only OpenID discovery without Yadis.

295

296

Parameters:

297

- uri: str, identity URL

298

299

Returns:

300

tuple: (claimed_id, services) where:

301

- claimed_id: str, final URL after redirects

302

- services: list of OpenIDServiceEndpoint objects from HTML

303

304

Raises:

305

DiscoveryFailure: when HTTP request fails

306

"""

307

```

308

309

### Service Processing Utilities

310

311

Utility functions for processing and ordering discovered services.

312

313

```python { .api }

314

def arrangeByType(service_list, preferred_types):

315

"""

316

Reorder services by type preference.

317

318

Parameters:

319

- service_list: list, OpenIDServiceEndpoint objects to reorder

320

- preferred_types: list, type URIs in order of preference

321

322

Returns:

323

list: reordered services with preferred types first

324

"""

325

326

def getOPOrUserServices(openid_services):

327

"""

328

Extract OP Identifier services or return user services ordered by preference.

329

330

Parameters:

331

- openid_services: list, OpenIDServiceEndpoint objects

332

333

Returns:

334

list: OP Identifier services if found, otherwise user services ordered by type preference

335

"""

336

337

def findOPLocalIdentifier(service_element, type_uris):

338

"""

339

Find OP-Local Identifier from XRDS service element.

340

341

Parameters:

342

- service_element: ElementTree.Node, xrd:Service element

343

- type_uris: list, xrd:Type values for this service

344

345

Returns:

346

str or None: OP-Local Identifier if present

347

348

Raises:

349

DiscoveryFailure: when multiple conflicting LocalID tags are found

350

"""

351

```

352

353

## Usage Examples

354

355

### Basic Discovery

356

357

```python

358

from openid.consumer.discover import discover

359

360

try:

361

claimed_id, services = discover("https://example.com/user")

362

363

if services:

364

# Use the first (highest priority) service

365

service = services[0]

366

print(f"Server URL: {service.server_url}")

367

print(f"OpenID Version: {'2.0' if not service.compatibilityMode() else '1.x'}")

368

print(f"OP Identifier: {service.isOPIdentifier()}")

369

else:

370

print("No OpenID services found")

371

372

except DiscoveryFailure as e:

373

print(f"Discovery failed: {e}")

374

```

375

376

### XRI Discovery

377

378

```python

379

from openid.consumer.discover import discoverXRI

380

381

try:

382

claimed_id, services = discoverXRI("=example*user")

383

384

for service in services:

385

print(f"Canonical ID: {service.canonicalID}")

386

print(f"Server: {service.server_url}")

387

print(f"Local ID: {service.getLocalID()}")

388

389

except XRDSError as e:

390

print(f"XRI resolution failed: {e}")

391

```

392

393

### Service Endpoint Analysis

394

395

```python

396

from openid.consumer.discover import discover, OPENID_2_0_TYPE

397

398

claimed_id, services = discover("https://example.com/user")

399

400

for service in services:

401

# Check OpenID version support

402

if service.supportsType(OPENID_2_0_TYPE):

403

print("Supports OpenID 2.0")

404

405

# Check for extensions

406

if service.usesExtension("http://openid.net/extensions/sreg/1.1"):

407

print("Supports Simple Registration extension")

408

409

# Get appropriate namespace

410

namespace = service.preferredNamespace()

411

print(f"Preferred namespace: {namespace}")

412

```

413

414

### HTML Link Discovery

415

416

```python

417

from openid.consumer.discover import OpenIDServiceEndpoint

418

419

html_content = '''

420

<html>

421

<head>

422

<link rel="openid2.provider" href="https://example.com/openid">

423

<link rel="openid2.local_id" href="https://example.com/user/local">

424

</head>

425

</html>

426

'''

427

428

services = OpenIDServiceEndpoint.fromHTML("https://example.com/user", html_content)

429

430

for service in services:

431

print(f"Provider: {service.server_url}")

432

print(f"Local ID: {service.local_id}")

433

```

434

435

## Constants

436

437

```python { .api }

438

# OpenID Namespace URIs

439

OPENID_1_0_NS = 'http://openid.net/xmlns/1.0'

440

441

# OpenID Type URIs (in preference order)

442

OPENID_IDP_2_0_TYPE = 'http://specs.openid.net/auth/2.0/server' # OP Identifier

443

OPENID_2_0_TYPE = 'http://specs.openid.net/auth/2.0/signon' # User Identifier

444

OPENID_1_1_TYPE = 'http://openid.net/signon/1.1' # OpenID 1.1

445

OPENID_1_0_TYPE = 'http://openid.net/signon/1.0' # OpenID 1.0

446

447

# OpenIDServiceEndpoint class constants

448

openid_type_uris = [

449

OPENID_IDP_2_0_TYPE,

450

OPENID_2_0_TYPE,

451

OPENID_1_1_TYPE,

452

OPENID_1_0_TYPE

453

]

454

```

455

456

## Types

457

458

```python { .api }

459

# Discovery result tuple

460

DiscoveryResult = tuple[str, list[OpenIDServiceEndpoint]]

461

462

# Exception types

463

class DiscoveryFailure(Exception):

464

"""Raised when OpenID discovery fails."""

465

466

class XRDSError(Exception):

467

"""Raised when XRDS parsing fails."""

468

```