Skip to content

Instantly share code, notes, and snippets.

@wiverson
Last active April 26, 2023 22:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wiverson/86ec69ddf13b137306842348eaec37a2 to your computer and use it in GitHub Desktop.
Save wiverson/86ec69ddf13b137306842348eaec37a2 to your computer and use it in GitHub Desktop.

Unity with Supabase

Here are my notes on getting Supabase working with Unity.

If you are only interested in using Supabase with your project with email/password accounts, it's pretty easy. The main thing is to use the Unity NuGet package to install UniTask (async/await) and supabase-csharp.

However, the really neat part is also supporting native Sign in With Apple on iOS. Apple is increasingly requiring developers to support Sign in with Apple on iOS, and it does provide a pretty seamless experience. One nice thing is that by following these steps Supabase integrates both the on-device native Sign in with Apple and the web-based version of Sign in with Apple. This means you could do something like having a iOS Unity project for your main gameplay or application, and also allow users to use the same account to access details in a web application (e.g. something you build in SvelteKit or React or whatever).

I'm assuming you want to support Sign in with Apple, so I'm providing the steps in the order I would recommend for starting out with a new project. If you just want to use email/password accounts you can skip the Sign in with Apple stuff.

Get Supabase working with Sign in with Apple via a vanilla JavaScript project.

This requires a number of steps, but it's reasonably well documented on the Supabase site. Make sure you follow every step, one at a time, and are very careful to store all of the various artifacts (scripts, keys, etc.) in a secure, backed up system. You might find it best to store much of this information in a secure vault or password manager. Take notes, take your time, make sure you read as much documentation as you can while going through all of this stuff.

Pay particular attention to the App ID and Service ID values. The App ID you create will have a Bundle identifier, which you will use in many other places later, including in your Unity build and when you configure your Supabase project.

I recommend making sure your Supabase project can successfully complete a Sign in with Apple flow before moving on. There are a LOT of steps, and if you mess any of them up things will NOT work. If you have a modicum of experience with modern web development (e.g. npm, SvelteKit or React, etc) you should be able to follow the Supabase documentation and get signed in via an Apple sign in. This will use the OAuth web flow, but that's actually fine - the same Apple Service ID keys that are used with the OAuth flow are also used to validate the information that comes from the Sign in with Apple native dialog later.

To illustrate how easy it easy to mess things up consider the section of the Supabase setup instructions that requires you to install Ruby and run a script. This script generates a JWT from the private .p8 key file you download from Apple. This JWT is then copied and pasted as a secret into the Supabase console. I initially made the mistake of including an extra character (space? return?) when I pasted it into the Supabase console, and that was enough to break everything with no helpful error messages. If that sounds frustrating... you're right! There are many such steps that are unfortunately similarly fragile.

Supabase improvement: Improve the flow for this configuration and setup. For example, allow a user to simply upload the .p8 file instead of running the Ruby script locally. Add as much error handling in the console to make it harder to break.

WARNING: You will have to repeat the process of generating this JWT every six months or all Sign in with Apple services will break on Supabase, which will mean your users will no longer be able to log in!

Supabase improvement: Add an email notification flow to automatically warn users with a month's notice that their Apple JWT secret is going to expire, with a link back to the documentation on how to update it.

Add Sign in with Apple to Unity

Now that your Supabase project is set up and working with Sign in with Apple, you'll want to be able to bring up the native Sign in with Apple project in your Unity project.

There are several options for integrating the native Sign in with Apple code with a Unity project, but https://github.com/lupidan/apple-signin-unity seems to be the most full featured and it's the one I got working. Again, follow the setup instructions carefully - a single skipped step can break the system. Similarly without useful errors.

WARNING: Remember that you are working with a live Apple system as part of the Sign in with Apple process. If you are absolutely certain that things are set up correctly on your side and things are still not working, you may want to consider opening up a ticket with Apple. Apple does offer Code-Level Support and as of this writing a $99 Apple Developer account includes two incidents. If there is a problem on the Apple side, you may be able to get them to refund a credit. If it's not and they help you, it's well worth it.

When you go through the documenation, you may be tempted to skip a step or try to do add thge entitlements to XCode manually. You will absolutely want to add a script to add the Sign in With Apple capability to your Unity-generated XCode project. You will be generating XCode projects a LOT to build and test Sign in with Apple and you really don't want to have to do it manually every time.

Make sure you are using the same App bundle id for your Unity project as you used for your Supabase project setup. You may get a signing error in XCode and need to turn automatic signing on in your XCode project. I set up automatic signing in Unity and for some reason I still had to turn it back on again in XCode. This is an area that might be solvable via a Unity build script, but I haven't researched this yet.

Success for this step is getting an Apple identifier back out of the Sign in with Apple native dialog. We want to be as close to the iOS version as possible while doing this testing. You can only get the native Sign in with Apple dialog to appear when running either on a real iOS device or via My Mac (Designed for iPad).

