Getting Started with Cedarling C Bindings#
C bindings for the Jans Cedarling authorization engine, providing policy-based access control through a native C API.
Installation#
Prerequisites#
- C compiler (GCC, Clang, or MSVC)
- Rust toolchain
Build from Source#
-
Clone the Janssen repository:
git clone --depth 1 https://github.com/JanssenProject/jans.git -
Navigate to the Cedarling C bindings directory:
cd jans/jans-cedarling/bindings/cedarling_c -
Build the Rust library (this also generates
target/include/cedarling_c.hunder that crate’starget/directory):cargo build --release -p cedarling_c -
Copy the built artifacts to your project:
- Linux:
cp target/release/libcedarling_c.so . - macOS:
cp target/release/libcedarling_c.dylib . - Windows:
cp target/release/cedarling_c.dll .andcp target/release/cedarling_c.dll.lib cedarling_c.lib
- Linux:
Linking Your Application#
Linux/macOS:
gcc -o myapp myapp.c -L. -lcedarling_c -Wl,-rpath,.
Windows (MSVC):
cl myapp.c cedarling_c.lib
Runtime Notes:
-
Linux: Add the library directory to
LD_LIBRARY_PATH:export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH -
macOS: Add the library directory to
DYLD_LIBRARY_PATH:export DYLD_LIBRARY_PATH=$(pwd):$DYLD_LIBRARY_PATH -
Windows: Place
cedarling_c.dllalongside your executable or in a directory inPATH.
Configuration#
Before performing authorization, you need to configure a Cedarling instance. Configuration is provided as a JSON string.
Basic Configuration#
const char* config = "{"
"\"CEDARLING_APPLICATION_NAME\": \"MyApp\","
"\"CEDARLING_POLICY_STORE_ID\": \"your-policy-store-id\","
"\"CEDARLING_LOG_LEVEL\": \"INFO\","
"\"CEDARLING_LOG_TYPE\": \"std_out\","
"\"CEDARLING_POLICY_STORE_LOCAL_FN\": \"/path/to/policy-store.yaml\""
"}";
Configuration Properties#
| Property | Description |
|---|---|
CEDARLING_APPLICATION_NAME |
Name of your application |
CEDARLING_POLICY_STORE_ID |
ID of the policy store |
CEDARLING_LOG_LEVEL |
Logging level (DEBUG, INFO, WARN, ERROR) |
CEDARLING_LOG_TYPE |
Log output type (std_out, memory, off) |
CEDARLING_POLICY_STORE_LOCAL_FN |
Path to local policy store file |
CEDARLING_JWT_SIG_VALIDATION |
Enable/disable JWT signature validation |
CEDARLING_JWT_STATUS_VALIDATION |
Enable/disable JWT status validation |
For complete configuration documentation, see Configuration Properties.
Initialize Cedarling#
#include <stdio.h>
#include <stdint.h>
#include "cedarling_c.h"
int main() {
// Initialize the library
if (cedarling_init() != 0) {
printf("Failed to initialize Cedarling library\n");
return 1;
}
// Print version
printf("Cedarling version: %s\n", cedarling_version());
// Configuration JSON
const char* config = "{"
"\"CEDARLING_APPLICATION_NAME\": \"MyApp\","
"\"CEDARLING_POLICY_STORE_ID\": \"example-policy-store\","
"\"CEDARLING_LOG_LEVEL\": \"INFO\","
"\"CEDARLING_LOG_TYPE\": \"std_out\","
"\"CEDARLING_POLICY_STORE_LOCAL_FN\": \"./policy-store.yaml\""
"}";
// Create a new instance
CedarlingInstanceResult instance_result;
int ret = cedarling_new(config, &instance_result);
if (ret != 0) {
printf("Failed to create instance: %s\n", instance_result.error_message);
cedarling_free_instance_result(&instance_result);
return 1;
}
uint64_t instance_id = instance_result.instance_id;
printf("Instance created with ID: %llu\n", (unsigned long long)instance_id);
cedarling_free_instance_result(&instance_result);
// Use instance for authorization...
// Cleanup
cedarling_shutdown(instance_id);
cedarling_drop(instance_id);
// Shuts down all library-held instances and clears this thread's last error
cedarling_cleanup();
return 0;
}
Authorization#
Cedarling provides two main authorization interfaces:
- Multi-Issuer Authorization: Processes JWT tokens from multiple issuers. Token data is mapped to Cedar entities based on
token_metadataconfiguration. - Unsigned Authorization: Allows you to pass principals directly, bypassing tokens entirely. Useful for internal data or when tokens are not available.
Multi-Issuer Authorization#
Use cedarling_authorize_multi_issuer when authorizing with JWT tokens:
1. Define the request JSON:
const char* request = "{"
"\"tokens\": ["
" {\"mapping\": \"Jans::Access_token\", \"payload\": \"eyJhbGciOiJIUzI1NiIs...\"},"
" {\"mapping\": \"Jans::Id_token\", \"payload\": \"eyJhbGciOiJIUzI1NiIs...\"}"
"],"
"\"action\": \"Jans::Action::\\\"Update\\\"\","
"\"resource\": {"
" \"cedar_entity_mapping\": {\"entity_type\": \"Jans::Issue\", \"id\": \"random_id\"},"
" \"org_id\": \"some_long_id\""
"},"
"\"context\": {}"
"}";
2. Authorize:
CedarlingResult auth_result;
int ret = cedarling_authorize_multi_issuer(instance_id, request, &auth_result);
if (ret == 0) {
printf("Authorization result: %s\n", (char*)auth_result.data);
// Parse JSON result to check decision
} else {
printf("Authorization error: %s\n", auth_result.error_message);
}
cedarling_free_result(&auth_result);
See Multi-Issuer Authorization for more details.
Unsigned Authorization#
Use cedarling_authorize_unsigned when you have custom principals (not derived from JWTs):
1. Define the request JSON with principals:
const char* request = "{"
"\"principals\": ["
" {"
" \"cedar_entity_mapping\": {\"entity_type\": \"Jans::TestPrincipal1\", \"id\": \"user123\"},"
" \"is_ok\": true"
" }"
"],"
"\"action\": \"Jans::Action::\\\"UpdateForTestPrincipals\\\"\","
"\"resource\": {"
" \"cedar_entity_mapping\": {\"entity_type\": \"Jans::Issue\", \"id\": \"random_id\"},"
" \"org_id\": \"some_long_id\","
" \"country\": \"US\""
"},"
"\"context\": {}"
"}";
2. Authorize:
CedarlingResult auth_result;
int ret = cedarling_authorize_unsigned(instance_id, request, &auth_result);
if (ret == 0) {
char* result_str = (char*)auth_result.data;
printf("Authorization result: %s\n", result_str);
// Check if decision is true
if (strstr(result_str, "\"decision\":true") != NULL) {
printf("Access granted\n");
} else {
printf("Access denied\n");
}
} else {
printf("Authorization error: %s\n", auth_result.error_message);
}
cedarling_free_result(&auth_result);
Context Data API#
The Context Data API allows you to push external data into the Cedarling evaluation context, making it available in Cedar policies through the context.data namespace.
Push Data#
Store data in the context for use in policy evaluation:
TTL is given in seconds; use <= 0 when you do not want to override the store default.
const char* value = "{\"role\": [\"admin\", \"editor\"], \"level\": 5}";
CedarlingResult push_result;
int ret = cedarling_context_push(instance_id, "user:123", value, 300, &push_result);
if (ret != 0) {
printf("Error pushing data: %s\n", push_result.error_message);
}
cedarling_free_result(&push_result);
Get Data#
Retrieve stored data by key:
CedarlingResult get_result;
int ret = cedarling_context_get(instance_id, "user:123", &get_result);
if (ret == 0) {
char* value = (char*)get_result.data;
if (strcmp(value, "null") != 0) {
printf("Value: %s\n", value);
} else {
printf("Key not found\n");
}
}
cedarling_free_result(&get_result);
Remove Data#
Remove a specific entry:
CedarlingResult remove_result;
int ret = cedarling_context_remove(instance_id, "user:123", &remove_result);
if (ret == 0) {
printf("Remove result: %s\n", (char*)remove_result.data); // {"removed": true/false}
}
cedarling_free_result(&remove_result);
Clear All Data#
Remove all entries from the data store:
CedarlingResult clear_result;
int ret = cedarling_context_clear(instance_id, &clear_result);
cedarling_free_result(&clear_result);
List All Entries#
List all entries with their metadata:
CedarlingResult list_result;
int ret = cedarling_context_list(instance_id, &list_result);
if (ret == 0) {
printf("Entries: %s\n", (char*)list_result.data); // JSON array
}
cedarling_free_result(&list_result);
Get Statistics#
Get statistics about the data store:
CedarlingResult stats_result;
int ret = cedarling_context_stats(instance_id, &stats_result);
if (ret == 0) {
printf("Stats: %s\n", (char*)stats_result.data);
}
cedarling_free_result(&stats_result);
Using Data in Cedar Policies#
Data pushed via the Context Data API is automatically available in Cedar policies under the context.data namespace:
permit(
principal,
action == Jans::Action::"read",
resource
) when {
context.data["user:123"].role.contains("admin")
};
Logging#
Retrieve logs stored in memory:
// Get all logs and clear the buffer
CedarlingStringArray logs;
int ret = cedarling_pop_logs(instance_id, &logs);
if (ret == 0) {
printf("Retrieved %zu logs\n", logs.count);
for (size_t i = 0; i < logs.count; i++) {
printf("Log %zu: %s\n", i + 1, logs.items[i]);
}
cedarling_free_string_array(&logs);
}
// Get all log IDs
CedarlingStringArray log_ids;
ret = cedarling_get_log_ids(instance_id, &log_ids);
if (ret == 0) {
printf("Found %zu log IDs\n", log_ids.count);
cedarling_free_string_array(&log_ids);
}
// Get logs by tag
CedarlingStringArray tagged_logs;
ret = cedarling_get_logs_by_tag(instance_id, "authorization", &tagged_logs);
cedarling_free_string_array(&tagged_logs);
// Get logs by request ID
CedarlingStringArray request_logs;
ret = cedarling_get_logs_by_request_id(instance_id, "req-123", &request_logs);
cedarling_free_string_array(&request_logs);
// Get a specific log by ID
CedarlingResult log_result;
ret = cedarling_get_log_by_id(instance_id, "log-id-here", &log_result);
if (ret == 0) {
printf("Log: %s\n", (char*)log_result.data);
} else if (log_result.error_code == KEY_NOT_FOUND) {
/* Log id does not exist (instance was valid) */
}
cedarling_free_result(&log_result);
Error Handling#
All functions return an error code (0 for success, non-zero for failure). Additional error information is available through:
// Last error string is owned by the caller — free with cedarling_free_string
char* error = cedarling_get_last_error();
if (error) {
printf("Last error: %s\n", error);
cedarling_free_string(error);
}
// Clear the last error
cedarling_clear_last_error();
Error Codes#
Values match the CedarlingErrorCode enum in cedarling_c.h (names are SCREAMING_SNAKE_CASE in C).
| Code | Name | Description |
|---|---|---|
| 0 | Success | Operation completed successfully |
| 1 | InvalidArgument | Invalid argument provided |
| 2 | InstanceNotFound | Instance not found |
| 3 | JsonError | JSON parsing error |
| 4 | AuthorizationError | Authorization error |
| 5 | ConfigurationError | Configuration error |
| 6 | Internal | Internal error |
| 7 | KeyNotFound | Requested key or id does not exist (e.g. unknown log id) |
Memory Management#
- Do not free the pointer from
cedarling_version(): it points at static data for the library version string. - Must free the pointer from
cedarling_get_last_error()withcedarling_free_string()when non-null (each call returns a fresh copy).
Free struct payloads with the matching API after use:
// Owned CedarlingResult
cedarling_free_result(&result);
// Owned CedarlingInstanceResult
cedarling_free_instance_result(&instance_result);
// Owned CedarlingStringArray
cedarling_free_string_array(&array);
// Owned standalone C string (e.g. from cedarling_get_last_error)
cedarling_free_string(str);
Complete Example#
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "cedarling_c.h"
int main() {
// Initialize
if (cedarling_init() != 0) {
printf("Failed to initialize\n");
return 1;
}
printf("Cedarling version: %s\n", cedarling_version());
// Configuration
const char* config = "{"
"\"CEDARLING_APPLICATION_NAME\": \"ExampleApp\","
"\"CEDARLING_POLICY_STORE_ID\": \"example-store\","
"\"CEDARLING_LOG_LEVEL\": \"DEBUG\","
"\"CEDARLING_LOG_TYPE\": \"memory\","
"\"CEDARLING_POLICY_STORE_LOCAL_FN\": \"./policy-store.yaml\""
"}";
// Create instance
CedarlingInstanceResult instance_result;
if (cedarling_new(config, &instance_result) != 0) {
printf("Error: %s\n", instance_result.error_message);
cedarling_free_instance_result(&instance_result);
return 1;
}
uint64_t instance_id = instance_result.instance_id;
cedarling_free_instance_result(&instance_result);
// Push context data
CedarlingResult push_result;
cedarling_context_push(instance_id, "app:config",
"{\"feature_flags\": {\"premium\": true}}", 0, &push_result);
cedarling_free_result(&push_result);
// Authorize
const char* request = "{"
"\"principals\": [{"
" \"cedar_entity_mapping\": {\"entity_type\": \"Jans::TestPrincipal1\", \"id\": \"user1\"},"
" \"is_ok\": true"
"}],"
"\"action\": \"Jans::Action::\\\"UpdateForTestPrincipals\\\"\","
"\"resource\": {\"cedar_entity_mapping\": {\"entity_type\": \"Jans::Issue\", \"id\": \"res1\"}, \"org_id\": \"org1\", \"country\": \"US\"},"
"\"context\": {}"
"}";
CedarlingResult auth_result;
if (cedarling_authorize_unsigned(instance_id, request, &auth_result) == 0) {
printf("Authorization: %s\n", (char*)auth_result.data);
} else {
printf("Authorization error: %s\n", auth_result.error_message);
}
cedarling_free_result(&auth_result);
// Get logs
CedarlingStringArray logs;
if (cedarling_pop_logs(instance_id, &logs) == 0) {
printf("Logs: %zu entries\n", logs.count);
cedarling_free_string_array(&logs);
}
// Cleanup
cedarling_shutdown(instance_id);
cedarling_drop(instance_id);
cedarling_cleanup(); /* all instances + this thread's last error */
printf("Done!\n");
return 0;
}
Thread Safety#
All functions in the Cedarling C library are thread-safe. Multiple threads can safely use the same Cedarling instance concurrently.
Next Steps#
- Explore Cedar Policy Language for writing authorization policies
- Learn about Multi-Issuer Authorization for JWT-based authorization
- Review Configuration Properties for all available options