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

server-implementation.mddocs/

0

# Server Implementation

1

2

Complete OpenID server functionality for identity providers. The server handles association requests, authentication requests, and verification with support for multiple session types and response encoding formats.

3

4

## Capabilities

5

6

### Server Initialization

7

8

Initialize an OpenID server with storage backend and endpoint configuration.

9

10

```python { .api }

11

class Server:

12

def __init__(self, store, op_endpoint=None, signatoryClass=Signatory, encoderClass=SigningEncoder, decoderClass=Decoder):

13

"""

14

Initialize OpenID server.

15

16

Parameters:

17

- store: OpenIDStore instance for persistent storage

18

- op_endpoint: str, server endpoint URL

19

- signatoryClass: class, signatory implementation (default: Signatory)

20

- encoderClass: class, encoder implementation (default: SigningEncoder)

21

- decoderClass: class, decoder implementation (default: Decoder)

22

"""

23

24

def decodeRequest(self, query):

25

"""

26

Decode incoming OpenID request from query parameters.

27

28

Parameters:

29

- query: dict, query parameters from HTTP request

30

31

Returns:

32

OpenIDRequest subclass or None if invalid

33

"""

34

35

def encodeResponse(self, response):

36

"""

37

Encode OpenID response for HTTP transmission.

38

39

Parameters:

40

- response: OpenIDResponse object

41

42

Returns:

43

WebResponse object with HTTP status, headers, and body

44

"""

45

46

def handleRequest(self, request):

47

"""

48

Handle decoded OpenID request and generate appropriate response.

49

50

Parameters:

51

- request: OpenIDRequest object

52

53

Returns:

54

OpenIDResponse object

55

"""

56

57

def openid_check_authentication(self, request):

58

"""

59

Handle check_authentication request for verifying signatures.

60

61

Parameters:

62

- request: CheckAuthRequest object

63

64

Returns:

65

OpenIDResponse with verification result

66

"""

67

68

def openid_associate(self, request):

69

"""

70

Handle associate request for establishing shared secrets.

71

72

Parameters:

73

- request: AssociateRequest object

74

75

Returns:

76

OpenIDResponse with association data or error

77

"""

78

```

79

80

### Base Request Classes

81

82

Handle the base OpenID request types and common functionality.

83

84

```python { .api }

85

class OpenIDRequest:

86

"""Base class for all OpenID requests.

87

88

Represents an incoming OpenID request with common attributes and methods.

89

"""

90

91

mode = None # The openid.mode parameter value

92

93

def __init__(self):

94

"""

95

Initialize base OpenID request.

96

97

The mode attribute should be set by subclasses to indicate

98

the specific OpenID operation being requested.

99

"""

100

```

101

102

### Authentication Requests

103

104

Handle user authentication requests with trust root validation and response generation.

105

106

```python { .api }

107

class CheckIDRequest:

108

"""OpenID authentication request (checkid_setup or checkid_immediate)."""

109

110

@classmethod

111

def fromMessage(cls, message, op_endpoint):

112

"""

113

Create CheckIDRequest from OpenID message.

114

115

Parameters:

116

- message: Message object

117

- op_endpoint: str, server endpoint URL

118

119

Returns:

120

CheckIDRequest object

121

"""

122

123

def answer(self, allow, server_url=None, identity=None, claimed_id=None):

124

"""

125

Generate response to authentication request.

126

127

Parameters:

128

- allow: bool, whether to allow authentication

129

- server_url: str, server URL for response

130

- identity: str, user's identity URL

131

- claimed_id: str, user's claimed identifier

132

133

Returns:

134

OpenIDResponse object

135

"""

136

137

def encodeToURL(self, server_url):

138

"""

139

Encode request as URL for redirecting user.

140

141

Parameters:

142

- server_url: str, server base URL

143

144

Returns:

145

str, encoded URL

146

"""

147

148

def getCancelURL(self):

149

"""

150

Get URL for user cancellation.

151

152

Returns:

153

str, cancel URL

154

"""

155

156

def idSelect(self):

157

"""

158

Check if request uses identifier select mode.

159

160

Returns:

161

bool, True if identifier select

162

"""

163

164

def trustRootValid(self):

165

"""

166

Validate the trust root against return_to URL.

167

168

Returns:

169

bool, True if trust root is valid

170

"""

171

172

def returnToVerified(self):

173

"""

174

Verify the return_to URL is valid and matches trust root.

175

176

Returns:

177

bool, True if return_to is verified

178

"""

179

180

def getClaimedID(self):

181

"""

182

Get the claimed identifier from this request.

183

184

Returns:

185

str, the claimed identifier (OpenID 2.0) or identity (OpenID 1.x)

186

"""

187

188

def getTrustRoot(self):

189

"""

190

Get the trust root (realm) from this request.

191

192

Returns:

193

str, the trust root URL that identifies the requesting party

194

"""

195

```

