"""Base data types which are extended with concrete OpenId data types in :mod:`simple_openid_connect.data`"""importabcimportloggingfromtypingimportList,Literal,Type,TypeVarfromcryptojwtimportJWK,JWS,JWT,KeyBundle,KeyJarfromfurlimportQuery,furlfrompydanticimportBaseModellogger=logging.getLogger(__name__)Self=TypeVar("Self",bound="OpenidBaseModel")
[docs]classOpenidBaseModel(BaseModel,metaclass=abc.ABCMeta):""" Base model type upon which all openid data types are built. It implements encoding and decoding functionality that are commonly used in OpenID contexts. """
[docs]defencode_x_www_form_urlencoded(self)->str:""" Encode this message as a `x-www-form-urlencoded` formatted string. This is useful to send the message to an OP (if it is a request) either directly as GET url parameters or as an `x-www-form-urlencoded` request body """query=Query()query.set(self.model_dump(exclude_defaults=True))returnquery.encode()# type: ignore # because furl has no typedefs, but we know what this returns
[docs]defencode_url(self,url:str)->str:""" Encode this message as query string parameters into the existing url. This method explicitly only encodes the message into an urls query string because Openid specifies that only responses can be returned via a fragment and since this library is only intended for usage as a relying party, it should never need to generate responses. """url_parsed=furl(url)url_parsed.args.update(self.model_dump(exclude_defaults=True))returnurl_parsed.tostr()# type: ignore # because furl has no typedefs, but we know what this returns
[docs]@classmethoddefparse_x_www_form_urlencoded(cls:Type[Self],s:str)->Self:""" Parse a received message that is parsed from the given `x-www-form-urlencoded` formatted string. """query=Query(s)one_value_params={key:query.params[key]forkeyinquery.params.keys()}returncls.model_validate(one_value_params)
[docs]@classmethoddefparse_url(cls:Type[Self],url:str,location:Literal["query","fragment","auto"]="auto",)->Self:""" Parse a received message that is encoded as part of the URL as query parameters. :param url: The url which contains a message either in its query string or fragment :param location: Where the message data is located in the url. If set to 'auto', fragment will be tried first with query being used as a fallback. """iflocation=="query":returncls.parse_x_www_form_urlencoded(str(furl(url).query))eliflocation=="fragment":fragment=furl(url).fragment.queryreturncls.parse_x_www_form_urlencoded(str(fragment))eliflocation=="auto":try:returncls.parse_url(url,location="fragment")exceptExceptionase:logger.debug("Could not parse %s from fragment, trying query string: %s",cls.__name__,e,)returncls.parse_url(url,location="query")else:raiseValueError(f"invalid location value {location}")
[docs]@classmethoddefparse_jwt(cls:Type[Self],value:str,signing_keys:List[JWK])->Self:""" Parse received data that is encoded as a signed Json-Web-Signature (JWS). :param value: The encoded JWT :param signing_keys: List of keys one of which has been used to sign the JWT """verifier=JWS()msg=verifier.verify_compact(value,signing_keys)returncls.model_validate(msg)