OpenID support for modern servers and consumers with comprehensive authentication functionality.
—
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.
Core message representation with namespace-aware argument handling and format conversion.
class Message:
"""OpenID protocol message with namespace support."""
def __init__(self, openid_namespace=None):
"""
Initialize message with optional OpenID namespace.
Parameters:
- openid_namespace: str, OpenID namespace URI (auto-detected if None)
"""
@classmethod
def fromPostArgs(cls, args):
"""
Create message from POST form arguments.
Parameters:
- args: dict, form arguments (typically request.POST)
Returns:
Message object
"""
@classmethod
def fromOpenIDArgs(cls, openid_args):
"""
Create message from OpenID-specific arguments.
Parameters:
- openid_args: dict, arguments with 'openid.' prefix stripped
Returns:
Message object
"""
@classmethod
def fromKVForm(cls, kvform_string):
"""
Create message from key-value form string.
Parameters:
- kvform_string: str, key-value form data
Returns:
Message object
"""
def copy(self):
"""
Create deep copy of this message.
Returns:
Message, copy of this message
"""
def toPostArgs(self):
"""
Convert message to POST form arguments.
Returns:
dict, form arguments with 'openid.' prefixes
"""
def toArgs(self):
"""
Convert message to basic argument dictionary.
Returns:
dict, message arguments
"""
def toFormMarkup(self, action_url, form_tag_attrs=None, submit_text="Continue"):
"""
Generate HTML form markup for message.
Parameters:
- action_url: str, form action URL
- form_tag_attrs: dict, additional form tag attributes
- submit_text: str, submit button text
Returns:
str, HTML form markup
"""
def toURL(self, base_url):
"""
Convert message to URL with query parameters.
Parameters:
- base_url: str, base URL
Returns:
str, URL with message encoded as query parameters
"""
def toKVForm(self):
"""
Convert message to key-value form string.
Returns:
str, key-value form representation
"""
def toURLEncoded(self):
"""
Convert message to URL-encoded form data.
Returns:
str, URL-encoded form data
"""Access and modify message arguments with namespace support.
def hasKey(self, namespace, ns_key):
"""
Check if message has argument in namespace.
Parameters:
- namespace: str, namespace URI or symbol
- ns_key: str, argument key within namespace
Returns:
bool, True if argument exists
"""
def getKey(self, namespace, ns_key):
"""
Get full argument key for namespace and key.
Parameters:
- namespace: str, namespace URI or symbol
- ns_key: str, argument key within namespace
Returns:
str, full argument key with namespace alias
"""
def getArg(self, namespace, key, default=None):
"""
Get argument value from namespace.
Parameters:
- namespace: str, namespace URI or symbol
- key: str, argument key
- default: default value if not found
Returns:
str or default, argument value
"""
def getArgs(self, namespace):
"""
Get all arguments in namespace.
Parameters:
- namespace: str, namespace URI or symbol
Returns:
dict, {key: value} arguments in namespace
"""
def setArg(self, namespace, key, value):
"""
Set argument value in namespace.
Parameters:
- namespace: str, namespace URI or symbol
- key: str, argument key
- value: str, argument value
"""
def updateArgs(self, namespace, updates):
"""
Update multiple arguments in namespace.
Parameters:
- namespace: str, namespace URI or symbol
- updates: dict, {key: value} updates
"""
def delArg(self, namespace, key):
"""
Delete argument from namespace.
Parameters:
- namespace: str, namespace URI or symbol
- key: str, argument key to delete
"""
def getAliasedArg(self, aliased_key, default=None):
"""
Get argument by aliased key (e.g., 'openid.mode').
Parameters:
- aliased_key: str, full aliased key
- default: default value if not found
Returns:
str or default, argument value
"""Handle OpenID namespace detection and version compatibility.
def setOpenIDNamespace(self, openid_ns_uri, implicit):
"""
Set OpenID namespace for this message.
Parameters:
- openid_ns_uri: str, OpenID namespace URI
- implicit: bool, whether namespace is implicit (OpenID 1.x)
"""
def getOpenIDNamespace(self):
"""
Get OpenID namespace URI for this message.
Returns:
str, OpenID namespace URI
"""
def isOpenID1(self):
"""
Check if message uses OpenID 1.x protocol.
Returns:
bool, True if OpenID 1.x
"""
def isOpenID2(self):
"""
Check if message uses OpenID 2.0 protocol.
Returns:
bool, True if OpenID 2.0
"""Manage namespace URI to alias mappings for message encoding.
class NamespaceMap:
"""Maps namespace URIs to short aliases for message encoding."""
def __init__(self):
"""Initialize empty namespace map."""
def getAlias(self, namespace_uri):
"""
Get alias for namespace URI.
Parameters:
- namespace_uri: str, namespace URI
Returns:
str, namespace alias or None if not defined
"""
def getNamespaceURI(self, alias):
"""
Get namespace URI for alias.
Parameters:
- alias: str, namespace alias
Returns:
str, namespace URI or None if not defined
"""
def addAlias(self, namespace_uri, desired_alias, implicit=False):
"""
Add namespace URI with desired alias.
Parameters:
- namespace_uri: str, namespace URI
- desired_alias: str, desired alias (may be modified if conflicts)
- implicit: bool, whether namespace is implicit
Returns:
str, actual alias assigned
"""
def add(self, namespace_uri):
"""
Add namespace URI with auto-generated alias.
Parameters:
- namespace_uri: str, namespace URI
Returns:
str, assigned alias
"""
def isDefined(self, namespace_uri):
"""
Check if namespace URI is defined.
Parameters:
- namespace_uri: str, namespace URI
Returns:
bool, True if defined
"""
def isImplicit(self, namespace_uri):
"""
Check if namespace is implicit (no explicit alias).
Parameters:
- namespace_uri: str, namespace URI
Returns:
bool, True if implicit
"""
def items(self):
"""
Get all namespace URI to alias mappings.
Returns:
list, [(namespace_uri, alias), ...] tuples
"""Utilities for managing global namespace alias registrations.
def registerNamespaceAlias(namespace_uri, alias):
"""
Register global namespace alias for automatic assignment.
Parameters:
- namespace_uri: str, namespace URI
- alias: str, preferred alias
Raises:
NamespaceAliasRegistrationError: if alias conflicts
"""from openid.message import Message, OPENID2_NS
# Create new message
message = Message(OPENID2_NS)
# Set basic OpenID arguments
message.setArg('openid.ns', OPENID2_NS)
message.setArg('openid.mode', 'checkid_setup')
message.setArg('openid.identity', 'https://user.example.com')
message.setArg('openid.return_to', 'https://consumer.example.com/return')
# Access arguments
mode = message.getArg('openid.ns', 'mode')
identity = message.getArg('openid.ns', 'identity')
print(f"Mode: {mode}")
print(f"Identity: {identity}")
# Check OpenID version
if message.isOpenID2():
print("Using OpenID 2.0")
elif message.isOpenID1():
print("Using OpenID 1.x")# Convert to different formats
post_args = message.toPostArgs()
print("POST arguments:", post_args)
url = message.toURL('https://op.example.com/openid')
print("URL:", url)
kvform = message.toKVForm()
print("Key-value form:")
print(kvform)
# Generate HTML form
form_html = message.toFormMarkup(
action_url='https://op.example.com/openid',
submit_text='Continue to Identity Provider'
)
print("HTML form:")
print(form_html)# From POST data (typical web framework usage)
post_data = {
'openid.ns': 'http://specs.openid.net/auth/2.0',
'openid.mode': 'id_res',
'openid.identity': 'https://user.example.com',
'openid.sig': 'signature_value'
}
message_from_post = Message.fromPostArgs(post_data)
# From key-value form (for check_authentication)
kvform_data = """ns:http://specs.openid.net/auth/2.0
mode:id_res
identity:https://user.example.com
sig:signature_value"""
message_from_kv = Message.fromKVForm(kvform_data)
# From OpenID args (without 'openid.' prefix)
openid_args = {
'ns': 'http://specs.openid.net/auth/2.0',
'mode': 'id_res',
'identity': 'https://user.example.com'
}
message_from_args = Message.fromOpenIDArgs(openid_args)from openid.extensions import sreg
# Create message with extension
message = Message()
message.setArg('openid.ns', 'http://specs.openid.net/auth/2.0')
message.setArg('openid.mode', 'checkid_setup')
# Add SREG extension namespace
sreg_ns = 'http://openid.net/extensions/sreg/1.1'
message.setArg('openid.ns.sreg', sreg_ns)
message.setArg(sreg_ns, 'required', 'nickname,email')
message.setArg(sreg_ns, 'optional', 'fullname')
# Access extension arguments
sreg_args = message.getArgs(sreg_ns)
print("SREG arguments:", sreg_args)
# Check if extension is present
if message.hasKey(sreg_ns, 'required'):
required_fields = message.getArg(sreg_ns, 'required')
print(f"Required SREG fields: {required_fields}")from openid.message import NamespaceMap
# Create namespace map
ns_map = NamespaceMap()
# Add namespaces
openid_alias = ns_map.addAlias('http://specs.openid.net/auth/2.0', 'openid')
sreg_alias = ns_map.addAlias('http://openid.net/extensions/sreg/1.1', 'sreg')
ax_alias = ns_map.add('http://openid.net/srv/ax/1.0') # Auto-generated alias
print(f"OpenID alias: {openid_alias}")
print(f"SREG alias: {sreg_alias}")
print(f"AX alias: {ax_alias}")
# Check namespace definitions
if ns_map.isDefined('http://openid.net/extensions/sreg/1.1'):
alias = ns_map.getAlias('http://openid.net/extensions/sreg/1.1')
print(f"SREG namespace has alias: {alias}")
# Get all mappings
for namespace_uri, alias in ns_map.items():
print(f"{namespace_uri} -> {alias}")# Create original message
original = Message()
original.setArg('openid.ns', 'http://specs.openid.net/auth/2.0')
original.setArg('openid.ns', 'mode', 'checkid_setup')
original.setArg('openid.ns', 'identity', 'https://user.example.com')
# Create copy
copy = original.copy()
# Modify copy without affecting original
copy.setArg('openid.ns', 'mode', 'checkid_immediate')
copy.setArg('openid.ns', 'return_to', 'https://consumer.example.com/return')
# Original is unchanged
print("Original mode:", original.getArg('openid.ns', 'mode'))
print("Copy mode:", copy.getArg('openid.ns', 'mode'))from openid.message import registerNamespaceAlias
# Register commonly used namespace aliases
try:
registerNamespaceAlias('http://openid.net/extensions/sreg/1.1', 'sreg')
registerNamespaceAlias('http://openid.net/srv/ax/1.0', 'ax')
registerNamespaceAlias('http://schemas.openid.net/pape/policies/2007/06/phishing-resistant', 'pape')
print("Namespace aliases registered successfully")
except Exception as e:
print(f"Failed to register alias: {e}")
# Now these aliases will be used automatically when creating messages
message = Message()
# When adding SREG extension, 'sreg' alias will be preferred# OpenID namespace URIs
OPENID1_NS = 'http://openid.net/signon/1.0'
OPENID2_NS = 'http://specs.openid.net/auth/2.0'
# Special namespace symbols
NULL_NAMESPACE = object() # For arguments without namespace
OPENID_NS = object() # For current OpenID namespace
BARE_NS = object() # For bare arguments (no prefix)
# Standard OpenID arguments
IDENTIFIER_SELECT = 'http://specs.openid.net/auth/2.0/identifier_select'
SREG_URI = 'http://openid.net/sreg/1.0'
# URL length limits
OPENID1_URL_LIMIT = 2047 # Maximum URL length for OpenID 1.x
# Special sentinel for required parameters
no_default = object()
# Key-value form constants
KV_FORM_SEPARATOR = '\n'
KV_PAIR_SEPARATOR = ':'
# Exception types
class UndefinedOpenIDNamespace(Exception):
"""OpenID namespace not defined."""
class InvalidOpenIDNamespace(Exception):
"""Invalid OpenID namespace URI."""
class NamespaceAliasRegistrationError(Exception):
"""Namespace alias registration failed."""Install with Tessl CLI
npx tessl i tessl/pypi-python3-openid