196

197

### Association Requests

198

199

Handle association establishment for shared secret creation between consumer and server.

200

201

```python { .api }

202

class AssociateRequest(OpenIDRequest):

203

"""OpenID association request.

204

205

A request to establish an association between consumer and server.

206

207

Attributes:

208

- mode: "associate"

209

- assoc_type: str, type of association ("HMAC-SHA1" or "HMAC-SHA256")

210

- session: ServerSession, session handler for this association type

211

- session_classes: dict, mapping of session types to handler classes

212

"""

213

214

mode = "associate"

215

session_classes = {

216

'no-encryption': 'PlainTextServerSession',

217

'DH-SHA1': 'DiffieHellmanSHA1ServerSession',

218

'DH-SHA256': 'DiffieHellmanSHA256ServerSession',

219

}

220

221

def __init__(self, session, assoc_type):

222

"""

223

Construct AssociateRequest.

224

225

Parameters:

226

- session: ServerSession object for handling this association type

227

- assoc_type: str, association type ("HMAC-SHA1" or "HMAC-SHA256")

228

"""

229

230

@classmethod

231

def fromMessage(cls, message, op_endpoint=None):

232

"""

233

Create AssociateRequest from OpenID message.

234

235

Parameters:

236

- message: Message object

237

- op_endpoint: str, server endpoint URL

238

239

Returns:

240

AssociateRequest object

241

"""

242

243

def answer(self, assoc):

244

"""

245

Generate successful association response.

246

247

Parameters:

248

- assoc: Association object with shared secret

249

250

Returns:

251

OpenIDResponse with association data

252

"""

253

254

def answerUnsupported(self, message, preferred_association_type=None, preferred_session_type=None):

255

"""

256

Generate unsupported association type response.

257

258

Parameters:

259

- message: str, error message

260

- preferred_association_type: str, preferred association type

261

- preferred_session_type: str, preferred session type

262

263

Returns:

264

OpenIDResponse with error and preferences

265

"""

266

```

267

268

### Check Authentication Requests

269

270

Handle signature verification requests from consumers.

271

272

```python { .api }

273

class CheckAuthRequest(OpenIDRequest):

274

"""OpenID check_authentication request.

275

276

A request to verify the validity of a previous response signature.

277

278

Attributes:

279

- mode: "check_authentication"

280

- assoc_handle: str, association handle the response was signed with

281

- signed: Message, the message with signature to verify

282

- invalidate_handle: str, optional association handle to check validity

283

"""

284

285

mode = "check_authentication"

286

required_fields = ["identity", "return_to", "response_nonce"]

287

288

def __init__(self, assoc_handle, signed, invalidate_handle=None):

289

"""

290

Construct CheckAuthRequest.

291

292

Parameters:

293

- assoc_handle: str, association handle for signature verification

294

- signed: Message, signed message to verify

295

- invalidate_handle: str, optional handle to invalidate

296

"""

297

298

@classmethod

299

def fromMessage(cls, message, op_endpoint=None):

300

"""

301

Create CheckAuthRequest from OpenID message.

302

303

Parameters:

304

- message: Message object

305

- op_endpoint: str, server endpoint URL

306

307

Returns:

308

CheckAuthRequest object

309

"""

310

311

def answer(self, signatory):

312

"""

313

Generate signature verification response.

314

315

Parameters:

316

- signatory: Signatory object for verification

317

318

Returns:

319

OpenIDResponse with verification result

320

"""

321

```

322

323

### Response Generation

324

325

Generate and encode OpenID responses with proper formatting and signing.

326

327

