The Unifize APIs are crafted following RESTful standards, ensuring that interaction with resources is consistent and intuitive. The APIs are endpoint-driven and typically represent distinct resources, allowing for clear, organized, and efficient communication between systems.
All interactions are managed through the JSON data format, which necessitates setting the content type to application/json. This requirement is enforced for both request and response bodies unless otherwise specified, facilitating seamless data exchange and integration with various applications. Such consistent data handling simplifies interaction and enhances compatibility with different platforms and programming environments.
Authentication and Authorization
Authentication is a necessary aspect of utilizing the Unifize APIs and is implemented via app tokens. These tokens are generated according to the guidelines detailed in the App Tokens page.
Additionally, authorization is handled via a permissions framework, which grants granular control over access levels and operations. Permissions can be set initially during app creation and remain flexible, allowing for modifications as the needs of the application evolve. This dual-layered security model ensures that the Unifize APIs provide robust protection for sensitive operations and data.
Organizations
Central to Unifize's architecture are logical entities known as "Organizations," which help partition and manage resources within the system. The Org Id of these organizations must be specified during the initial token request process. This token, once embedded within a JWT, eliminates the need for repeatedly specifying the Org Id in further API calls, streamlining authentication processes and reducing complexity.
You can read more about Organizations in the Concepts and Terminologies page.
Test it out
To do this using your preferred programming language, the only requirements are:
A library to mint JWTs
A library to read/parse PEM files
A library to make network requests
Below are some examples in commonly used programming languages.
const fs = require('fs');
const jwt = require('jsonwebtoken');
const axios = require('axios');
/**
* Loads a private key from a PEM file
* @param {string} filePath - Path to the PEM file
* @returns {string} Private key as a string
*/
function loadPrivateKeyFromPem(filePath) {
return fs.readFileSync(filePath, 'utf8');
}
/**
* Generates a JWT token with the given app ID and signed using the private key
* @param {number} appId - The application ID to use as issuer
* @param {string} privateKeyPath - Path to the private key PEM file
* @returns {string} Signed JWT token
*/
function generateJwt(appId, privateKeyPath) {
// Load the private key
const privateKey = loadPrivateKeyFromPem(privateKeyPath);
// Get current time in seconds
const nowSeconds = Math.floor(Date.now() / 1000);
// Create the payload
const payload = {
iss: appId.toString(),
iat: nowSeconds,
exp: nowSeconds + 600
};
// Sign the JWT
return jwt.sign(payload, privateKey, { algorithm: 'RS256' });
}
/**
* Exchanges the JWT for an access token
* @param {string} jwtToken - The JWT token to exchange
* @param {number} orgId - The organization ID
* @returns {Promise} Promise resolving to the access token response
*/
async function exchangeJwtForAccessToken(jwtToken, orgId) {
try {
const response = await axios.post(
`https://api.example.com/application/installation/${orgId}/token`,
{ token: jwtToken },
{
headers: {
'Authorization': `Bearer ${jwtToken}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
} catch (error) {
throw error;
}
}
/**
* Makes an API call with the access token in the Authorization header
* @param {string} accessToken - The access token to use for authorization
* @returns {Promise} Promise resolving to the API response
*/
async function makeApiCall(accessToken) {
try {
const response = await axios.get('https://api.example.com/', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
return response;
} catch (error) {
throw error;
}
}
/**
* Main function to demonstrate the JWT generation, token exchange, and API call
*/
async function main() {
// Example usage
const appId = 12345;
const orgId = 67890;
const privateKeyPath = 'path/to/private_key.pem';
try {
// Generate JWT
const jwt = generateJwt(appId, privateKeyPath);
console.log('JWT generated successfully');
// Exchange JWT for access token
const tokenResponse = await exchangeJwtForAccessToken(jwt, orgId);
const accessToken = tokenResponse.access_token;
const expiresAt = tokenResponse.expires_at;
console.log(`Access token received, expires at: ${expiresAt}`);
// Make API call with access token
const response = await makeApiCall(accessToken);
// Print response
console.log(`Status code: ${response.status}`);
console.log(`Response body:`, response.data);
} catch (error) {
console.error('Error:', error.message);
if (error.response) {
console.error('Response status:', error.response.status);
console.error('Response data:', error.response.data);
}
}
}
// Execute the main function
main();
Prerequisites:
pip install pyjwt requests cryptography
import jwt
import time
import requests
from pathlib import Path
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.backends import default_backend
def load_private_key_from_pem(file_path):
"""
Loads a private key from a PEM file
Args:
file_path (str): Path to the PEM file
Returns:
cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey: The private key object
"""
pem_data = Path(file_path).read_bytes()
# Load the private key - passing None for password if the key is not encrypted
private_key = load_pem_private_key(pem_data, password=None, backend=default_backend())
return private_key
def generate_jwt(app_id, private_key_path):
"""
Generates a JWT token with the given app ID and signed using the private key
Args:
app_id (int): The application ID to use as issuer
private_key_path (str): Path to the private key PEM file
Returns:
str: Signed JWT token
"""
# Load the private key
private_key = load_private_key_from_pem(private_key_path)
# Get current time in seconds
now_seconds = int(time.time())
# Create the payload
payload = {
'iss': str(app_id),
'iat': now_seconds,
'exp': now_seconds + 600
}
# Sign the JWT
return jwt.encode(payload, private_key, algorithm='RS256')
def exchange_jwt_for_access_token(jwt_token, org_id):
"""
Exchanges the JWT for an access token
Args:
jwt_token (str): The JWT token to exchange
org_id (int): The organization ID
Returns:
dict: The access token response containing access_token and expires_at
"""
headers = {
'Authorization': f'Bearer {jwt_token}',
'Content-Type': 'application/json'
}
payload = {
'token': jwt_token
}
response = requests.post(
f'https://api.example.com/application/installation/{org_id}/token',
headers=headers,
json=payload
)
response.raise_for_status() # Raise an exception for HTTP errors
return response.json()
def make_api_call(access_token):
"""
Makes an API call with the access token in the Authorization header
Args:
access_token (str): The access token to use for authorization
Returns:
requests.Response: The API response
"""
headers = {
'Authorization': f'Bearer {access_token}'
}
return requests.get('https://api.example.com/', headers=headers)
def main():
"""
Main function to demonstrate the JWT generation, token exchange, and API call
"""
# Example usage
app_id = 12345
org_id = 67890
private_key_path = 'path/to/private_key.pem'
try:
# Generate JWT
jwt_token = generate_jwt(app_id, private_key_path)
print("JWT generated successfully")
# Exchange JWT for access token
token_response = exchange_jwt_for_access_token(jwt_token, org_id)
access_token = token_response['access_token']
expires_at = token_response['expires_at']
print(f"Access token received, expires at: {expires_at}")
# Make API call with access token
response = make_api_call(access_token)
# Print response
print(f"Status code: {response.status_code}")
print(f"Response body: {response.text}")
except Exception as e:
print(f"Error: {str(e)}")
if __name__ == "__main__":
main()
Add the following dependencies to your project:
io.jsonwebtoken:jjwt-api
io.jsonwebtoken:jjwt-impl
io.jsonwebtoken:jjwt-jackson (or another JSON processor implementation)
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.jackson.io.JacksonDeserializer;
import io.jsonwebtoken.jackson.io.JacksonSerializer;
import io.jsonwebtoken.io.Deserializer;
import io.jsonwebtoken.io.Serializer;
public class JwtApiClient {
/**
* Generates a JWT token with the given app ID and signed using the private key
*/
public static String generateJwt(int appId, String privateKeyPath) throws Exception {
// Load the private key from the PEM file
PrivateKey privateKey = loadPrivateKeyFromPem(privateKeyPath);
// Get current time
long nowSeconds = Instant.now().getEpochSecond();
// Build the JWT
return Jwts.builder()
.setIssuer(String.valueOf(appId))
.setIssuedAt(new Date(nowSeconds * 1000))
.setExpiration(new Date((nowSeconds + 600) * 1000))
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
/**
* Exchanges the JWT for an access token
*/
public static Map<String, Object> exchangeJwtForAccessToken(String jwt, int orgId) throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
// Create the JSON payload
String jsonPayload = String.format("{\"token\":\"%s\"}", jwt);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/application/installation/" + orgId + "/token"))
.header("Authorization", "Bearer " + jwt)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new IOException("Failed to exchange token: " + response.statusCode() + ", " + response.body());
}
// Parse the JSON response using the JJWT Jackson deserializer
Deserializer<Map<String, ?>> deserializer = new JacksonDeserializer<>();
return (Map<String, Object>) deserializer.deserialize(response.body().getBytes());
}
/**
* Makes an API call to the root endpoint with the access token in the Authorization header
*/
public static HttpResponse<String> makeApiCall(String accessToken) throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/"))
.header("Authorization", "Bearer " + accessToken)
.GET()
.build();
return client.send(request, HttpResponse.BodyHandlers.ofString());
}
/**
* Loads an RSA private key from a PEM file
*/
private static PrivateKey loadPrivateKeyFromPem(String filePath) throws Exception {
String key = new String(Files.readAllBytes(Paths.get(filePath)));
// Remove PEM format headers and footers and decode
String privateKeyPEM = key
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s", "");
byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);
// Create the private key
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
return keyFactory.generatePrivate(keySpec);
}
public static void main(String[] args) {
// Example usage
int appId = 12345;
int orgId = 67890;
String privateKeyPath = "path/to/private_key.pem";
try {
// 1. Generate JWT
String jwt = generateJwt(appId, privateKeyPath);
System.out.println("JWT generated successfully");
// 2. Exchange JWT for access token
Map<String, Object> tokenResponse = exchangeJwtForAccessToken(jwt, orgId);
String accessToken = (String) tokenResponse.get("access_token");
String expiresAt = (String) tokenResponse.get("expires_at");
System.out.println("Access token received, expires at: " + expiresAt);
// 3. Make API call with access token
HttpResponse<String> response = makeApiCall(accessToken);
// 4. Print response
System.out.println("Status code: " + response.statusCode());
System.out.println("Response body: " + response.body());
} catch (Exception e) {
e.printStackTrace();
}
}
}