oidc.py
Overview
The oidc.py file provides an implementation of an OpenID Connect (OIDC) client by extending a base OAuth client (OAuthClient). It is designed to interact with OIDC providers, handle OIDC-specific metadata discovery, validate ID tokens (JWTs), and fetch user information securely.
The core functionality centers around:
Discovering OIDC provider metadata dynamically using the issuer URL.
Parsing and validating OIDC ID Tokens with signature verification using the provider's JWKS (JSON Web Key Set).
Fetching and normalizing user profile information according to OIDC standards.
This file is a critical component for applications that require secure authentication via OIDC providers, enabling seamless integration with identity services like Google, Microsoft, or any OIDC-compliant system.
Classes and Methods
class OIDCClient(OAuthClient)
Extends the OAuthClient class to provide OpenID Connect specific features.
Constructor: __init__(self, config)
Initializes the OIDC client by loading provider metadata and setting up necessary endpoints and keys.
Parameters:
config(dict): Configuration dictionary that must include at least the"issuer"key, representing the OIDC provider's issuer URL.
Raises:
ValueError: If the issuer URL is missing or metadata retrieval fails.
Behavior:
Uses the issuer URL to discover OIDC metadata from the standard
/.well-known/openid-configurationendpoint.Updates the config with discovered endpoints:
issuerjwks_uriauthorization_urltoken_urluserinfo_url
Calls the parent
OAuthClientconstructor with the enriched config.Stores
issuerandjwks_urias instance variables for later use.
Usage Example:
config = {"issuer": "https://accounts.example.com"} client = OIDCClient(config)
_load_oidc_metadata(self, issuer)
Fetches OIDC provider metadata from the issuer's discovery endpoint.
Parameters:
issuer(str): The base URL for the OIDC provider.
Returns:
dict: Parsed JSON metadata containing endpoints and configuration.
Raises:
ValueError: If the metadata cannot be fetched or parsed.
Implementation details:
Constructs the metadata URL by appending
/.well-known/openid-configurationto the issuer.Uses
requests.getwith a timeout of 7 seconds to retrieve metadata.Raises an error if the HTTP request fails.
Usage Example:
metadata = client._load_oidc_metadata("https://accounts.example.com") print(metadata['authorization_endpoint'])
parse_id_token(self, id_token)
Validates and decodes an OIDC ID Token (JWT) with signature verification.
Parameters:
id_token(str): The JWT string received from the OIDC provider.
Returns:
dict: The decoded token payload if validation succeeds.
Raises:
ValueError: If token parsing, verification, or decoding fails.
Implementation details:
Extracts the JWT header without validation to determine the signing algorithm (defaulting to
RS256).Uses
jwt.PyJWKClientwith thejwks_urito fetch and cache the signing keys.Verifies the JWT signature and validates claims including:
Audience (
aud) matching the client ID.Issuer (
iss) matching the configured issuer.
Returns the validated token payload as a Python dictionary.
Usage Example:
decoded = client.parse_id_token(id_token) print(decoded['sub']) # Subject identifier for the user
fetch_user_info(self, access_token, id_token=None, **kwargs)
Fetches user profile information from the OIDC provider.
Parameters:
access_token(str): OAuth2 access token to authorize the userinfo request.id_token(str, optional): ID Token to parse and extract user info claims.**kwargs: Additional parameters (currently unused).
Returns:
dict: Normalized user information dictionary.
Behavior:
If an
id_tokenis provided, parses it to get initial user info claims.Calls the parent class's
fetch_user_info()method using theaccess_tokento retrieve user info from the provider's userinfo endpoint.Merges and normalizes the combined user info.
Usage Example:
user_info = client.fetch_user_info(access_token="abc123", id_token=id_token) print(user_info['email'])
normalize_user_info(self, user_info)
Normalizes user info dictionary to a standard format.
Parameters:
user_info(dict): Raw user information.
Returns:
dict: Normalized user information.
Behavior:
Delegates normalization to the parent class
OAuthClient.
Usage Example:
normalized = client.normalize_user_info(raw_user_info)
Important Implementation Details
Dynamic Metadata Discovery: Instead of hardcoding URLs, the client dynamically fetches OIDC provider metadata from the
/.well-known/openid-configurationendpoint, ensuring compatibility with any OIDC-compliant provider.JWKS Usage for ID Token Verification: The client fetches the JSON Web Key Set (JWKS) from the provider to verify the signature of ID tokens securely and reliably, preventing token forgery.
Inheritance: The
OIDCClientleverages and extends the baseOAuthClientclass, reusing its OAuth2 flows and user info normalization, enhancing code reuse and maintainability.Error Handling: Robust error handling ensures that failures in metadata retrieval or token verification raise clear exceptions, facilitating debugging and secure failure modes.
Interaction with Other Parts of the System
Depends on:
OAuthClient(from.oauth): Provides base OAuth2 client functionality such as token requests and user info retrieval.External libraries:
jwt(PyJWT): For JWT decoding and verification.requests: For HTTP requests to metadata and userinfo endpoints.
Provides:
An OIDC-compliant client class that can be instantiated and used by authentication modules or middleware within the application.
Methods to verify and parse ID tokens and fetch user profile data securely.
Typical Usage Context:
Authentication flows in web applications or APIs requiring OIDC login.
Middleware or service layers validating tokens and enriching user sessions with profile data.
Visual Diagram
classDiagram
class OIDCClient {
-issuer: str
-jwks_uri: str
+__init__(config: dict)
-_load_oidc_metadata(issuer: str) dict
+parse_id_token(id_token: str) dict
+fetch_user_info(access_token: str, id_token: str = None, **kwargs) dict
+normalize_user_info(user_info: dict) dict
}
class OAuthClient {
+__init__(config: dict)
+fetch_user_info(access_token: str) dict
+normalize_user_info(user_info: dict) dict
}
OIDCClient --|> OAuthClient
Summary
oidc.py defines the OIDCClient class, a specialized OAuth client tailored for OpenID Connect. It simplifies OIDC integration by automating metadata discovery, securely validating ID tokens, and fetching user info. By extending OAuthClient, it inherits core OAuth2 features while adding OIDC-specific logic, making it a key component for authentication in systems supporting OIDC providers.