```python { .api }

328

class OpenIDResponse:

329

"""Base class for OpenID responses.

330

331

Represents a response to an OpenID request with fields and encoding methods.

332

333

Attributes:

334

- request: OpenIDRequest, the original request being responded to

335

- fields: Message, response fields and parameters

336

"""

337

338

def __init__(self, request):

339

"""

340

Initialize OpenID response.

341

342

Parameters:

343

- request: OpenIDRequest, the request this response answers

344

"""

345

346

def toFormMarkup(self, form_tag_attrs=None):

347

"""

348

Generate HTML form markup for response.

349

350

Parameters:

351

- form_tag_attrs: dict, additional form tag attributes

352

353

Returns:

354

str, HTML form markup

355

"""

356

357

def toHTML(self, form_tag_attrs=None):

358

"""

359

Generate complete HTML page with auto-submitting form.

360

361

Parameters:

362

- form_tag_attrs: dict, additional form tag attributes

363

364

Returns:

365

str, complete HTML page

366

"""

367

368

def renderAsForm(self):

369

"""

370

Check if response should be rendered as HTML form.

371

372

Returns:

373

bool, True if form rendering required

374

"""

375

376

def needsSigning(self):

377

"""

378

Check if response needs to be signed.

379

380

Returns:

381

bool, True if signing required

382

"""

383

384

def whichEncoding(self):

385

"""

386

Determine appropriate encoding method for response.

387

388

Returns:

389

tuple, encoding method identifier

390

"""

391

392

def encodeToURL(self):

393

"""

394

Encode response as URL query parameters.

395

396

Returns:

397

str, URL with encoded response

398

"""

399

400

def addExtension(self, extension_response):

401

"""

402

Add extension data to response.

403

404

Parameters:

405

- extension_response: Extension response object

406

"""

407

408

def encodeToKVForm(self):

409

"""

410

Encode response as key-value form.

411

412

Returns:

413

str, key-value form data

414

"""

415

416

class WebResponse:

417

"""HTTP response wrapper for OpenID server responses.

418

419

Encapsulates HTTP response data including status code, headers, and body

420

for transmission back to the client.

421

422

Attributes:

423

- code: int, HTTP status code (default: 200)

424

- headers: dict, HTTP response headers (default: {})

425

- body: str/bytes, response body content

426

"""

427

428

def __init__(self, code=200, headers=None, body=""):

429

"""

430

Initialize HTTP response.

431

432

Parameters:

433

- code: int, HTTP status code

434

- headers: dict, HTTP headers

435

- body: str, response body

436

"""

437

```

438

439

### Message Signing

440

441

Handle message signing and verification with association management.

442

443

```python { .api }

444

class Signatory:

445

"""Message signing and verification component.

446

447

Handles message signing with associations and signature verification.

448

Manages association lifecycle including creation and invalidation.

449

450

Attributes:

451

- store: OpenIDStore, storage backend for associations and nonces

452

"""

453

454

def __init__(self, store):

455

"""

456

Initialize signatory with storage backend.

457

458

Parameters:

459

- store: OpenIDStore instance

460

"""

461

462

def verify(self, assoc_handle, message):

463

"""

464

Verify message signature using association.

465

466

Parameters:

467

- assoc_handle: str, association handle

468

- message: Message object to verify

469

470

Returns:

471

bool, True if signature valid

472

"""

473

474

def sign(self, response):

475

"""

476

Sign response message.

477

478

Parameters:

479

- response: OpenIDResponse object to sign

480

481

Returns:

482

Association object used for signing

483

"""

484

485

def createAssociation(self, dumb=True, assoc_type='HMAC-SHA1'):

486

"""

487

Create new association.

488

489

Parameters:

490

- dumb: bool, whether association is for dumb mode

491

- assoc_type: str, association type ('HMAC-SHA1' or 'HMAC-SHA256')

492

493

Returns:

494

Association object

495

"""

496

497

def getAssociation(self, assoc_handle, dumb, checkExpiration=True):

498

"""

499

Retrieve association by handle.

500

501

Parameters:

502

- assoc_handle: str, association handle

503

- dumb: bool, whether association is for dumb mode

504

- checkExpiration: bool, whether to check expiration

505

506

Returns:

507

Association object or None

508

"""

509

510

def invalidate(self, assoc_handle, dumb):

511

"""

512

Invalidate association.

513

514

Parameters:

515

- assoc_handle: str, association handle to invalidate

516

- dumb: bool, whether association is for dumb mode

517

"""

518

```

519

520

### Message Encoding and Decoding

521

522

Handle message encoding for responses and decoding of incoming requests.

523

524

