@@ -50,6 +50,22 @@ def decorate_scope(
5050 return list (decorated )
5151
5252
53+ def extract_certs (public_cert_content ):
54+ # Parses raw public certificate file contents and returns a list of strings
55+ # Usage: headers = {"x5c": extract_certs(open("my_cert.pem").read())}
56+ public_certificates = re .findall (
57+ r'-----BEGIN CERTIFICATE-----(?P<cert_value>[^-]+)-----END CERTIFICATE-----' ,
58+ public_cert_content , re .I )
59+ if public_certificates :
60+ return [cert .strip () for cert in public_certificates ]
61+ # The public cert tags are not found in the input,
62+ # let's make best effort to exclude a private key pem file.
63+ if "PRIVATE KEY" in public_cert_content :
64+ raise ValueError (
65+ "We expect your public key but detect a private key instead" )
66+ return [public_cert_content .strip ()]
67+
68+
5369class ClientApplication (object ):
5470
5571 def __init__ (
@@ -59,7 +75,7 @@ def __init__(
5975 verify = True , proxies = None , timeout = None ):
6076 """Create an instance of application.
6177
62- :param client_id: Your app has a clinet_id after you register it on AAD.
78+ :param client_id: Your app has a client_id after you register it on AAD.
6379 :param client_credential:
6480 For :class:`PublicClientApplication`, you simply use `None` here.
6581 For :class:`ConfidentialClientApplication`,
@@ -69,8 +85,12 @@ def __init__(
6985 {
7086 "private_key": "...-----BEGIN PRIVATE KEY-----...",
7187 "thumbprint": "A1B2C3D4E5F6...",
88+ "public_certificate": "...-----BEGIN CERTIFICATE-----..." (Optional. See below.)
7289 }
7390
91+ public_certificate (optional) is public key certificate which is
92+ sent through 'x5c' JWT header only for
93+ subject name and issuer authentication to support cert auto rolls
7494 :param str authority:
7595 A URL that identifies a token authority. It should be of the format
7696 https://login.microsoftonline.com/your_tenant
@@ -113,9 +133,12 @@ def _build_client(self, client_credential, authority):
113133 if isinstance (client_credential , dict ):
114134 assert ("private_key" in client_credential
115135 and "thumbprint" in client_credential )
136+ headers = {}
137+ if 'public_certificate' in client_credential :
138+ headers ["x5c" ] = extract_certs (client_credential ['public_certificate' ])
116139 signer = JwtSigner (
117140 client_credential ["private_key" ], algorithm = "RS256" ,
118- sha1_thumbprint = client_credential .get ("thumbprint" ))
141+ sha1_thumbprint = client_credential .get ("thumbprint" ), headers = headers )
119142 client_assertion = signer .sign_assertion (
120143 audience = authority .token_endpoint , issuer = self .client_id )
121144 client_assertion_type = Client .CLIENT_ASSERTION_TYPE_JWT
0 commit comments