From a13f1eaf86aa9a266b404d8ea182e31c0482dfa7 Mon Sep 17 00:00:00 2001 From: Joseph Schultz Date: Thu, 31 Aug 2023 19:10:56 -0500 Subject: [PATCH] Migrate documentation to github wiki --- Documentation/DesktopClients.md | 179 ----------------------- Documentation/Examples.md | 9 -- Documentation/GettingStarted.md | 110 -------------- Documentation/NativeSignInWithApple.md | 156 -------------------- Documentation/OfflineSupport.md | 22 --- Documentation/RefreshTokenThread.md | 16 --- Documentation/ReleaseNotes.md | 49 ------- Documentation/ServerSideApplications.md | 75 ---------- Documentation/SessionPersistence.md | 68 --------- Documentation/ThirdPartyOAuth.md | 77 ---------- Documentation/TroubleShooting.md | 22 --- Documentation/Unity.md | 181 ------------------------ Documentation/UsingTheClient.md | 54 ------- modules/storage-csharp | 2 +- 14 files changed, 1 insertion(+), 1019 deletions(-) delete mode 100644 Documentation/DesktopClients.md delete mode 100644 Documentation/Examples.md delete mode 100644 Documentation/GettingStarted.md delete mode 100644 Documentation/NativeSignInWithApple.md delete mode 100644 Documentation/OfflineSupport.md delete mode 100644 Documentation/RefreshTokenThread.md delete mode 100644 Documentation/ReleaseNotes.md delete mode 100644 Documentation/ServerSideApplications.md delete mode 100644 Documentation/SessionPersistence.md delete mode 100644 Documentation/ThirdPartyOAuth.md delete mode 100644 Documentation/TroubleShooting.md delete mode 100644 Documentation/Unity.md delete mode 100644 Documentation/UsingTheClient.md diff --git a/Documentation/DesktopClients.md b/Documentation/DesktopClients.md deleted file mode 100644 index 18591fd4..00000000 --- a/Documentation/DesktopClients.md +++ /dev/null @@ -1,179 +0,0 @@ -# Desktop Clients - -A desktop client for the purposes of this document is a client that runs on an -end user machine. For example, a Unity, Godot, or MonoGame game, or an application built with -Xamarin, MAUI or Avalonia app. - -There are a LOT of potential desktop clients, and many of them have very specific rules for -things like UI thread access, or how to handle file paths. This document will attempt to -cover the most common scenarios, but it is not exhaustive. - -This library includes the core library for accessing and using Supabase services, as well -utilities and interfaces to make it easier to use the library in common desktop scenarios. -For example, clients often go offline, and then come back online. Different UI frameworks -have very different UI threading models, which affects how you might need to deal with callbacks. -And, of course, because pretty much everything involves async/await calls, you need to be -comfortable with -the [.NET async system](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/async). - -So, while this library is intended to be as easy to use as possible, it does require some -understanding of the underlying .NET system and (depending on your choice of UI framework) -may require some adaption. In most cases, this means implementing an existing interface -with the specific details required by your UI framework. - -Typical areas where you may need to adapt the library include: - -- [Session Persistence](SessionPersistence.md) - basically, where to store the JWT token (usually on disk) - so the user doesn't have to log in again every time they start the app. -- [Network Awareness/Offline Support](OfflineSupport.md) - updating the client to handle - network connectivity changes and to handle offline operations. - -## Brief Note on .NET Versions - -There are many different [versions of .NET](https://versionsof.net/), and the naming can be -somewhat confusing. In addition to the different versions, there's also the .NET Standard -set of specifications. - -This project targets -the [.NET Standard 2.0 specification](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0). -This means that it can be used with a wide variety of different client-side technologies, -which you can see looking at the .NET Standard -2.0 [compatibility matrix](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0#net-implementation-support). - -## Installation - -The [library is available via NuGet](https://www.nuget.org/packages/supabase-csharp). Depending on your -environment, you can install it via the command line: - -``` -dotnet add package supabase-csharp -``` - -Many IDEs also provide a way to install NuGet packages. If you are using Unity you can check out -the [Unity specific instructions](Unity.md). - -### Projects defaulting to `System.Text.Json` (i.e. Blazor WASM) - -You will need to manuall install NewtonsoftJson support: - -```bash -dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson --version 7.0.5 -``` -And include the following in your initialization code: -```c# -builder.Services.AddControllers().AddNewtonsoftJson(); -```` - -## Getting Started - -From a high level, for most desktop or mobile apps you create a Supabase client as long-running -object, configure it, and then use it to access the various services. - -Below is a heavily annotated example based on the Unity, but this can be adapted to other -UI frameworks. - -```csharp -using Supabase; -using Supabase.Gotrue; -using Supabase.Gotrue.Interfaces; -using Client = Supabase.Client; - -private async void Initialize() -{ - // We create an object to monitor the network status. - NetworkStatus NetworkStatus = new(); - - // Create a Supabase objects object. - SupabaseOptions options = new(); - // Set the options to auto-refresh the JWT token with a background thread. - options.AutoRefreshToken = true; - - // Create the client object. Note that the project URL and the anon key are - // available in the Supabase dashboard. In addition, note that the public anon - // key is not a security risk - in JavaScript projects, this key is visible - // in the browser viewing source! - _supabase = new Client("https://project URL", "supabase PUBLIC anon key", options); - - // This adds a listener for debug information, especially useful for dealing - // with errors from the auto-refresh background thread. - _supabase.Auth.AddDebugListener(DebugListener); - - // Here we are getting the auth service and passing it to the network status - // object. The network status object will tell the auth service when the - // network is up or down. - NetworkStatus.Client = (Supabase.Gotrue.Client)_supabase.Auth; - - // Here we are setting up the persistence layer. This is an object that implements - // the IGotrueSessionPersistence interface. This is used to store the JWT - // token so the user won't have to log in every time they start the app. - _supabase.Auth.SetPersistence(new UnitySession()); - - // Here we are setting up the listener for the auth state. This listener will - // be called in response to the auth state changing. This is where you would - // update your UI to reflect the current auth state. - _supabase.Auth.AddStateChangedListener(UnityAuthListener); - - // We now instruct the auth service to load the session from disk using the persistence - // object we created earlier - _supabase.Auth.LoadSession(); - - // In this case, we are setting up the auth service to allow unconfirmed user sessions. - // Depending on your use case, you may want to set this to false and require the user - // to validate their email address before they can log in. - _supabase.Auth.Options.AllowUnconfirmedUserSessions = true; - - // This is a well-known URL that is used to test network connectivity. - // We use this to determine if the network is up or down. - string url = - $"{SupabaseSettings.SupabaseURL}/auth/v1/settings?apikey={SupabaseSettings.SupabaseAnonKey}"; - try - { - // We start the network status object. This will attempt to connect to the - // well-known URL and determine if the network is up or down. - _supabase!.Auth.Online = await NetworkStatus.StartAsync(url); - } - catch (NotSupportedException) - { - // On certain platforms, the NetworkStatus object may not be able to determine - // the network status. In this case, we just assume the network is up. - _supabase!.Auth.Online = true; - } - catch (Exception e) - { - // If there are other kinds of error, we assume the network is down, - // and in this case we send the error to a UI element to display to the user. - // This PostMessage method is specific to this application - you will - // need to adapt this to your own application. - PostMessage(NotificationType.Debug, $"Network Error {e.GetType()}", e); - _supabase!.Auth.Online = false; - } - if (_supabase.Auth.Online) - { - // If the network is up, we initialize the Supabase client. - await _supabase.InitializeAsync(); - - // Here we are fetching the current settings for the auth service as exposed - // by the server. For example, we might want to know which providers have been - // configured, or change the behavior if auto-confirm email is turned off or on. - Settings = await _supabase.Auth.Settings(); - } -} -``` - -## More Examples - -- [Phantom-KNA](https://gist.github.com/Phantom-KNA) - posted this example of [a Xamarin Forms/MAUI](https://gist.github.com/Phantom-KNA/0eabbbe52076370489d0ecbf73f0a6c6) - bootstrap/configuration. -- [kaushalkumar86](https://github.com/kaushalkumar86) posted this -example of how to implement the [Realtime notification listeners](https://github.com/supabase-community/realtime-csharp/issues/34#issuecomment-1696985179) -- [salsa2k](https://github.com/salsa2k) posted this example of working [auth using MAUI Blazor Hybrid](https://github.com/supabase-community/supabase-csharp/discussions/83#discussioncomment-6863545) - -## Next Steps - -- Read the [Supabase documentation](https://supabase.com/docs), watch videos, etc. -- Browse the [API documentation](https://supabase-community.github.io/supabase-csharp/api/Supabase.html) for the - Supabase C# client. -- Check out the Examples and Tests for each of the sub-project libraries. -- Check out the [Supabase C# discussion board](https://github.com/supabase-community/supabase-csharp/discussions) -- Have fun! diff --git a/Documentation/Examples.md b/Documentation/Examples.md deleted file mode 100644 index 840657b4..00000000 --- a/Documentation/Examples.md +++ /dev/null @@ -1,9 +0,0 @@ -# Examples - -- Supabase + Unity + Apple Native Sign-in - [Video](https://www.youtube.com/watch?v=S0hTwtsUWcM) by [@wiverson](https://github.com/wiverson) -- Blazor WASM Template using Supabase - [Repo](/Examples/BlazorWebAssemblySupabaseTemplate) / [Live demo](https://blazorwasmsupabasetemplate.web.app/) by [@rhuanbarros](https://github.com/rhuanbarros) -- Realtime Example using Supabase Realtime Presence - [Repo](https://github.com/supabase-community/realtime-csharp/tree/master/Examples/PresenceExample) / [Live demo](https://multiplayer-csharp.azurewebsites.net/) - -Create a PR to list your work here! - -You can find other examples in the [discussion forums](https://github.com/supabase-community/supabase-csharp/discussions). diff --git a/Documentation/GettingStarted.md b/Documentation/GettingStarted.md deleted file mode 100644 index 34962dec..00000000 --- a/Documentation/GettingStarted.md +++ /dev/null @@ -1,110 +0,0 @@ -# Getting Started - -The Supabase C# library is a wrapper around the various REST APIs provided by Supabase and -the various server components (e.g. GoTrue, Realtime, etc.). - -## Video Options - -If you prefer video format: [@Milan Jovanović](https://www.youtube.com/@MilanJovanovicTech) has created a [video crash course to get started](https://www.youtube.com/watch?v=uviVTDtYeeE)! - -## Getting Oriented - -At the most basic level, Supabase provides services based on the Postgres database and -the supporting ecosystem. You can use the online, hosted version of Supabase, run it locally -via CLI and Docker images, or some other combination (e.g. hosted yourself on another cloud service). - -One option for .NET developers, of course, is to just treat it as any other RDBMS package. You can -create a project, grab the .NET connection string, and use it as you would any other database. - -However, Supabase also provides a number of other services that are useful for building applications -and services. These include: - -- Authentication ([GoTrue](https://github.com/supabase/gotrue)) -- PostgREST ([REST API](https://postgrest.org/en/stable/)) -- [Realtime](https://github.com/supabase/realtime) -- [Storage](https://github.com/supabase/storage-api) -- [Edge Functions](https://github.com/supabase/edge-runtime) - -Authentication is provided by GoTrue, which is a JWT-based authentication service. It provides -a number of features, including email/password authentication, OAuth, password resets, email -verification, and more. In addition, you can use it to handle the native Sign in With Apple and -Sign in With Google authentication systems. - -PostgREST is a REST API that is automatically generated from your database schema. It provides -a number of features, including filtering, sorting, and more. - -Realtime is a service that provides realtime updates to your database. It is based on Postgres -LISTEN/NOTIFY and WebSockets. - -Storage is a service that provides a simple interface for storing files. It is based on Postgres -and provides a number of features, including file versioning, metadata, and more. - -Edge Functions is a service that provides a way to run serverless functions on the edge. - -The Supabase C# library provides a wrapper around the various REST APIs provided by Supabase and -the various server components (e.g. GoTrue, Realtime, etc.). It also provides a number of -convenience methods and classes - for example, utilities to make the native Sign in with Apple -flow easier. - -Care has been taken to mirror, as much as possible, the [Javascript Supabase API](https://github.com/supabase/supabase-js). As this is an unofficial client, there are times where this client lags behind the offical client. **If there are missing features, please open an issue or pull request!** - -## Basic Concepts - -There are two main ways to access your Supabase instance - either via an "untrusted" client -(e.g. Unity or some other mobile/desktop client) or a "trusted" client (e.g. a server-side application). - -The untrusted clients have two key factors - first, you'll likely want to manage the user -state (e.g. login, logout, etc.) and second, you'll be using the anonymous/public API key to -access those services. The user is expected to use some kind of credentials (e.g. email/password, -OAuth, or a JWT from a native sign-in dialog, etc.) to access the services. The Supabase session -(a JWT issued by GoTrue) serves as the authentication token for the various services. - -Tip: If you aren't familiar with JWTs, you can read more about them [here](https://jwt.io/introduction/). -You can also use this site to decode the JWTs issued by GoTrue, which you may find helpful -when learning about Supabase Auth. If you are a traditional server-side dev, you might find -it helpful to think of the JWT as a session cookie, except that it is cryptographically signed -(proving that it was "minted" by GoTrue). You can use standard JWT libraries to decode the -token, access the details, and verify the signature. - -Trusted, server-side code is usually best managed as a stateless system, where each request -is managed independently. In this scenario, you will often want to use the library in conjunction -with the private API key. - -**Remember - the public key is designed to be used in untrusted clients, while the private key -is designed to be used in trusted clients ONLY.** - -## Next Steps - -Given that the configuration is pretty different depending on the scenario, we'll cover each -of the scenarios separately. - -- [Unity](Unity.md) -- [Desktop Clients](DesktopClients.md) -- [Server-Side Applications](ServerSideApplications.md) - -To use this library on the Supabase Hosted service but separately from the `supabase-csharp`, you'll need to specify -your url and public key like so: - -```c# -var auth = new Supabase.Gotrue.Client(new ClientOptions -{ - Url = "https://PROJECT_ID.supabase.co/auth/v1", - Headers = new Dictionary - { - { "apikey", SUPABASE_PUBLIC_KEY } - } -}) -``` - -Otherwise, using it this library with a local instance: - -```c# -var options = new ClientOptions { Url = "https://example.com/api" }; -var client = new Client(options); -var user = await client.SignUp("new-user@example.com"); - -// Alternatively, you can use a StatelessClient and do API interactions that way -var options = new StatelessClientOptions { Url = "https://example.com/api" } -await new StatelessClient().SignUp("new-user@example.com", options); -``` - diff --git a/Documentation/NativeSignInWithApple.md b/Documentation/NativeSignInWithApple.md deleted file mode 100644 index 003997cd..00000000 --- a/Documentation/NativeSignInWithApple.md +++ /dev/null @@ -1,156 +0,0 @@ -# Native Sign In With Apple - -Apple has recently taken more aggressive steps toward requiring developers to -use the native Sign in with Apple flow on submitted applications. In particular, -while you can use an OAuth flow for Sign in with Apple on web platforms, many -developers have reported issues with Apple rejecting applications that use -this flow on iOS. - -To support Sign in with Apple, first you need to configure the flow with Apple -and Supabase. You can find instructions for this in the [Supabase documentation on -Apple Sign in](https://supabase.com/docs/guides/auth/social-login/auth-apple). -Be sure to follow the steps _very_ carefully, as a single typo can cause the -whole thing to not work. - -Tip: It may seem confusing to have to configure the flow for native Sign in with Apple -"twice" - once for the OAuth web flow and once for native mobile/desktop flow. -This is because the native flow gets tokens back from Apple, and then uses the -web flow to complete the validation process. - -Once all of the server configuration is done, you just need to configure a -few options for the native dialog, call it to present it to the user, and then -handle the callback. Once you have the information from the dialog (a JWT -from Apple), you then send that JWT to Supabase to convert it to a Supabase session. - -Tip: You have to make sure everything is all configured correction - the -application bundle ID, the nonce, etc. If you get any of these wrong, the -dialog will not work and the error message may or may not be very helpful. -You may want to include some kind of debug logging to help you figure out -what's going on in a way that you can monitor even in a non-debug build. -This might be as simple as writing to a user-accessible file or perhaps -involving presenting a debug console in the UI in some fasion. - -## Native Sign in with Apple via Unity - -You can use native Sign in with Apple functionality by [installing a plugin](https://github.com/lupidan/apple-signin-unity). - -Depending on the platform you are using, you may need to write a bit of Swift code to expose -the native dialog, or you may be able to find an existing library. For example, here is -information on using native [Sign in with Apple on Xamarin](https://learn.microsoft.com/en-us/xamarin/ios/platform/ios13/sign-in). - -Here is a snippet of pseudo-C# code to show how to use the native dialog: - -```csharp - -// These are values that we will use to handle the flow. - -// This is a one-time use code. It's used to verify the JWT from Apple. -private string? _nonce; - -// This is a hash of the one-time use code. -private string? _nonceVerify; - -// This is the identity token from Apple. -private string? _identityToken; - -// This is the authorization code from Apple. -private string? _authorizationCode; - -// This method brings up the native dialog. -public void SignInWithApple() -{ - // This generates a random nonce (a one-time code) used to verify the JWT from - // Apple. - _nonce = Supabase.Gotrue.Helpers.GenerateNonce(); - - // This is a SHA256 hash of the nonce. - _nonceVerify = Supabase.Gotrue.Helpers.GenerateSHA256NonceFromRawNonce(_nonce); - - // Here we set the options for the native dialog, including the nonce. - AppleAuthLoginArgs loginArgs = - new(LoginOptions.IncludeEmail | LoginOptions.IncludeFullName, _nonceVerify); - - // Here we are asking the native dialog wrapper library to present the native - // dialog. We are passing in the nonce and a callback to handle the results. - _appleAuthManager!.LoginWithAppleId(loginArgs, SuccessCallback, ErrorCallback); -} - -// This method handles the callback from the native dialog and sets the data -// to be used later. -private void SuccessCallback(ICredential credential) -{ - // Obtained credential, cast it to IAppleIDCredential - if (credential is IAppleIDCredential appleIdCredential) - { - // Apple User ID - string? userId = appleIdCredential.User; - - // Email and first name are received ONLY in the first login - // You may want to add logic to save these fields locally - - // Identity token - _identityToken = Encoding.UTF8.GetString(appleIdCredential.IdentityToken, 0, - appleIdCredential.IdentityToken.Length); - - // Authorization code - _authorizationCode = Encoding.UTF8.GetString(appleIdCredential.AuthorizationCode, 0, - appleIdCredential.AuthorizationCode.Length); - - // And now you have all the information to create/login a user in your system - _doAppleSignIn = true; - } - else - { - // Error handling - you don't want to use Sign in with Apple on non Apple devices - } -} - -// Now we use an Update() declared async. -private async void Update() -{ - // Updates the AppleAuthManager instance to execute - // pending callbacks inside Unity's execution loop - _appleAuthManager?.Update(); - - if (_doAppleSignIn) - { - _doAppleSignIn = false; - // ReSharper disable once Unity.PerformanceCriticalCodeInvocation - bool status = await SignInToSupabaseWithApple(); - if (!status) - Debug.Log("Sign in failed", gameObject); - _doAppleSignIn = false; - } -} - -// This method takes the data from the callback and sends it to Supabase to -// validate the JWT and create a Supabase session. Note that because this method -// goes over the network, it's declared async. In Unity this means you might want -// to call this method from an Update method marked async. -private async Task SignInToSupabaseWithApple() -{ - try - { - Session? session = await SupabaseManager.Client()?.Auth.SignInWithIdToken(Constants.Provider.Apple, _identityToken!, _nonce)!; - if (session?.User?.Id != null) - { - // You logged in successfully! Depending on your application you may want to load another scene, etc. - } - else - { - // Something went wrong - you may want to display an error message to the user. - } - } - catch (GotrueException e) - { - // Something went wrong - you may want to display an error message to the user. GotrueExceptions - // generally mean the server sent back an error message. - } - catch (Exception e) - { - // Catch-all for anything else that might have gone wrong. - } - return true; -} - -``` \ No newline at end of file diff --git a/Documentation/OfflineSupport.md b/Documentation/OfflineSupport.md deleted file mode 100644 index 1a3802fd..00000000 --- a/Documentation/OfflineSupport.md +++ /dev/null @@ -1,22 +0,0 @@ -# Offline Support - -The Supabase Auth client supports online/offline usage. The Client now has a simple boolean option "Online" -which can be set to to false. This can be combined with the NetworkStatus class to allow the client -to automatically go online & offline based on the device's network status. - -To use this new NetworkStatus, add the following: - -```csharp -// Create the client -var client = new Client(new ClientOptions { AllowUnconfirmedUserSessions = true }); -// Create the network status monitor -var status = new NetworkStatus(); -// Tell the network status monitor to update the client's online status -status.Client = client; -// Start the network status monitor -await status.StartAsync(); -// rest of the usual client configuration -``` - -Only the stateful Client supports this feature, and only for the managed user sessions. -Admin JWT methods and the stateless client are not affected. diff --git a/Documentation/RefreshTokenThread.md b/Documentation/RefreshTokenThread.md deleted file mode 100644 index e1c98336..00000000 --- a/Documentation/RefreshTokenThread.md +++ /dev/null @@ -1,16 +0,0 @@ -# Updated Refresh Token Handling - -The Supabase client supports setting a maximum wait time before refreshing the token. This is useful -for scenarios where you want to refresh the token before it expires, but not too often. - -By default, GoTrue servers are typically set to expire the token after an hour, and the refresh -thread will refresh the token when ~20% of that time is left. - -However, you can set the expiration time to be much longer on the server (up to a week). In this -scenario, you may want to refresh the token more often than once every 5 days or so, but not every hour. - -There is now a new option `MaximumRefreshWaitTime` which allows you to specify the maximum amount -in time that the refresh thread will wait before refreshing the token. This defaults to 4 hours. -This means that if you have your server set to a one hour token expiration, nothing changes, but -if you extend the server refresh to (for example) a week, as long as the user launches the app -at least once a week, they will never have to re-authenticate. diff --git a/Documentation/ReleaseNotes.md b/Documentation/ReleaseNotes.md deleted file mode 100644 index 3d0467fa..00000000 --- a/Documentation/ReleaseNotes.md +++ /dev/null @@ -1,49 +0,0 @@ - -# Release Notes & Breaking Changes - -The Supabase C# library uses [semantic versioning](https://semver.org/). This is a pretty standard -versioning scheme - if you see a X.Y.Z version number, expect major breaks for a X version bump, -minor changes for a Y, and bug fixes for Z. - -## BREAKING CHANGES v3.1 → v4.x - -- Exceptions have been simplified to a single `GotrueException`. A `Reason` field has been added - to `GotrueException` to clarify what happened. This should also be easier to manage as the Gotrue - server API & messages evolve. -- The session delegates for `Save`/`Load`/`Destroy` have been simplified to no longer require `async`. -- Console logging in a few places (most notable the background refresh thread) has been removed - in favor of a notification method. See `Client.AddDebugListener()` and the test cases for examples. - This will allow you to implement your own logging strategy (write to temp file, console, user visible - err console, etc). -- The client now more reliably emits AuthState changes. -- There is now a single source of truth for headers in the stateful Client - the `Options` headers. - -New feature: - -- Added a `Settings` request to the stateless API only - you can now query the server instance to - determine if it's got the settings you need. This might allow for things like a visual - component in a tool to verify the GoTrue settings are working correctly, or tests that run differently - depending on the server configuration. - -Implementation notes: - -- Test cases have been added to help ensure reliability of auth state change notifications - and persistence. -- Persistence is now managed via the same notifications as auth state change - -## BREAKING CHANGES v3.0 → 3.1 - -- We've implemented the PKCE auth flow. SignIn using a provider now returns an instance of `ProviderAuthState` rather - than a `string`. -- The provider sign in signature has moved `scopes` into `SignInOptions` - -In Short: - -```c# -# What was: -var url = await client.SignIn(Provider.Github, "scopes and things"); - -# Becomes: -var state = await client.SignIn(Provider.Github, new SignInOptions { "scopes and things" }); -// Url is now at `state.Uri` -``` diff --git a/Documentation/ServerSideApplications.md b/Documentation/ServerSideApplications.md deleted file mode 100644 index 8982433a..00000000 --- a/Documentation/ServerSideApplications.md +++ /dev/null @@ -1,75 +0,0 @@ -# Supabase And .NET Server-Side Applications - -At the most basic level, Supabase is a Postgres database. As such, you can create a project, grab the .NET connection -string, -and use it as you would any other database. - -However, if you use the Supabase Hosted service, you'll also have access to a number of other services that are useful -for -building applications and services. - -For many applications, you may want to start by looking at the standard Supabase JavaScript client -library. For many flows, you may just want to use that library in the client. For example, you might -want to rely on the JavaScript client for the bulk of the user authentication flow, and then pass along -the signed-in user JWT to your server-side application via cookie, and then use that JWT to perform -a few key C# REST operations. - -You may want to think carefully about how you want to manage connectivity. For example, while you -_could_ route all of the Realtime traffic from a client side app to your .NET server, and then route -that back again to the client, it's probably better to just use the Realtime client directly from -the client-side app. - -## Nomenclature Warning - -The Supabase stateless library technically does store state in an instance - it stores the JWT token and the -refresh token. - -In practice, this means that you should not reuse the same stateless client across multiple -requests. Instead, you should create a new client for each request. While this does make things a bit -more confusing (as well as put a tiny bit more load on the garbage collector), this is closer to the -way the base Supabase JS client initially worked. At some point in the future, we may add a true stateless -client that does not store any state. - -## Installation - -The [library is available via NuGet](https://www.nuget.org/packages/supabase-csharp). Depending on your -environment, you can install it via the command line: - -``` -dotnet add package supabase-csharp -``` - -Many IDEs also provide a way to install NuGet packages. - -## Getting Started - -You can use the base Supabase client to access the individual services, or you can just grab the -individual client that you need. - -For example, here is a way to use the GoTrue stateless client directly. This fragment of code -can seen in more detail in -the [corresponding test case](https://github.com/supabase-community/gotrue-csharp/blob/master/GotrueTests/StatelessClientTests.cs). - -```csharp -using static Supabase.Gotrue.StatelessClient; - -IGotrueStatelessClient client = new StatelessClient(); - -var user = $"{RandomString(12)}@supabase.io"; -var serviceRoleKey = GenerateServiceRoleToken(); -var result = await _client.InviteUserByEmail(user, serviceRoleKey, Options); -``` - -The main thing to note is the serviceRoleKey, which is the key available -in the Supabase Admin UI labeled `service_role`. This key is an admin key - -it can be used to perform any operation on the service. As such, you should -treat it like a password and keep it safe. - -## Next Steps - -- Read the [Supabase documentation](https://supabase.com/docs), watch videos, etc. -- Browse the [API documentation](https://supabase-community.github.io/supabase-csharp/api/Supabase.html) for the - Supabase C# client. -- Check out the Examples and Tests for each of the sub-project libraries. -- Check out the [Supabase C# discussion board](https://github.com/supabase-community/supabase-csharp/discussions) -- Have fun! diff --git a/Documentation/SessionPersistence.md b/Documentation/SessionPersistence.md deleted file mode 100644 index feee07c7..00000000 --- a/Documentation/SessionPersistence.md +++ /dev/null @@ -1,68 +0,0 @@ -# Session Persistence - -## Persisting, Retrieving, and Destroying Sessions. - -This Gotrue client is written to be agnostic when it comes to session persistence, retrieval, and -destruction. `ClientOptions` exposes properties that allow these to be specified. - -In the event these are specified and the `AutoRefreshToken` option is set, as the `Client` Initializes, it will also -attempt to retrieve, set, and refresh an existing session. - -# Xamarin Example - -Using `Xamarin.Essentials` in `Xamarin.Forms`, this might look like: - -```c# -// This is a method you add your application launch/setup -async void Initialize() { - - // Specify the methods you'd like to use as persistence callbacks - var persistence = new GotrueSessionPersistence(SaveSession, LoadSession, DestroySession); - var client = new Client( - Url = GOTRUE_URL, - new ClientOptions { - AllowUnconfirmedUserSessions = true, - SessionPersistence = persistence }); - - // Specify a debug callback to listen to problems with the background token refresh thread - client.AddDebugListener(LogDebug); - - // Specify a call back to listen to changes in the user state (logged in, out, etc) - client.AddStateChangedListener(AuthStateListener); - - // Load the session from persistence - client.LoadSession(); - // Loads the session using SessionRetriever and sets state internally. - await client.RetrieveSessionAsync(); -} - -// Add callback methods for above -// Here's a quick example of using this to save session data to the user's cache folder -// You'll want to add methods for loading the file and deleting when the user logs out -internal bool SaveSession(Session session) -{ - var cacheFileName = ".gotrue.cache"; - - try - { - var cacheDir = FileSystem.CacheDirectory; - var path = Path.Join(cacheDir, cacheFileName); - var str = JsonConvert.SerializeObject(session); - - using (StreamWriter file = new StreamWriter(path)) - { - file.Write(str); - file.Dispose(); - return Task.FromResult(true); - }; - } - catch (Exception err) - { - Debug.WriteLine("Unable to write cache file."); - throw err; - } -} -``` - -You can find other sample implementations in the [Unity](Unity.md) -and [Desktop Clients](DesktopClients.md) documentation. diff --git a/Documentation/ThirdPartyOAuth.md b/Documentation/ThirdPartyOAuth.md deleted file mode 100644 index ad9ffb14..00000000 --- a/Documentation/ThirdPartyOAuth.md +++ /dev/null @@ -1,77 +0,0 @@ -# 3rd Party OAuth - -OAuth is a standard for allowing users to sign in to your application using an account -from another provider. This is great, because it allows Supabase to support -a wide range of social media providers. Unfortunately, most documentation for -OAuth involves a purely web-based flow, which doesn't work well for native -applications. - -The workaround for this challenge is to use some kind of deep-linking mechanism. -All of the major OS providers include a mechanism for allowing an native application -to declare that it can handle a particular URL scheme. In this flow, the application -will open a web browser to the OAuth provider's web site, and the web site will -redirect back to the application using the URL scheme. The application can then -handle the callback and complete the OAuth flow. - -You'll have to check the documentation for your particular OAuth provider, your -target framework[s], and your target platform[s] to figure out how to do set up this -entire flow. Superficially, the flow is: - -1. The user clicks a social login button -2. The application opens a web browser to the OAuth provider's web site with a - callback URL that uses the application's URL scheme -3. The user logs in to the OAuth provider -4. The OAuth provider redirects back to the application using the callback URL -5. The application handles the callback and sends the details back to Supabase. -6. Supabase converts the OAuth token to a Supabase session. -7. The application uses the Supabase session to authenticate the user. - -Unfortunately, the details of this flow are different for every OAuth provider, -every target framework, and every target platform. You'll have to do some research -to figure out how to do this for your particular application. - -Below is an example of how to do this for Google Sign In, but the details will -be different for every provider. - -## Supabase OAuth - -Once again, Gotrue client is written to be agnostic of platform. In order for Gotrue to sign in a user from an Oauth -callback, the PKCE flow is preferred: - -1) The Callback Url must be set in the Supabase Admin panel -2) The Application should have listener to receive that Callback -3) Generate a sign in request using: `client.SignIn(PROVIDER, options)` and setting the options to use the - PKCE `FlowType` -4) Store `ProviderAuthState.PKCEVerifier` so that the application callback can use it to verify the returned code -5) In the Callback, use stored `PKCEVerifier` and received `code` to exchange for a session. - -```c# -var state = await client.SignIn(Constants.Provider.Github, new SignInOptions -{ - FlowType = Constants.OAuthFlowType.PKCE, - RedirectTo = "http://localhost:3000/oauth/callback" -}); - -// In callback received from Supabase returning to RedirectTo (set above) -// Url is set as: http://REDIRECT_TO_URL?code=CODE -var session = await client.ExchangeCodeForSession(state.PKCEVerifier, RETRIEVE_CODE_FROM_GET_PARAMS); -``` - -## Additional Example - -Note: Make sure Project Settings -> Auth -> Auth settings -> User Signups is turned ON. - -After your implementation with [GoogleSignInClient](https://github.com/googlesamples/google-signin-unity) or another, -use the IdToken like this: - -```csharp -var identityToken = Encoding.UTF8.GetString(System.Text.Encoding.UTF8.GetBytes(GoogleIdToken), 0, GoogleIdToken.Length); -``` - -And finally use SignInWithIdToken for Login or Create New User: - -```csharp -var user = await Supabase.Auth.SignInWithIdToken(Supabase.Gotrue.Constants.Provider.Google, identityToken); -``` - -Thanks to [Phantom-KNA](https://gist.github.com/Phantom-KNA) for this example. \ No newline at end of file diff --git a/Documentation/TroubleShooting.md b/Documentation/TroubleShooting.md deleted file mode 100644 index 184d078e..00000000 --- a/Documentation/TroubleShooting.md +++ /dev/null @@ -1,22 +0,0 @@ - -# Troubleshooting - -**Q: I've created a User but while attempting to log in it throws an exception:** - -A: Provided the credentials are correct, make sure that the User has also confirmed their email. - -Adding a handler for email confirmation to a desktop or mobile application can be done, but it -requires setting up URL handlers for each platform, which can be pretty difficult to do if you -aren't really comfortable with configuring these handlers. ( -e.g. [Windows](https://learn.microsoft.com/en-us/windows/win32/search/-search-3x-wds-ph-install-registration), -[Apple](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app), -[Android](https://developer.android.com/training/app-links)) -You may find it easier to create a -simple web application to handle email confirmation - that way a user can just click a link in -their email and get confirmed that way. Your desktop or mobile app should inspect the user object -that comes back and use that to see if the user is confirmed. - -You might find it easiest to do something like create and deploy a -simple [SvelteKit](https://kit.svelte.dev/) or even a very basic -pure [JavaScript](https://github.com/supabase/examples-archive/tree/main/supabase-js-v1/auth/javascript-auth) project -to handle email verification. diff --git a/Documentation/Unity.md b/Documentation/Unity.md deleted file mode 100644 index 338a8505..00000000 --- a/Documentation/Unity.md +++ /dev/null @@ -1,181 +0,0 @@ -# Unity Support - -The Client works with Unity. You can find an example of a session persistence -implementation for Unity at this [gist](https://gist.github.com/wiverson/fbb07498743dff19b72c9c58599931e9). - -## Key Project Setup Details - -UniTask is included in the project to help with async/await support. You don't want your game UI to lock up waiting for -network requests, so you'll have to get into the weeds on async/await. - -Install the Unity-specific version of the NewtonSoft JSON libraries. This version is specifically designed to work with -Unity. - -[Managed stripping is set to off/minimal](https://docs.unity3d.com/Manual/ManagedCodeStripping.html). This is mainly to -avoid issues around code stripping removing constructors for JSON-related operations and reflection. Some users have -reported setting custom ProGuard configurations also works. - -Depending on your project, you may want to add Proguard rules to your project. -See [more info here](https://github.com/supabase-community/supabase-csharp/issues/87). - -## Update and Dirty Flags - -Often you may want to perform the following steps: - -- User clicks on some UI element, which kicks off an async request -- The async request succeeds or fails -- Now you want to update the UI based on those results. - -Unfortunately, Unity does not allow for async methods to be called by the UI builder. `Update`, however, can be declared -as async. So, to solve this we have to perform the following steps: - -- When the user clicks on a UI element, set a flag/data indicating that this has happened. -- In the Update loop, check to see if this data flag has been set. If so, call the async method. Send the resulting - information back to the calling class, either by setting the data to a field/property or via a callback. -- In the Update loop, check to see if the result data is set. If so, update the UI as appropriate. - -## Notifications and Debug Info - -You'll want to have some mechanism for sending notifications to the user when events occur. For example, when the -application boots up you'll likely want to refresh the user's session. That's an async request, so you'll want to have a -mechanism for posting the notification back to both the user and/or the application itself. - -## Session Persistence - -Note that for a variety of reasons you should only use local device preferences (e.g. screen resolution) with -PlayerPrefs. PlayerPrefs has a number of limitations, including only supporting access via the user thread. - -With Supabase, you want the option to store the user session data to the filesystem - for example, if the background -thread refreshes the user session - without affecting the UI. Fortunately, we can use the standard .NET APIs for file -system access. This also has the side-effect of reducing the surface area of the Unity Engine for automated testing. - -A session is a JWT and is roughly analogous to a cookie. If you am want to increase the security of the session storage -see suggestions under Save Password below. - -## Network Status and Caching Data - -You'll want to take the Supabase client offline when the user doesn't have a network connection to avoid accidentally -signing the user out. In addition you may want to limit the operations a user can perform when the network goes -online/offline. - -## Unit Testing - -Testing your Supabase integration is much, much easier if you can develop test cases that run in Unity and/or NUnit. -Unfortunately, as of this writing async test cases seem to only work with prerelease versions of the Unity Test -Framework. - -You'll want to install version X via the Package Manager. Select Add By Name... and use this version. You can now -declare individual test cases with async declarations and they will work. There is a gotcha however - as of this writing -the Setup and Teardown methods cannot be declared as async and will fail. - -You are encouraged to voice your support for a full 2.0 release of the Unity Test framework with full async support on -the forum. - -# Implementing Save Password - -Implementing save password functionality can be a nice way to streamline the user experience. However, this can present -a security risk if not implemented correctly. This is complicated by the lack of portable system level secure storage. - -At a minimum, you s should look at a strategy that includes: - -- Encrypt the user password on disk using a well known two way encryption algorithm. -- Use a randomly generated key for the encryption. Include an app-specific salt in the key. -- Store the randomly generated key and the encrypted password in different locations (eg Player Prefs and the - application data directory). - -In this scenario, a hostile actor would have to have access to the key, the salt, and the stored encrypted password. -This level of access probably means the device is completely compromised (eg on the level of a key logger or network -root certificate), which is usually out of scope for most applications. - -# Complex Local Cache - -If you would like to add more comprehensive support for local SQL storage of cached data, check out SQLite. There are a -variety of different options for setting up SQLite on Unity depending on your target platforms. - -Implementing a local sync storage solution is outside the scope of this document. You may want to post ideas, questions, -and strategies to the forum. - -# Unity Setup Step By Step - -1. Install the NuGet CLI. Unity doesn't natively support NuGet, so you'll need to install the CLI and then install the - package manually. - -On macOS, the easiest way to do this is with [Homebrew](https://brew.sh/). - -``` -brew install nuget -``` - -On Windows, you can install the CLI with [Chocolatey](https://chocolatey.org/). - -``` -choco install nuget.commandline -``` - -2. Install the Supabase C# library using NuGet. On macOS this command looks like: - -``` -nuget install supabase-csharp -OutputDirectory ./Assets/Supabase -Framework netstandard2.0 -``` - -You can add a version flag if you want to grab a specific version (e.g. `-Version 0.13.1`). - -3. Delete conflicting/unneeded libraries. - -Here are the core Supabase libraries: - -- supabase-storage-csharp.1.4.0 -- supabase-csharp.0.13.1 -- supabase-core.0.0.3 -- realtime-csharp.6.0.4 -- postgrest-csharp.3.2.5 -- gotrue-csharp.4.2.1 -- functions-csharp.1.3.1 - -Here are the only required supporting libraries as of this writing: - -- MimeMapping.2.0.0 -- System.Reactive.5.0.0 -- System.Threading.4.3.0 -- System.Threading.Channels.5.0.0 -- System.Threading.Tasks.4.3.0 -- System.Threading.Tasks.Extensions.4.5.4 -- Websocket.Client.4.6.1 -- JWTDecoder.0.9.2 - -The rest of the libraries (mostly various System libraries) are already included in Unity -or are otherwise not required. - -4. Install the Unity-specific version of the NewtonSoft JSON libraries. This version is specifically designed to work - with Unity. - -Open the Package Manager in Unity and press the + (plus) button in the upper-left corner of the window. - -Choose the Add package by name option and enter `com.unity.nuget.newtonsoft-json`. Press -enter. You should see the `Newtonsoft Json` package appear (v3.2.1 as of this writing). Click on the package and then -click the Download/Install buttons as usual. - -5. Install UniTask. UniTask is a library that provides async/await support for Unity. You can install it via the - Package Manager. Open the Package Manager in Unity and press the + (plus) button in the upper-left corner of the - window. - -Choose the Add package by git URL... option and -enter `https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask`. -Press enter. You should see the UniTask package (v2.3.3 as o this writing). Download/install as usual. - -6. (Optional) Install a preview version of the Unity test framework that supports running async tests. - -This time, open up the Package Manager and select Add Package by name... Enter `com.unity.test-framework` for the -name of the package and `2.0.1-exp.2` for the version. This version was released on November 14, 2022. Unity may -prompt you to update to the 2.0.1-pre.18 version, but unfortunately this version was released on January 24, 2022 -and is a downgrade. - -7. (Optional) Install the native Sign in with Apple package. - -This is only necessary if you plan to support native Sign in with Apple. You can find instructions for installation -[here](https://github.com/lupidan/apple-signin-unity). - -8. You should be able to start working with Supabase! - -For next steps, check out the [Desktop Clients](DesktopClients.md) documentation to see -an example of setting up a Supabase manager. diff --git a/Documentation/UsingTheClient.md b/Documentation/UsingTheClient.md deleted file mode 100644 index 7bf8dee1..00000000 --- a/Documentation/UsingTheClient.md +++ /dev/null @@ -1,54 +0,0 @@ -### Using the Client - -As for actually using the client, each service is listed as a property on `Supabase.Client`. Some services have helpers to make interactions easier. Properties are provided for every client in the event that advanced customization of the client is needed. - -1. `Supabase.Postgrest` - - Is better accessed using `supabase.From()` as it provides a wrapper class with some helpful accessors (see below) -2. `Supabase.Realtime` - - If used for listening to `postgres_changes` can be accessed using: `supabase.From().On(listenerType, (sender, response) => {})` - - Otherwise, use `Supabase.Realtime.Channel("channel_name")` for `Broadcast` and `Presence` listeners. - -```csharp -// Get the Auth Client -var auth = supabase.Auth; - -// Get the Postgrest Client for a Model -var table = supabase.From(); - -// Invoke an RPC Call -await supabase.Rpc("hello_world", null); - -// Invoke a Supabase Function -await supabase.Functions.Invoke("custom_function"); - -// Get the Storage Client -var storageBucket = supabase.Storage.From("bucket_name"); - -// Use syntax for broadcast, presence, and postgres_changes -var realtime = supabase.Realtime.Channel("room_1"); - -// Alternatively, shortcut syntax for postgres_changes -await supabase.From().On(ListenType.All, (sender, response) => -{ - switch (response.Event) - { - case Constants.EventType.Insert: - break; - case Constants.EventType.Update: - break; - case Constants.EventType.Delete: - break; - } - - Debug.WriteLine($"[{response.Event}]:{response.Topic}:{response.Payload.Data}"); -}); -``` - -# More Tips - -- Take time to review your options. Which can be found in `Supabase.SupabaseOptions`. -- Be aware that many of the supabase features require permissions for proper access from a client. This is **especially true** for `realtime`, `postgres`, and `storage`. If you are having problems getting the client to pull data, **verify that you have proper permissions for the logged in user.** -- Connection to `Supabase.Realtime` is, by default, not enabled automatically, this can be changed in options. -- When logging in using the `Supabase.Auth` (Gotrue) client, state is managed internally. The currently logged in user's token will be passed to all the Supabase features automatically (via header injection). -- Token refresh enabled by default and is handled by a timer on the Gotrue client. -- Client libraries [listed above](#status) have additional information in their readme files. diff --git a/modules/storage-csharp b/modules/storage-csharp index 46187468..85cd638f 160000 --- a/modules/storage-csharp +++ b/modules/storage-csharp @@ -1 +1 @@ -Subproject commit 46187468e08345a25963aabfa973333c0363ef21 +Subproject commit 85cd638f3480d7116abc5dc218701143d29e06f4