```python { .api }

525

class Encoder:

526

"""Base encoder for OpenID responses to WebResponse format.

527

528

Encodes OpenIDResponse objects into WebResponse objects for HTTP transmission.

529

530

Attributes:

531

- responseFactory: class, WebResponse factory for creating responses (default: WebResponse)

532

"""

533

534

responseFactory = 'WebResponse'

535

536

def encode(self, response):

537

"""

538

Encode OpenIDResponse to WebResponse.

539

540

Parameters:

541

- response: OpenIDResponse, response object to encode

542

543

Returns:

544

WebResponse object with appropriate encoding (URL redirect, HTML form, or key-value form)

545

546

Raises:

547

EncodingError when response cannot be encoded as protocol message

548

"""

549

550

class SigningEncoder(Encoder):

551

"""Encoder that signs responses before encoding.

552

553

Extends Encoder to automatically sign responses that require signing

554

before converting to WebResponse format.

555

556

Attributes:

557

- signatory: Signatory, component used for signing responses

558

"""

559

560

def __init__(self, signatory):

561

"""

562

Create SigningEncoder with signatory.

563

564

Parameters:

565

- signatory: Signatory, component for message signing

566

"""

567

568

def encode(self, response):

569

"""

570

Encode OpenIDResponse to WebResponse, signing first if needed.

571

572

Parameters:

573

- response: OpenIDResponse, response object to encode and potentially sign

574

575

Returns:

576

WebResponse object with signed response data

577

578

Raises:

579

EncodingError when response cannot be encoded

580

AlreadySigned when response is already signed

581

"""

582

583

class Decoder:

584

"""Decoder for incoming OpenID requests from query parameters.

585

586

Transforms HTTP query parameters into appropriate OpenIDRequest objects

587

based on the openid.mode parameter.

588

589

Attributes:

590

- server: Server, the server instance this decoder works for

591

- _handlers: dict, mapping of modes to request handler classes

592

"""

593

594

_handlers = {

595

'checkid_setup': 'CheckIDRequest.fromMessage',

596

'checkid_immediate': 'CheckIDRequest.fromMessage',

597

'check_authentication': 'CheckAuthRequest.fromMessage',

598

'associate': 'AssociateRequest.fromMessage',

599

}

600

601

def __init__(self, server):

602

"""

603

Construct Decoder.

604

605

Parameters:

606

- server: Server, the server instance for request processing

607

"""

608

609

def decode(self, query):

610

"""

611

Transform query parameters into OpenIDRequest.

612

613

Parameters:

614

- query: dict, HTTP query parameters with each key mapping to one value

615

616

Returns:

617

OpenIDRequest subclass instance or None if not an OpenID request

618

619

Raises:

620

ProtocolError when query appears to be OpenID request but is invalid

621

"""

622

623

def defaultDecoder(self, message, server):

624

"""

625

Called when no handler found for the request mode.

626

627

Parameters:

628

- message: Message, the decoded message object

629

- server: Server, the server instance

630

631

Returns:

632

None (default behavior for unknown modes)

633

"""

634

```

635

636

### Session Types

637

638

Handle different session types for association establishment.

639

640

