Supabase Cloud Saving
Crystal Save integrates directly with Supabase Storage to store save files, metadata, and screenshots in the cloud. The integration supports public buckets, per-device folders, Unity Authentication, and custom JWT-based authorization with Row-Level Security (RLS).
1. Prepare Your Supabase Project
Create a Supabase Project in the Supabase Dashboard.
Open “Explore Storage” in the Storage section.
Create a bucket (e.g.
game-saves
).Enable Row-Level Security (RLS) for the bucket by adding policies under storage.objects:
Read (SELECT)
Insert (upload)
Update (overwrite)
Delete (optional)
Example shared public bucket rule:
bucket_id = 'game-saves'
Example per-user private folder rules:
-- Allow only the authenticated user to read/write their own saves
bucket_id = 'game-saves'
AND name LIKE ('users/' || auth.uid() || '/%')
(See “Supabase User ID Strategy” below for more.)
2. Configure Crystal Save Settings
Create or open a SaveSettings asset:
Tools → Crystal Save → Settings → Create Save Settings File
Enable Cloud Save.
Set Save Backend to
Supabase
.Fill in:
Supabase URL (from your project dashboard)
Supabase Anon Key
Storage Bucket name (e.g.
game-saves
)
Choose a User Folder Strategy:
Shared – one global folder (
users/guest/...
).PublicPerBuild – shared folder but different per release build.
GuidPerDevice – client generates a GUID and uses
users/{guid}
.UnityAuthentication – folder from Unity Authentication
auth.uid()
.Custom – fully custom folder path & token (see below).
3. Using a Custom Folder & Auth Resolver
If using the Custom strategy:
Assign a
CustomFolderAuthResolver
(or your ownIUserAuthorizationResolver
) tocustomUserFolderResolver
in SaveSettings.The resolver:
Persists a
userId
and optional JWT token.Implements:
string ResolveUserFolder(); // e.g. "users/{uid}" string ResolveAccessKey(); // JWT or anon key
Fires SupabaseAuthRelay.FireLoggedIn(userId)
automatically when cached credentials exist.
Call:
resolver.SetUserId(uid);
resolver.SetToken(jwt);
after login to store credentials.
4. Handling Authentication
Crystal Save ships with SupabaseRestAuth
helper methods for:
Sign-up (
SignUpWithEmailPassword
)Sign-in (
SignInWithPassword
)Send magic link (
SendMagicLink
)
These methods:
POST to Supabase Auth REST endpoints.
Return
jwt
(access token) anduid
(user ID).Store them in your resolver to enable authenticated storage.
Example sign-in flow:
StartCoroutine(SupabaseRestAuth.SignInWithPassword(
supabaseUrl, supabaseAnonKey,
email, password,
(jwt, uid) => {
resolver.SetUserId(uid);
resolver.SetToken(jwt);
SupabaseAuthRelay.FireLoggedIn(uid);
},
error => Debug.LogError(error)
));
5. Waiting for Login Before Saving
If you require login before any cloud save:
await SaveManager.Instance.WaitForSupabaseLoginAsync();
This waits until SupabaseAuthRelay
reports a login or times out.
6. Saving & Loading with Supabase
Once logged in:
Save:
await SaveManager.Instance.SaveAsync(slotNumber);
Load:
await SaveManager.Instance.LoadAsync(slotNumber);
Behind the scenes:
Files and metadata are uploaded to
bucket/users/{uid}/...
Authorization header is set from:
Resolver’s JWT (custom strategy)
Anon key (if no JWT)
7. Local Mirrors & Offline Play
If Keep Local Mirror is enabled:
SupabaseSaveSystem
writes.sav
and.meta
files locally.Allows offline play and syncs when back online.
8. Supabase User ID Strategies & RLS Summary
Shared
users/guest/...
bucket_id = 'game-saves'
PublicPerBuild
users/{buildId}/...
Same as Shared
GuidPerDevice
users/{guid}/...
Same as Shared (public bucket)
UnityAuthentication
users/{auth.uid()}/...
`bucket_id='game-saves' AND name LIKE ('users/'
Custom
Depends on resolver
Match resolver path in RLS
9. Logout
SaveManager.Instance.LogoutFromSupabase();
Clears resolver credentials.
Switches back to guest/public access if possible.
10. Tips & Best Practices
Use GuidPerDevice for quick per-device separation without full auth.
Use UnityAuthentication or Custom+JWT for secure per-user isolation.
Keep your anon key private (even though it’s public by design).
Use RLS to enforce folder isolation on the server side.
Last updated