Configure (2 minutes)
Configure, sign in, save, and read a leaderboard, then pick the systems you need. Everything below runs on any platform the toolkit supports, including WebGL. Minimum editor: Unity 6.3 LTS (6000.3).
- Open the Setup Wizard: Tools → ShipIt → Firebase Toolkit → Setup Wizard.
- Paste your project credentials: the Web API key and Project ID from Firebase console → Project settings → General. Press Test Connection, which validates the key and echoes the project it belongs to, then Save. This writes
Assets/Resources/ShipIt/FirebaseSettings.asset. - Enable sign-in methods: in Firebase console → Authentication → Sign-in method, enable Anonymous (guest play) and/or Email/Password.
- Publish a rules pack: toolkit window → Rules Packs →
game_basics.rules. Cloud saves and scores are refused until their rules exist; default-deny is correct behaviour, not a bug.
Add moderation.rules and lobbies.rules when those features are used; each of the seven packs documents what it protects.
Initialize and sign in
// once, at startup (the Firebase Bootstrap component can do this for you)
FirebaseClient.InitializeFromResources();
var auth = FirebaseClient.Instance.Auth;
// guest: enable Anonymous in the console first
var session = await auth.SignInAnonymouslyAsync();
// or email/password
var session = await auth.SignInWithEmailAsync(email, password);
Sessions persist and refresh automatically. Server failures arrive as FirebaseAuthException with .ServerCode (EMAIL_EXISTS, INVALID_LOGIN_CREDENTIALS, …). Upgrade guests with LinkEmailPasswordAsync: the uid stays the same, so progress survives the device.
Cloud saves
[Serializable] public class SaveData { public int level; public string hero; }
await FirebaseClient.Instance.CloudSave
.SaveAsync("slot1",
new SaveData { level = 3, hero = "Ada" });
var state = await FirebaseClient.Instance.CloudSave
.LoadAsync<SaveData>("slot1");
Enable Offline Cache in settings and writes made while disconnected are queued durably and replayed on reconnect.
Leaderboard
await FirebaseClient.Instance.Leaderboard
.SubmitScoreAsync(score);
var top = await FirebaseClient.Instance.Leaderboard
.GetTopScoresAsync(10);
Personal-best semantics: a lower score never overwrites a higher one, and the shipped rules enforce the same server-side.
Check it in the editor: open the Collection Browser, which reads exactly what a signed-out client sees, then the Data Browser or Query Console for anything else.
Before you ship: move trust server-side
Startup code is client code. Before shipping anything with value, move currency grants and spends, score validation, IAP receipt checks and mail claims into Cloud Functions. Function Templates ships eight deployable templates for exactly these, and its validatePurchase template verifies receipts against Google Play and the App Store server-side rather than trusting the client.
Then run the Pentest screen against your project: it sweeps as an anonymous caller and reports what your rules actually expose.
Auth errors
Every server error the toolkit surfaces, mapped to meaning and fix. All exceptions carry the raw server message; typed subclasses add context. Auth errors arrive on FirebaseAuthException.ServerCode.
| ServerCode | Meaning | Fix |
|---|---|---|
EMAIL_EXISTS | Address already registered | Sign in instead, or send a password reset |
INVALID_LOGIN_CREDENTIALS / INVALID_PASSWORD / EMAIL_NOT_FOUND | Wrong email or password | Check credentials; legacy projects may return the older codes |
INVALID_EMAIL | Malformed address | Validate before submit |
USER_DISABLED | Account disabled in the console | Console → Authentication → Users |
WEAK_PASSWORD | Fewer than 6 characters | Enforce length client-side too |
TOO_MANY_ATTEMPTS_TRY_LATER | Device throttled | Back off; minutes, not seconds |
OPERATION_NOT_ALLOWED / PASSWORD_LOGIN_DISABLED | Provider disabled | Enable it in Console → Authentication → Sign-in method |
INVALID_ID_TOKEN | Session token expired or revoked | Restore or sign in again; auto-refresh normally handles this |
ADMIN_ONLY_OPERATION | Restricted operation | Needs the Admin SDK, server-side |
Firestore errors
Firestore failures arrive on FirebaseFirestoreException.Status.
| Status | Meaning | Fix |
|---|---|---|
PERMISSION_DENIED | Security Rules rejected the call | Check the rules in the console or the Rules Simulator; signed-out callers need public read rules |
NOT_FOUND | Document absent | Catch and treat as create-or-default |
FAILED_PRECONDITION | Missing composite index (queries) or a failed precondition | The message links to the index-creation URL; open it |
RESOURCE_EXHAUSTED | Quota or billing limit | Check usage; the offline queue pauses on this status |
ABORTED | Transaction contention | RunInTransactionAsync retries automatically; raise maxAttempts for hot paths |
UNAVAILABLE | Transient server or network issue | The retry policy already retried; surface it as "try again" |
UNAUTHENTICATED | Token missing or expired and no session restored | Sign in first |
Storage errors
Storage failures arrive as FirebaseStorageException.
| Symptom | Cause | Fix |
|---|---|---|
| 403 on upload or read | Rules deny the path or caller | The game_basics pack covers saves and scores; add rules for your own folders |
| 404 on download | Object absent, or the wrong bucket | Check the bucket in the Setup Wizard and the object path's case |
| 402, or a billing-flavoured 403 | The project is not on the Blaze plan | Cloud Storage for Firebase needs Blaze; Spark projects have had no bucket access since 2026-02-03 |
PROJECT_ID.firebasestorage.app for projects created since September 2024, and PROJECT_ID.appspot.com for older ones. Set the exact name shown in Firebase console → Storage.Functions errors
Callable failures arrive as FirebaseFunctionsException, with the HttpsError status and details attached.
| Symptom | Fix |
|---|---|
| 401 or 403 on a call | The function requires auth: sign in before calling, and check its allowUnauthenticated configuration |
The package's Documentation~/Troubleshooting.md carries more tables: vector search, moderation and lobby symptoms, the remaining Functions codes (404, INVALID_ARGUMENT), and the download-token case for public Storage URLs.
Top gotchas
- WebGL: everything here is plain HTTP, but you still cannot open sockets. Realtime Database streaming uses SSE over HTTP. Do not import the official Firebase Unity SDK alongside this toolkit.
- "Setup incomplete" at runtime: the settings asset is not at
Assets/Resources/ShipIt/FirebaseSettings.asset. Create it from the Setup Wizard. - Rules debugging: the Collection Browser shows exactly what an anonymous caller sees. If a call works there but fails in-game, your token or provider is the difference; check the sign-in state.
- Offline writes: enable Offline Cache in settings; deferred documents come back flagged
DeferredLocally. Transactions never queue; they are always live. - Token expiry mid-session: handled by SecureToken refresh;
GetValidIdTokenAsync()collapses concurrent refreshes into one. - WebGL hosting: if the loading bar hangs at 90%, the host is serving the
.gzbuild files withoutContent-Encoding: gzip. Enable Decompression Fallback in the WebGL Player Settings, or configure the host to send the header. - Aggregations read as zero: fixed in 0.13.0, where the parser read the response one level too high.
AggregateAsyncnow reads both the documented and the flattened shapes.
Platform support
| Platform | Status | Notes |
|---|---|---|
| Windows / macOS / Linux (Mono and IL2CPP) | Supported | Full feature set |
| Android / iOS (IL2CPP) | Supported | Full feature set; link.xml guards reflection-materialized types |
| WebGL | Supported | The reason this toolkit exists; see WebGL specifics |
Scripting backend: pure C# over UnityWebRequest. No native plugins, no JavaScript bridges, no Reflection.Emit, so it is safe for IL2CPP/AOT and for the WebAssembly sandbox. Build proofs exist for WebGL and Windows IL2CPP; see Verified builds.
WebGL specifics
- All networking uses
UnityWebRequest, the only HTTP stack available in the browser sandbox. Realtime listeners that need raw sockets (Firestore gRPC streams) are out of scope. - Session persistence and the offline mutation queue go through
PlayerPrefs, which Unity maps to IndexedDB on the web. Clearing browser data clears them, by design of the platform. - CORS: Firebase's googleapis endpoints send permissive headers for API calls, so hosting your build on any origin works without extra configuration.
- OAuth popup or redirect sign-in and the phone reCAPTCHA widget need a JavaScript bridge, which the toolkit deliberately does not have. Use email and password, anonymous, custom tokens, or a provider token obtained natively.
- Builds stay lean: the toolkit adds only C# code, and the engine's own HTTP client does the transport.
Known limitations
| Limitation | Reason | Workaround |
|---|---|---|
| No Firestore realtime listeners | Upstream realtime is gRPC-only; REST has no documented stream | Use the Realtime Database (RtdbService.Listen), which streams over SSE, or poll Firestore queries |
| No Query Explain | It accepts only IAM server credentials, and service accounts must never ship in a client | Run it from server-side tooling |
| Vector search needs an index | Firestore requires a vectorConfig index for findNearest | The Vector Search screen exports the exact firestore.indexes.json snippet |
| App Check uses the debug provider | Native attestation (Play Integrity, DeviceCheck) needs platform plugins | Implement IAppCheckTokenProvider; the debug provider works while enforcement is off |
| Resumable upload chunk size is fixed per call | Simplicity | Pass a larger chunkSize for big files |
| Rules Simulator probes real paths | The REST surface cannot read a project's rules | Read its verdicts as evidence from real allow and deny responses, not as a rules parser |
Verified builds
The v0.23.0 release run on Unity 6000.3.11f1, September 20, 2026. The build checks use the Panel Tour demo scene.
| Check | How | Result |
|---|---|---|
| EditMode suite | Unity Test Runner, or CI testMode: editmode | 776 passed, 0 failed |
| PlayMode smoke suite | Unity Test Runner, headless; demo boot path and drop-in panel construction | 17 passed |
| WebGL build | Batch build of the Panel Tour scene | 0 errors, 0 warnings, about 10.1 MB |
| WebGL runtime boot | ci/webgl-proof.cjs in headless Chromium | Pass, no page errors |
| Windows IL2CPP build | The same scene with the IL2CPP scripting backend | 0 errors, 0 warnings |
Unity.exe -batchmode -nographics -quit -projectPath <project> \
-executeMethod ShipIt.Firebase.EditorTools.WebGLVerification.BuildDemo
Unity.exe -batchmode -nographics -quit -projectPath <project> \
-executeMethod ShipIt.Firebase.EditorTools.Il2CppVerification.BuildDemo
The WebGL boot check matters most: the official Firebase Unity SDK's documented failure for this combination is a crash on startup, and a boot test catches exactly that class of defect.
Support
Email shipit.unity@gmail.com with your Unity version, the toolkit version, the target platform and the relevant log lines. The complete engineering changelog ships inside the package as CHANGELOG.md; highlights are on the release notes page.