def validate_jwt(request, client_id, pem_bytes, expect_x5c=False): """Validate the request meets AAD's expectations for a client credential grant using a certificate, as documented at https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials """ cert = x509.load_pem_x509_certificate(pem_bytes, default_backend()) # jwt is of the form 'header.payload.signature'; 'signature' is 'header.payload' signed with cert's private key jwt = six.ensure_str(request.body["client_assertion"]) header, payload, signature = (urlsafeb64_decode(s) for s in jwt.split(".")) signed_part = jwt[: jwt.rfind(".")] claims = json.loads(payload.decode("utf-8")) assert claims["aud"] == request.url assert claims["iss"] == claims["sub"] == client_id deserialized_header = json.loads(header.decode("utf-8")) assert deserialized_header["alg"] == "RS256" assert deserialized_header["typ"] == "JWT" if expect_x5c: # x5c should have all the certs in the PEM file, in order, minus headers and footers pem_lines = pem_bytes.decode("utf-8").splitlines() header = "-----BEGIN CERTIFICATE-----" assert len(deserialized_header["x5c"]) == pem_lines.count(header) # concatenate the PEM file's certs, removing headers and footers chain_start = pem_lines.index(header) pem_chain_content = "".join(line for line in pem_lines[chain_start:] if not line.startswith("-" * 5)) assert "".join(deserialized_header["x5c"]) == pem_chain_content, "JWT's x5c claim contains unexpected content" else: assert "x5c" not in deserialized_header assert urlsafeb64_decode(deserialized_header["x5t"]) == cert.fingerprint(hashes.SHA1()) # nosec cert.public_key().verify(signature, signed_part.encode("utf-8"), padding.PKCS1v15(), hashes.SHA256())
async def mock_send(request, **kwargs): jwt = request.body["client_assertion"] header, payload, signature = (urlsafeb64_decode(s) for s in jwt.split(".")) claims = json.loads(payload.decode("utf-8")) validate_url(claims["aud"]) return mock_response(json_payload={ "token_type": "Bearer", "expires_in": 42, "access_token": access_token })
def validate_jwt(request, client_id, pem_bytes): """Validate the request meets AAD's expectations for a client credential grant using a certificate, as documented at https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials """ cert = x509.load_pem_x509_certificate(pem_bytes, default_backend()) # jwt is of the form 'header.payload.signature'; 'signature' is 'header.payload' signed with cert's private key jwt = request.body["client_assertion"] header, payload, signature = (urlsafeb64_decode(s) for s in jwt.split(".")) signed_part = jwt[: jwt.rfind(".")] claims = json.loads(payload.decode("utf-8")) deserialized_header = json.loads(header.decode("utf-8")) assert deserialized_header["alg"] == "RS256" assert deserialized_header["typ"] == "JWT" assert urlsafeb64_decode(deserialized_header["x5t"]) == cert.fingerprint(hashes.SHA1()) # nosec assert claims["aud"] == request.url assert claims["iss"] == claims["sub"] == client_id cert.public_key().verify(signature, signed_part.encode("utf-8"), padding.PKCS1v15(), hashes.SHA256())