Skip to main content

Manage Service Principals

Using the FAPI Credential Validation

The set of FAPI credentials have been created and now are to be used. You have the following items:

  • Passphrase to decrypt .key file
  • .KEY file
  • .CRT file
  • Client ID—can be obtained from PortX Integration—Manager Secure Module—Manage Service Principals Page which has a column labeled “Client ID” (shown in Figure 83)
  • Tenant Name



Figure 83. The Secure Module—Manage Service Principals Page Showing the FAPI Credential Including Client ID

Figure 83. The Secure Module—Manage Service Principals Page Showing the FAPI Credential Including Client ID




The “Example_FAPI-dev” was created from the information in Figure 79.


Validate FAPI Credentials

Validation is optional.

The goal is to successfully request a new access token.

Figure 84 shows an example of a private key when opened in a text editor.




Figure 84. FAPI Credential—Text Editor View of the Private Key

Figure 84. FAPI Credential—Text Editor View of the Private Key




Figure 85 shows an example of a certificate when opened in a text editor.




Figure 85. FAPI Credential—Text Editor View of Certificate

Figure 85. FAPI Credential—Text Editor View of Certificate




Optional—Manually Verifying and Decrypting .key file using command rsa through command line

This step validates that the passphrase is correct and the key can be decrypted. However, the next step, the next python script decrypts it before requesting the token.

First decrypt the key file using the passphrase.

Command line command to test passphrase to decrypt the key.

openssl rsa -in cert.key -out decrypted_client.key




Figure 86. Example of using the Open SSL command to decrypt .key

Figure 86. Example of using the Open SSL command to decrypt .key




Credentials Usage

Generating the one-time JWT to interact with the API.


Credentials Validation—Issue test token using automated script (Python)

Python example:

pip install --upgrade PyJWT 
pip install --upgrade requests
pip install --upgrade urllib3
pip install --upgrade cryptography


NOTE: Please note from Line 13 (after "Configuration Begin") to Line 18 (before "Configuration End") the values are specific to the client, and your own information needs to be entered in Line 13 through Line 18.

import copy
import json
import jwt
import time
import uuid
import requests
import urllib
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend



## Configuration - BEGIN ##
realm_name = 'demobank'
client_name = 'Example_FAPI_dev'
client_id = '32ec103f-6217-42d8-9c4c-c5e71547ad39'
certificate_file_path = 'Example_FAPI_dev.crt'
encrypted_key_file_path = 'Example_FAPI_dev.key'
pass_phrase='test1234'
## Configuration - END ##



##################################################################################
current_path = f"."
current_time = int(time.time())
realm_url = f"https://auth.piam.tenants.portx.io/realms/{realm_name}"
decrypted_key_file_path = 'decrypted_client.key'
####################################################################################
# openssl rsa -in ./client.key -out decrypted_client.key
# Read the encrypted private key
with open(encrypted_key_file_path, 'r') as key_file:
encrypted_key = key_file.read()
# Decrypt the private key using the password
private_key = load_pem_private_key(
encrypted_key.encode(),
password=pass_phrase.encode('utf-8'),
backend=default_backend()
)
with open(decrypted_key_file_path, 'wb') as key_file:
key_file.write(
private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
)
with open(decrypted_key_file_path, 'r') as key_file:
decrypted_key = key_file.read()
# Define JWT payload
headers = {
'alg': 'PS256',
'typ': 'JWT'
}
payload = {
'iss': client_id, # Issuer - this should be your client ID
'sub': client_id,
'aud': f'https://auth.piam.tenants.portx.io/realms/{realm_name}', # Audience - your Keycloak server URL
'jti': str(uuid.uuid4()),
'exp': current_time + 60, # Expiration time (60 seconds from now)c
'iat': current_time, # Issued At time (current time)
}
try:
jwt_token = jwt.encode(payload, decrypted_key, algorithm='PS256', headers=headers)
print("Generated JWT:", jwt_token)
except Exception as e:
print("Error generating JWT:", str(e))
raise
# Keycloak token endpoint
token_url = f'https://auth.piam.tenants.portx.io/realms/{realm_name}/protocol/openid-connect/token'
# Prepare the request data
data = {
'grant_type': 'client_credentials',
'client_assertion': jwt_token,
'client_assertion_type': 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
'client_id': client_id, # Ensure this is the correct client ID
}
client_cert_data = open(certificate_file_path, "r").read()
client_cert_data_enc = urllib.parse.quote(client_cert_data)
print(client_cert_data_enc)
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
}
headers_auth = copy.deepcopy(headers)
headers_auth.update({"X-Client-Cert": client_cert_data_enc})
# Make the request
response = requests.post(token_url, headers=headers_auth, data=data, cert=(certificate_file_path, decrypted_key_file_path), verify=True)
# Check the response
if response.status_code == 200:
print("::::::::::::::::")
print("Access Token:", response.json().get('access_token'))
decoded = jwt.decode(response.json().get('access_token'), options={"verify_signature": False})
print("\nDecoded JWT Payload (using PyJWT):")
print(json.dumps(decoded, indent=2))
else:
print("Failed to get access token:", response.status_code, response.text)



Expected Result

JWT


Figure 87. JWT Access Token—Text Editor View

Figure 87. JWT Access Token—Text Editor View




Optional Validation in jwt.io


Figure 88. Optional Validation in JWT.io
Figure 88. Optional Validation in JWT.io