Radikant-Supabase-C

database 🗄️ driver

Device Screen

Supabase c driver for backend platform that provides a full suite of tools on top of PostgreSQL, including authentication, real-time subscriptions, storage, edge functions, and auto-generated APIs. It allows developers to launch a production-ready backend in minutes while keeping full control over their data and infrastructure.

Firebase_Logo

Backend API

Symmetric, Asysmetic, Block and Stream ciphers

cms icon

Functions

Use TypeScript Functions

cms icon

SSE

Subscribe to database events/

cms icon

Auth

Login using Token

cms icon

File

Upload and download files.

cms icon

Realtime

Realtime events

Introduction

This tiny library allows to authenticate as Firebase-client against the Firebase backend using Firebase Auth. If a firebase user exists inside the firebase auth, you can use those credentials to log that user in from c, and subsequently make requests against the Firebase backend. You can very easily read and write to the Firebase Realtime Database (RTDB) but also to the Firestore Database.

In addition this library supports service accounts, which are “special" accounts assigned in Google Cloud that can be assigned granual and elevated rights. A service account can be used in an authentication context on a server to authenticate firebase client that are connecting and presenting a JWT token. But can also utilize  the service accounts elevated rights to acces permissive parts of the Firebase databases.

Auth

FileEditViewProject
â–¶
â– 
SupabaseTest — Legacy vs Modern
void test_legacy() {
const char *url = "https://project-id.supabase.co";
const char *anon_key = "eyJhbGciOiJIUzr"; // legacy
 
SB_Client_t *client = sb_client_init(url, anon_key);
sb_error_t err = sb_client_sign_in(client, "charlie@radikant.nl", "12341234");
err = sb_client_sign_out(client);
 
sb_client_free(client);
}
 
void test_modern() {
const char *url = "https://project-id.supabase.co";
const char *api_key = "sb_publishable_api_key"; // modern
 
SB_Client_t *client = sb_client_init(url, api_key);
sb_error_t err = sb_client_sign_in(client, "charlie@radikant.nl", "12341234");
err = sb_client_sign_out(client);
sb_client_free(client);
}
23:32:00 [INFO] Application started (/Users/charrlie/Desktop/Radikant-Log-C/test/test_log.c:87) 23:32:00 [SUCCESS] Database connected (/Users/charrlie/Desktop/Radikant-Log-C/test/test_log.c:88) 23:32:00 [ERROR] Failed to load configuration file (/Users/charrlie/Desktop/Radikant-Log-C/test/test_log.c:89) 23:32:00 [FAILURE] Unhandled exception occurred (/Users/charrlie/Desktop/Radikant-Log-C/test/test_log.c:90) 23:32:00 [ALERT] System resources critically low (/Users/charrlie/Desktop/Radikant-Log-C/test/test_log.c:91) 23:32:00 [WARN] Deprecated API usage detected (/Users/charrlie/Desktop/Radikant-Log-C/test/test_log.c:92)

User
Initially the user will log in with a username and a password beloging tot he firebase user, it the requests a JWT id token firebase will subequently return a response in json format containing; an id token and a refresh token. The id token can be used for TTL 1 hour to make requests and therefore is shortlived. The library doesnt automatically refresh tokens but checks upon ` request if the current token in possesion is still valid and if its expired use the refresh token to get a new id token and acces token. Therefore during a lengthy session the plain username and password is only sent once. It is possible to store the refresh token across reboots and continue a session but its comes with its own special security considerations.

File

FileEditViewProject
â–¶
â– 
SupabaseStorage — File Ops
int main() {
// 1. Init
SB_Client_t *client = sb_client_init(URL, KEY);
sb_client_sign_in(client, EMAIL, PASSWORD);
 
// 3. Create dummy file
const char *fname = "/Users/charrlie/Desktop/test_upload.txt";
FILE *f = fopen(fname, "w");
if (f) {
fprintf(f, "Hello Supabase Storage! Random: %d", rand());
fclose(f);
}
 
// 4. Upload
sb_error_t err = sb_storage_upload(client, BUCKET, "test_upload.txt", fname);
 
// 5. Download
const char *down_name = "test_download.txt";
err = sb_storage_download(client, BUCKET, "test_upload.txt", down_name);
remove(down_name); remove(fname);
sb_client_free(client);
return 0;
}

Service Account
The service account authentication flow is different from a regular users. The Service account  code generates a jwt-token with subject and issuer sub "your-custom-server@your-project.iam.gserviceaccount.com” and some other attributes. Most importantly it signs the JWT with a private key that belongs only to that  particular service account and therefore mints a special jwt token. This token is subsequently sent to a google endpoint that will return an OAuth access token. This OAuth token can subsequently be used to make request against firebase services.

SSE

FileEditViewProject
â–¶
â– 
SupabaseRealtime — SSE Listener
// Callback
bool my_sse_callback(const void *data, size_t len, void *userdata) {
printf("[SSE Event] Payload: \"%.*s\"\n", (int)len, (const char*)data);
fflush(stdout);
return true;
}
 
int main() {
SB_Client_t *client = sb_client_init(url, api_key);
sb_error_t err_auth = sb_client_sign_in(client, "charlie@radikant.nl", "12341234");
// Listen to 'sse-1' function
sb_error_t err = sb_sse_listen(client, "sse-1", my_sse_callback, NULL);
sb_client_free(client);
return 0;
}

Realtime

FileEditViewProject
â–¶
â– 
SupabaseRealtime — Subscription
int main() {
signal(SIGINT, handle_sigint);
const char *url = "https://api-key.supabase.co";
const char *anon_key = "eyJhbGci-jwt"; // legacy
 
SB_Client_t *client = sb_client_init(url, anon_key);
sb_realtime_connect(rt);
sb_realtime_subscribe(rt, "public", "Employees", on_employee_change, NULL);
 
sb_realtime_free(rt);
sb_client_free(client);
return 0;
}

Functions

FileEditViewProject
â–¶
â– 
EdgeFunctions — Invoke
SB_Client_t *client = sb_client_init(url, api_key);
sb_error_t err_auth = sb_client_sign_in(client, "charlie@radikant.nl", "12341234");
 
rjson_value *body = rjson_object_new();
rjson_object_add(body, "name", rjson_string_new("Gemsssini"));
 
int status = 0;
char *resp_body = NULL;
 
// --- Test Invoke Function
sb_error_t err = sb_functions_invoke(client, "hello-world", body, NULL, &status, &resp_body);
 
printf("Status: %d\n", status);
if (resp_body) {
printf("Body: %s\n", resp_body);
free(resp_body);
}
rjson_free(body);