296 lines
11 KiB
Markdown
296 lines
11 KiB
Markdown
<p align="center">
|
|
<img width="300" src=".github/supabase-gotrue.png"/>
|
|
</p>
|
|
|
|
<p align="center">
|
|
<img src="https://github.com/supabase/gotrue-csharp/workflows/Build%20And%20Test/badge.svg"/>
|
|
<a href="https://www.nuget.org/packages/gotrue-csharp/">
|
|
<img src="https://img.shields.io/badge/dynamic/json?color=green&label=Nuget%20Release&query=data[0].version&url=https%3A%2F%2Fazuresearch-usnc.nuget.org%2Fquery%3Fq%3Dpackageid%3Agotrue-csharp"/>
|
|
</a>
|
|
</p>
|
|
|
|
---
|
|
|
|
## New Features
|
|
|
|
### 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).
|
|
|
|
```csharp
|
|
|
|
```
|
|
|
|
### Offline Support
|
|
|
|
The Client now better 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.
|
|
|
|
By default, this change will not affect existing code.
|
|
|
|
### Updated Refresh Token Handling
|
|
|
|
The Client now 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.
|
|
|
|
## 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`
|
|
```
|
|
|
|
---
|
|
|
|
## Getting Started
|
|
|
|
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<Session>
|
|
{
|
|
Url = "https://PROJECT_ID.supabase.co/auth/v1",
|
|
Headers = new Dictionary<string, string>
|
|
{
|
|
{ "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);
|
|
```
|
|
|
|
## 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.
|
|
|
|
For 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;
|
|
}
|
|
}
|
|
```
|
|
|
|
## 3rd Party 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);
|
|
```
|
|
|
|
## 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.
|
|
|
|
## Status
|
|
|
|
- [x] API
|
|
- [x] Sign Up with Email
|
|
- [x] Sign In with Email
|
|
- [x] Send Magic Link Email
|
|
- [x] Invite User by Email
|
|
- [x] Reset Password for Email
|
|
- [x] Signout
|
|
- [x] Get Url for Provider
|
|
- [x] Get User
|
|
- [x] Update User
|
|
- [x] Refresh Access Token
|
|
- [x] List Users (includes filtering, sorting, pagination)
|
|
- [x] Get User by Id
|
|
- [x] Create User
|
|
- [x] Update User by Id
|
|
- [x] Client
|
|
- [x] Get User
|
|
- [x] Refresh Session
|
|
- [x] Auth State Change Handler
|
|
- [x] Provider Sign In (Provides URL)
|
|
- [x] Provide Interfaces for Custom Token Persistence Functionality
|
|
- [x] Documentation
|
|
- [x] Unit Tests
|
|
- [x] Nuget Release
|
|
|
|
## Package made possible through the efforts of:
|
|
|
|
Join the ranks! See a problem? Help fix it!
|
|
|
|
<a href="https://github.com/supabase-community/gotrue-csharp/graphs/contributors">
|
|
<img src="https://contrib.rocks/image?repo=supabase-community/gotrue-csharp" />
|
|
</a>
|
|
|
|
<small>Made with [contrib.rocks](https://contrib.rocks).</small>
|
|
|
|
## Contributing
|
|
|
|
We are more than happy to have contributions! Please submit a PR.
|
|
|
|
### Testing
|
|
|
|
To run the tests locally you must have docker and docker-compose installed. Then in the root of the repository run:
|
|
|
|
- `docker-compose up -d`
|
|
- `dotnet test`
|