I found that the easiest way to get Sign in with Apple working is using an Apple Silicon macOS system and setting the target in XCode to My Mac (Designed for iPad). Set your Unity Project Player settings to Target Device iPhone + iPad and the Target SDK to Device SDK. When you open the project in XCode, you should see Unity-iPhone > Any iOS Device (arm64) at the top. Click on this and change it to My Mac (Designed for iPad) and now you'll get a half-iOS/half-macOS (Catalyst) app. All-in-all this is actually not half bad - it's a lot closer to an iOS app than the macOS build, it's much faster to launch than the simulator, and best of all Sign in with Apple native dialogs will appear. If you are on an Intel Mac I highly recommend updating to at least a basic M1-based macOS machine such as Mac Mini - it'll run a lot faster and make iOS development much easier.

You may get errors related to Bitcode in your project. Make sure the Enable Bitcode is set to No in the Unity-iPhone and UnityFramework targets in XCode Build Settings.

Tip: You might be considering Godot as an alternative to Unity. It looks like there is a Godot plugin to make this work. I haven't explored this further, but in theory there's no reason you couldn't use supabase-csharp and this plugin in a Godot project. If you get this working write up your notes and let me know!

Add Supabase to Unity

  1. Add NuGet for Unity to your Unity project.
  2. Add UniTask to the project via NuGet (this will make async calls work properly)
  3. Add supabase-csharp to the project via NuGet
  4. Set Managed Stripping Level to Minimal in your Unity Player build settings. If you skip this step Unity will likely be overly aggressive about removing unused code and this will break the Newtonsoft JSON deserialization in your builds.

Once you have done this, start out by adding Supabase email/password sign in to your project first. This will allow you to get started working with Supabase and also get more familiar working with async/await functions in Unity. One easy way to verify basic connectivity is via a "Hello World" RPC call in Supabase, but you can get as fancy or simple as you want. The important thing is to verify that you can a) use async/await to call Supabase without locking up the Unity Player loop and b) you can get at least a "Hello World" back from Supabase.

One you are sure you can connect to your Supabase project, you now need to tie together the identifier from the native Sign in with Apple dialog back to your Supabase project.

Fortunately, now that you've gotten this far, this final part is pretty easy - use the SignInWithIdToken(Provider provider, string idToken) method in the Supabase Auth package (supabase-csharp and gotrue-csharp) and you are all set. The provider is just Provider.Apple and the idToken is the identifier that came back from the native Sign in with Apple dialog. If everything is working correctly you'll get a Supabase session back and the user will appear in the Supabase Auth dashboard.

Add a LOT of error-handling

This is absolutely the most basic version of this path - there is a lot that can go wrong, which in turn means there is a lot of UI that you need to build to get it all working properly. The network might be down. The user account might have been suspended by Apple. The user might have lost their phone and therefore lost their account, which means you may want to send an email confirming the user's identity when they set up their account. And on and on.

When it works, it's really easy, especially for end users. They hit the "Sign in with Apple" button, log in via a password or (more likely) via a FaceID verification, and then you just pass the result to Supabase and get a session.

When it doesn't work, it can be tricky. What, exactly, went wrong? Are you calling async functions correctly? Are all of your keys set up right? Did that JWT you set up six months ago and forgot about just expire?

That said, considering that we are attempting to connect three systems together that involve native security dialogs, web services, and a variety of different tokens/etc... not too bad.

Tips:

  1. When getting exceptions, you'll want to make sure to check the specific exception type so you can get the details out. For example, when working with gotrue-csharp I was getting UnauthorizedException when trying to set up access. The critical information I needed (the messages related to nonce configuration) were tucked in the UnauthorizedException.Content, not in the Exception.Message.
  2. Debugging in the native iOS build is a bit of a nightmare. I wound up using a textfield and doing a lot of string appending to figure things out. It's horrible but (eventually) I got it working. There are a bunch of Unity assets out there that will add an in-game console - depending on how things are going, you might want to spring for one of those. For example, something like https://github.com/SpaceMadness/lunar-unity-console would almost certainly have been easier.
  3. I'm not kidding about a single incorrect character messing everything up - take your time, make sure each step is working. If you are lucky and have done this before you might be able to get this working in a few hours. If you are unlucky it could easily take days.

Testing Tip

You may want to try testing to make sure your Supabase project is working correctly via an HTTP client.

If you can get the native Sign in With Apple dialog to get you an identifier, you can test to make sure the Supabase auth service is set up correctly via an ordinary web request with the following configuration:

POST https://fvkoppwyozwgkiowmcrg.supabase.co/auth/v1/token?grant_type=id_token
Accept: application/json
apiKey: <your Supabase anon key>
Authorization: Bearer <your Supabase anon key>

{
   "provider": "apple",
   "id_token": "<the identifier you got from the native Sign in with Apple dialog>"
}

You can use an online JWT tool to verify that a JWT is showing the data you expect. For example, that the nonce you send to Apple is the same value that comes back.

This is the format used by the Rider http client plugin, but it's pretty easy to adapt to cURL or a fancier client if you prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment