Comprehensive SAML 2.0 toolkit for Python applications enabling SSO and SLO functionality with Service Provider support
—
Specialized classes for handling SAML protocol messages including authentication requests, logout requests/responses, and SAML response validation. These classes provide comprehensive message generation, validation, and data extraction capabilities for complete SSO and SLO workflows.
Handles SAML Response messages from Identity Providers, including parsing, validation, and user data extraction with comprehensive security checks.
class OneLogin_Saml2_Response:
def __init__(self, settings: OneLogin_Saml2_Settings, response: str):
"""
Initialize SAML Response processor.
Parameters:
- settings: SAML configuration settings
- response: Base64 encoded XML SAML Response
"""Comprehensive validation of SAML Response structure, signatures, and security conditions.
def is_valid(self, request_data: dict, request_id: str = None, raise_exceptions: bool = False) -> bool:
"""
Validate SAML Response with comprehensive security checks.
Parameters:
- request_data: HTTP request information
- request_id: ID of AuthNRequest sent by SP
- raise_exceptions: Whether to raise exceptions on validation failure
Returns:
True if response is valid, False otherwise
Validates:
- SAML version compliance
- Response status
- Single assertion requirement
- XML schema validation
- Digital signatures
- Timestamp validity
- Audience restrictions
- Issuer verification
"""
def check_status(self) -> None:
"""
Validate response status is SUCCESS.
Raises:
- OneLogin_Saml2_ValidationError: If status is not SUCCESS
"""
def validate_timestamps(self, raise_exceptions: bool = True) -> bool:
"""
Validate NotBefore/NotOnOrAfter conditions.
Parameters:
- raise_exceptions: Whether to raise exceptions on failure
Returns:
True if timestamps are valid
"""Extract user identification and attributes from validated SAML assertions.
def get_nameid(self) -> str:
"""
Extract NameID value from assertion.
Returns:
NameID string or None
"""
def get_nameid_data(self) -> dict:
"""
Get complete NameID data structure.
Returns:
Dictionary with Value, Format, NameQualifier, SPNameQualifier
"""
def get_nameid_format(self) -> str:
"""
Get NameID format.
Returns:
NameID format string or None
"""
def get_attributes(self) -> dict:
"""
Extract SAML attributes indexed by Name.
Returns:
Dictionary mapping attribute names to value lists
"""
def get_friendlyname_attributes(self) -> dict:
"""
Extract SAML attributes indexed by FriendlyName.
Returns:
Dictionary mapping FriendlyNames to value lists
"""Retrieve session data for logout and session management.
def get_session_index(self) -> str:
"""
Get SessionIndex for logout operations.
Returns:
SessionIndex string or None
"""
def get_session_not_on_or_after(self) -> int:
"""
Get session expiration time.
Returns:
Unix timestamp or None
"""
def get_assertion_not_on_or_after(self) -> int:
"""
Get assertion validity end time.
Returns:
Unix timestamp or None
"""Access response and assertion metadata for auditing and debugging.
def get_audiences(self) -> list:
"""
Get valid audiences from AudienceRestriction.
Returns:
List of audience entity IDs
"""
def get_authn_contexts(self) -> list:
"""
Get authentication context class references.
Returns:
List of authentication context strings
"""
def get_issuers(self) -> list:
"""
Get issuers from response and assertion.
Returns:
List of issuer entity IDs
"""
def get_in_response_to(self) -> str:
"""
Get InResponseTo attribute value.
Returns:
InResponseTo ID string or None
"""
def get_id(self) -> str:
"""
Get Response ID.
Returns:
Response ID string
"""
def get_assertion_id(self) -> str:
"""
Get Assertion ID.
Returns:
Assertion ID string
"""Build SAML AuthnRequest messages to initiate Single Sign-On flows with configurable authentication parameters.
class OneLogin_Saml2_Authn_Request:
def __init__(self, settings: OneLogin_Saml2_Settings, force_authn: bool = False, is_passive: bool = False, set_nameid_policy: bool = True, name_id_value_req: str = None):
"""
Initialize AuthnRequest builder.
Parameters:
- settings: SAML configuration settings
- force_authn: Forces re-authentication at IdP
- is_passive: Sets passive authentication mode
- set_nameid_policy: Whether to include NameIDPolicy
- name_id_value_req: Specific NameID value to request
"""Generate and encode SAML authentication requests for IdP redirection.
def get_request(self, deflate: bool = True) -> str:
"""
Get encoded AuthnRequest for HTTP binding.
Parameters:
- deflate: Whether to compress with deflate (required for HTTP-Redirect)
Returns:
Base64 encoded (and optionally deflated) AuthnRequest XML
"""
def get_id(self) -> str:
"""
Get unique AuthnRequest ID.
Returns:
Request ID string for tracking
"""
def get_xml(self) -> str:
"""
Get raw XML AuthnRequest.
Returns:
Complete AuthnRequest XML string
"""Usage Example:
# Create authentication request
authn_request = OneLogin_Saml2_Authn_Request(
settings,
force_authn=True, # Force re-authentication
is_passive=False # Interactive authentication
)
# Get encoded request for redirect
encoded_request = authn_request.get_request(deflate=True)
request_id = authn_request.get_id()
# Build SSO URL with request
sso_url = f"{idp_sso_url}?SAMLRequest={encoded_request}&RelayState={return_url}"Handle SAML Logout Request messages for Single Logout initiation and processing.
class OneLogin_Saml2_Logout_Request:
def __init__(self, settings: OneLogin_Saml2_Settings, request: str = None, name_id: str = None, session_index: str = None, nq: str = None, name_id_format: str = None, spnq: str = None):
"""
Initialize Logout Request handler.
Parameters:
- settings: SAML configuration settings
- request: Existing logout request to parse (optional)
- name_id: NameID for logout
- session_index: Session identifier
- nq: NameQualifier
- name_id_format: NameID format
- spnq: SPNameQualifier
"""Generate and validate logout requests for SLO workflows.
def get_request(self, deflate: bool = True) -> str:
"""
Get encoded logout request.
Parameters:
- deflate: Whether to compress with deflate
Returns:
Base64 encoded logout request XML
"""
def get_xml(self) -> str:
"""
Get raw XML logout request.
Returns:
Complete LogoutRequest XML string
"""
def is_valid(self, request_data: dict, raise_exceptions: bool = False) -> bool:
"""
Validate incoming logout request.
Parameters:
- request_data: HTTP request information
- raise_exceptions: Whether to raise validation exceptions
Returns:
True if request is valid
Validates:
- XML schema compliance
- Destination verification
- Issuer validation
- Timestamp checking
- Digital signatures
"""Extract data from incoming logout requests using static methods.
@staticmethod
def get_id(request: str) -> str:
"""
Extract request ID from logout request.
Parameters:
- request: Base64 encoded logout request
Returns:
Request ID string
"""
@staticmethod
def get_nameid_data(request: str, key: str = None) -> dict:
"""
Get complete NameID data, handling encryption.
Parameters:
- request: Base64 encoded logout request
- key: Private key for NameID decryption
Returns:
Dictionary with NameID data
"""
@staticmethod
def get_nameid(request: str, key: str = None) -> str:
"""
Get NameID value from logout request.
Parameters:
- request: Base64 encoded logout request
- key: Private key for decryption
Returns:
NameID string
"""
@staticmethod
def get_issuer(request: str) -> str:
"""
Get issuer from logout request.
Parameters:
- request: Base64 encoded logout request
Returns:
Issuer entity ID string
"""
@staticmethod
def get_session_indexes(request: str) -> list:
"""
Get all session indexes from request.
Parameters:
- request: Base64 encoded logout request
Returns:
List of session index strings
"""Handle SAML Logout Response messages for Single Logout completion.
class OneLogin_Saml2_Logout_Response:
def __init__(self, settings: OneLogin_Saml2_Settings, response: str = None):
"""
Initialize Logout Response handler.
Parameters:
- settings: SAML configuration settings
- response: Base64 encoded SAML logout response (optional)
"""Create logout response messages for IdP-initiated SLO.
def build(self, in_response_to: str, status: str = OneLogin_Saml2_Constants.STATUS_SUCCESS) -> None:
"""
Build a new logout response.
Parameters:
- in_response_to: ID of the logout request being responded to
- status: Response status code (default: SUCCESS)
"""
def get_response(self, deflate: bool = True) -> str:
"""
Get encoded logout response.
Parameters:
- deflate: Whether to compress with deflate
Returns:
Base64 encoded logout response XML
"""
def get_xml(self) -> str:
"""
Get raw XML logout response.
Returns:
Complete LogoutResponse XML string
"""Validate incoming logout responses from Identity Providers.
def is_valid(self, request_data: dict, request_id: str = None, raise_exceptions: bool = False) -> bool:
"""
Validate logout response.
Parameters:
- request_data: HTTP request information
- request_id: ID of logout request sent by SP
- raise_exceptions: Whether to raise validation exceptions
Returns:
True if response is valid
Validates:
- XML schema compliance
- InResponseTo matching
- Issuer verification
- Destination validation
- Digital signatures
"""
def get_status(self) -> str:
"""
Get response status code.
Returns:
Status code string (SUCCESS, REQUESTER, RESPONDER, etc.)
"""
def get_issuer(self) -> str:
"""
Get response issuer.
Returns:
Issuer entity ID string
"""
def get_in_response_to(self) -> str:
"""
Get InResponseTo value.
Returns:
InResponseTo ID string
"""All message processing classes provide comprehensive error handling capabilities.
def get_error(self) -> str:
"""
Get validation error message.
Returns:
Descriptive error message string or None
"""# Process SAML Response in ACS endpoint
response = OneLogin_Saml2_Response(settings, saml_response)
if response.is_valid(request_data, request_id):
# Extract user data
user_id = response.get_nameid()
attributes = response.get_attributes()
session_index = response.get_session_index()
# Store session data
session['user_id'] = user_id
session['saml_attributes'] = attributes
session['saml_session_index'] = session_index
else:
# Handle validation failure
error = response.get_error()
print(f"SAML validation failed: {error}")# Handle incoming logout request
if 'SAMLRequest' in request.form:
logout_req = OneLogin_Saml2_Logout_Request(settings, request.form['SAMLRequest'])
if logout_req.is_valid(request_data):
# Extract logout data
nameid = OneLogin_Saml2_Logout_Request.get_nameid(request.form['SAMLRequest'])
session_indexes = OneLogin_Saml2_Logout_Request.get_session_indexes(request.form['SAMLRequest'])
# Build response
logout_resp = OneLogin_Saml2_Logout_Response(settings)
logout_resp.build(logout_req.get_id())
# Clear local session and return response
session.clear()
return redirect(f"{idp_slo_url}?SAMLResponse={logout_resp.get_response()}")# Create AuthN request with specific parameters
authn_req = OneLogin_Saml2_Authn_Request(
settings,
force_authn=True,
set_nameid_policy=True
)
# Build SSO redirect URL
sso_url = (
f"{settings.get_idp_sso_url()}"
f"?SAMLRequest={authn_req.get_request()}"
f"&RelayState={urllib.parse.quote(return_url)}"
)
# Store request ID for validation
session['saml_request_id'] = authn_req.get_id()Install with Tessl CLI
npx tessl i tessl/pypi-python3-saml