```python { .api }

641

class PlainTextServerSession:

642

"""Plain text session type (no encryption).

643

644

Handles association requests where no encryption is used for the shared secret.

645

The secret is transmitted in base64 encoded form.

646

647

Attributes:

648

- session_type: "no-encryption"

649

- allowed_assoc_types: list, supported association types ["HMAC-SHA1", "HMAC-SHA256"]

650

"""

651

652

session_type = 'no-encryption'

653

allowed_assoc_types = ['HMAC-SHA1', 'HMAC-SHA256']

654

655

@classmethod

656

def fromMessage(cls, unused_request):

657

"""

658

Create session from association request.

659

660

Parameters:

661

- unused_request: AssociateRequest object (not used for plain text)

662

663

Returns:

664

PlainTextServerSession object

665

"""

666

667

def answer(self, secret):

668

"""

669

Generate session response with shared secret.

670

671

Parameters:

672

- secret: bytes, shared secret

673

674

Returns:

675

dict, session response data

676

"""

677

678

class DiffieHellmanSHA1ServerSession:

679

"""Diffie-Hellman SHA1 session type.

680

681

Handles association requests using Diffie-Hellman key exchange with SHA1 hashing.

682

Encrypts the shared secret using the consumer's public key.

683

684

Attributes:

685

- session_type: "DH-SHA1"

686

- hash_func: function, SHA1 hash function for key derivation

687

- allowed_assoc_types: list, supported association types ["HMAC-SHA1"]

688

- dh: DiffieHellman, DH algorithm values for this request

689

- consumer_pubkey: long, consumer's public key from the request

690

"""

691

692

session_type = 'DH-SHA1'

693

hash_func = 'sha1'

694

allowed_assoc_types = ['HMAC-SHA1']

695

696

def __init__(self, dh, consumer_pubkey):

697

"""

698

Initialize DH SHA1 session.

699

700

Parameters:

701

- dh: DiffieHellman, DH algorithm parameters

702

- consumer_pubkey: long, consumer's public key

703

"""

704

705

@classmethod

706

def fromMessage(cls, message):

707

"""

708

Create DH session from association request.

709

710

Parameters:

711

- message: Message object with DH parameters

712

713

Returns:

714

DiffieHellmanSHA1ServerSession object

715

"""

716

717

def answer(self, secret):

718

"""

719

Generate DH session response with encrypted secret.

720

721

Parameters:

722

- secret: bytes, shared secret

723

724

Returns:

725

dict, DH session response data

726

"""

727

728

class DiffieHellmanSHA256ServerSession(DiffieHellmanSHA1ServerSession):

729

"""Diffie-Hellman SHA256 session type.

730

731

Extends DiffieHellmanSHA1ServerSession with SHA256 hashing instead of SHA1.

732

Provides stronger cryptographic hashing for the key derivation process.

733

734

Attributes:

735

- session_type: "DH-SHA256"

736

- hash_func: function, SHA256 hash function for key derivation

737

- allowed_assoc_types: list, supported association types ["HMAC-SHA256"]

738

"""

739

740

session_type = 'DH-SHA256'

741

hash_func = 'sha256'

742

allowed_assoc_types = ['HMAC-SHA256']

743

744

@classmethod

745

def fromMessage(cls, message):

746

"""

747

Create DH SHA256 session from association request.

748

749

Parameters:

750

- message: Message object with DH parameters

751

752

Returns:

753

DiffieHellmanSHA256ServerSession object

754

"""

755

756

def answer(self, secret):

757

"""

758

Generate DH SHA256 session response.

759

760

Parameters:

761

- secret: bytes, shared secret

762

763

Returns:

764

dict, DH session response data

765

"""

766

```

767

768

## Usage Examples

769

770

### Basic Server Setup

771

772

```python

773

from openid.server import server

774

from openid.store.filestore import FileOpenIDStore

775

776

# Initialize server

777

store = FileOpenIDStore('/tmp/openid_store')

778

openid_server = server.Server(store, op_endpoint="https://myop.com/openid")

779

780

# Handle incoming request

781

def handle_openid_request(request):

782

query = dict(request.GET.items())

783

openid_request = openid_server.decodeRequest(query)

784

785

if openid_request is None:

786

return HttpResponse("Invalid OpenID request", status=400)

787

788

if isinstance(openid_request, server.CheckIDRequest):

789

return handle_checkid_request(openid_request)

790

elif isinstance(openid_request, server.AssociateRequest):

791

return handle_associate_request(openid_request)

792

elif isinstance(openid_request, server.CheckAuthRequest):

793

return handle_checkauth_request(openid_request)

794

```

795

796

### Handling Authentication Requests

797

798

```python

799

def handle_checkid_request(openid_request):

800

# Validate trust root and return_to

801

if not openid_request.trustRootValid():

802

return HttpResponse("Invalid trust root", status=400)

803

804

if not openid_request.returnToVerified():

805

return HttpResponse("Invalid return_to URL", status=400)

806

807

# Check if user is authenticated

808

if user_is_authenticated(request):

809

# User is authenticated, generate positive response

810

openid_response = openid_request.answer(

811

allow=True,

812

server_url="https://myop.com/openid",

813

identity=get_user_identity_url(),

814

claimed_id=get_user_claimed_id()

815

)

816

817

# Add extension data if requested

818

sreg_request = sreg.SRegRequest.fromOpenIDRequest(openid_request)

819

if sreg_request:

820

sreg_response = sreg.SRegResponse.extractResponse(

821

sreg_request,

822

get_user_profile_data()

823

)

824

openid_response.addExtension(sreg_response)

825

else:

826

# User not authenticated, redirect to login

827

if openid_request.immediate:

828

# Immediate mode - cannot show login page

829

openid_response = openid_request.answer(allow=False)

830

else:

831

# Setup mode - redirect to login page

832

login_url = build_login_url(openid_request)

833

return HttpResponseRedirect(login_url)

834

835

# Encode and return response

836

web_response = openid_server.encodeResponse(openid_response)

837

return HttpResponse(

838

web_response.body,

839

status=web_response.code,

840

content_type='text/html'

841

)

842

```

843

844

### Handling Association Requests

845

846

```python

847

def handle_associate_request(openid_request):

848

openid_response = openid_server.openid_associate(openid_request)

849

web_response = openid_server.encodeResponse(openid_response)

850

return HttpResponse(

851

web_response.body,

852

status=web_response.code,

853

content_type='text/plain'

854

)

855

```

856

857

## Types

858

859

```python { .api }

860

# HTTP Status Codes

861

HTTP_OK = 200

862

HTTP_REDIRECT = 302

863

HTTP_ERROR = 400

864

865

# Request Modes

866

BROWSER_REQUEST_MODES = ['checkid_setup', 'checkid_immediate']

867

868

# Encoding Types

869

ENCODE_KVFORM = ('kvform',)

870

ENCODE_URL = ('URL/redirect',)

871

ENCODE_HTML_FORM = ('HTML form',)

872

873

# Exception Types

874

class ProtocolError(Exception):

875

"""OpenID protocol violation.

876

877

Raised when an OpenID protocol error occurs during request processing.

878

879

Attributes:

880

- message: Message, the OpenID message that caused the error

881

- text: str, human-readable error description

882

- reference: str, optional reference to specification

883

- contact: str, optional contact information for debugging

884

"""

885

886

def __init__(self, message, text=None, reference=None, contact=None):

887

"""

888

Initialize protocol error.

889

890

Parameters:

891

- message: Message, OpenID message causing the error

892

- text: str, error description

893

- reference: str, specification reference

894

- contact: str, contact information

895

"""

896

897

class VersionError(Exception):

898

"""Unsupported OpenID version.

899

900

Raised when an operation is attempted that is not compatible with

901

the protocol version being used.

902

"""

903

904

class NoReturnToError(Exception):

905

"""Missing return_to parameter.

906

907

Raised when a response cannot be generated because the request

908

contains no return_to URL for redirecting the user back.

909

"""

910

911

class EncodingError(Exception):

912

"""Response encoding error.

913

914

Raised when a response cannot be encoded as a protocol message

915

and should probably be rendered and shown to the user.

916

917

Attributes:

918

- response: OpenIDResponse, the response that failed to encode

919

- explanation: str, optional detailed explanation of the error

920

"""

921

922

def __init__(self, response, explanation=None):

923

"""

924

Initialize encoding error.

925

926

Parameters:

927

- response: OpenIDResponse, response that failed to encode

928

- explanation: str, detailed error explanation

929

"""

930

931

class AlreadySigned(EncodingError):

932

"""Response already signed.

933

934

Raised when attempting to sign a response that has already been signed.

935

Inherits from EncodingError.

936

"""

937

938

class UntrustedReturnURL(ProtocolError):

939

"""return_to URL not trusted.

940

941

Raised when a return_to URL is outside the declared trust_root.

942

Inherits from ProtocolError.

943

944

Attributes:

945

- return_to: str, the untrusted return_to URL

946

- trust_root: str, the declared trust root

947

"""

948

949

def __init__(self, message, return_to, trust_root):

950

"""

951

Initialize untrusted return URL error.

952

953

Parameters:

954

- message: Message, the OpenID message

955

- return_to: str, the untrusted return_to URL

956

- trust_root: str, the declared trust root

957

"""

958

959

class MalformedReturnURL(ProtocolError):

960

"""Malformed return_to URL.

961

962

Raised when the return_to URL doesn't look like a valid URL.

963

Inherits from ProtocolError.

964

965

Attributes:

966

- return_to: str, the malformed return_to URL

967

"""

968

969

def __init__(self, openid_message, return_to):

970

"""

971

Initialize malformed return URL error.

972

973

Parameters:

974

- openid_message: Message, the OpenID message

975

- return_to: str, the malformed return_to URL

976

"""

977

978

class MalformedTrustRoot(ProtocolError):

979

"""Malformed trust root.

980

981

Raised when the trust root is not well-formed according to

982

the OpenID specification trust_root format.

983

Inherits from ProtocolError.

984

"""

985

```