{"id":13812248,"url":"https://github.com/supabase-community/gotrue-csharp","last_synced_at":"2025-07-24T03:03:13.611Z","repository":{"id":38744107,"uuid":"334638542","full_name":"supabase-community/gotrue-csharp","owner":"supabase-community","description":"C# implementation of Supabase's GoTrue","archived":false,"fork":false,"pushed_at":"2024-07-26T16:38:38.000Z","size":19333,"stargazers_count":46,"open_issues_count":9,"forks_count":30,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-07-02T21:33:45.598Z","etag":null,"topics":["gotrue","netstandard20","supabase","xamarin"],"latest_commit_sha":null,"homepage":"https://supabase-community.github.io/gotrue-csharp/api/Supabase.Gotrue.Client.html","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/supabase-community.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-31T11:27:09.000Z","updated_at":"2025-05-01T10:29:52.000Z","dependencies_parsed_at":"2024-01-03T00:39:31.478Z","dependency_job_id":"d1255ae4-25cf-4fe0-907c-6c82bc8d98dd","html_url":"https://github.com/supabase-community/gotrue-csharp","commit_stats":{"total_commits":124,"total_committers":10,"mean_commits":12.4,"dds":0.2661290322580645,"last_synced_commit":"865e5ba4e3b9d2e05e62a349bb9b2e48c76fd5d7"},"previous_names":["supabase/gotrue-csharp"],"tags_count":48,"template":false,"template_full_name":null,"purl":"pkg:github/supabase-community/gotrue-csharp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase-community%2Fgotrue-csharp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase-community%2Fgotrue-csharp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase-community%2Fgotrue-csharp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase-community%2Fgotrue-csharp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/supabase-community","download_url":"https://codeload.github.com/supabase-community/gotrue-csharp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase-community%2Fgotrue-csharp/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264880819,"owners_count":23677917,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gotrue","netstandard20","supabase","xamarin"],"created_at":"2024-08-04T04:00:49.854Z","updated_at":"2025-07-24T03:03:13.558Z","avatar_url":"https://github.com/supabase-community.png","language":"C#","funding_links":[],"categories":["GoTrue"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg width=\"300\" src=\".github/supabase-gotrue.png\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/supabase/gotrue-csharp/workflows/Build%20And%20Test/badge.svg\"/\u003e\n  \u003ca href=\"https://www.nuget.org/packages/Supabase.Gotrue/\"\u003e\n    \u003cimg src=\"https://img.shields.io/nuget/vpre/Supabase.Gotrue\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## [Notice]: v5.0.0 renames this package from `gotrue-csharp` to `Supabase.Gotrue`. The depreciation notice has been set in NuGet. The API remains the same.\n\n## New Features\n\n### Unity Support\n\nThe Client works with Unity. You can find an example of a session persistence\nimplementation for Unity at this [gist](https://gist.github.com/wiverson/fbb07498743dff19b72c9c58599931e9).\n\n```csharp\n\n```\n\n### Offline Support\n\nThe Client now better supports online/offline usage. The Client now has a simple boolean option \"Online\"\nwhich can be set to to false. This can be combined with the NetworkStatus class to allow the client\nto automatically go online \u0026 offline based on the device's network status.\n\nTo use this new NetworkStatus, add the following:\n\n```csharp\n// Create the client\nvar client = new Client(new ClientOptions { AllowUnconfirmedUserSessions = true });\n// Create the network status monitor\nvar status = new NetworkStatus();\n// Tell the network status monitor to update the client's online status\nstatus.Client = client;\n// Start the network status monitor\nawait status.StartAsync();\n// rest of the usual client configuration\n```\n\nOnly the stateful Client supports this feature, and only for the managed user sessions.\nAdmin JWT methods and the stateless client are not affected.\n\nBy default, this change will not affect existing code.\n\n### Updated Refresh Token Handling\n\nThe Client now supports setting a maximum wait time before refreshing the token. This is useful\nfor scenarios where you want to refresh the token before it expires, but not too often.\n\nBy default, GoTrue servers are typically set to expire the token after an hour, and the refresh\nthread will refresh the token when ~20% of that time is left.\n\nHowever, you can set the expiration time to be much longer on the server (up to a week). In this\nscenario, you may want to refresh the token more often than once every 5 days or so, but not every hour.\n\nThere is now a new option `MaximumRefreshWaitTime` which allows you to specify the maximum amount\nin time that the refresh thread will wait before refreshing the token. This defaults to 4 hours.\nThis means that if you have your server set to a one hour token expiration, nothing changes, but\nif you extend the server refresh to (for example) a week, as long as the user launches the app\nat least once a week, they will never have to re-authenticate.\n\n## BREAKING CHANGES v3.1 → v4.x\n\n- Exceptions have been simplified to a single `GotrueException`. A `Reason` field has been added\n  to `GotrueException` to clarify what happened. This should also be easier to manage as the Gotrue\n  server API \u0026 messages evolve.\n- The session delegates for `Save`/`Load`/`Destroy` have been simplified to no longer require `async`.\n- Console logging in a few places (most notable the background refresh thread) has been removed\n  in favor of a notification method. See `Client.AddDebugListener()` and the test cases for examples.\n  This will allow you to implement your own logging strategy (write to temp file, console, user visible\n  err console, etc).\n- The client now more reliably emits AuthState changes.\n- There is now a single source of truth for headers in the stateful Client - the `Options` headers.\n\nNew feature:\n\n- Added a `Settings` request to the stateless API only - you can now query the server instance to\n  determine if it's got the settings you need. This might allow for things like a visual\n  component in a tool to verify the GoTrue settings are working correctly, or tests that run differently\n  depending on the server configuration.\n\nImplementation notes:\n\n- Test cases have been added to help ensure reliability of auth state change notifications\n  and persistence.\n- Persistence is now managed via the same notifications as auth state change\n\n## BREAKING CHANGES v3.0 → 3.1\n\n- We've implemented the PKCE auth flow. SignIn using a provider now returns an instance of `ProviderAuthState` rather\n  than a `string`.\n- The provider sign in signature has moved `scopes` into `SignInOptions`\n\nIn Short:\n\n```c#\n# What was:\nvar url = await client.SignIn(Provider.Github, \"scopes and things\");\n\n# Becomes:\nvar state = await client.SignIn(Provider.Github, new SignInOptions { \"scopes and things\" });\n// Url is now at `state.Uri`\n```\n\n---\n\n## Getting Started\n\nTo use this library on the Supabase Hosted service but separately from the `supabase-csharp`, you'll need to specify\nyour url and public key like so:\n\n```c#\nvar auth = new Supabase.Gotrue.Client(new ClientOptions\u003cSession\u003e\n{\n    Url = \"https://PROJECT_ID.supabase.co/auth/v1\",\n    Headers = new Dictionary\u003cstring, string\u003e\n    {\n        { \"apikey\", SUPABASE_PUBLIC_KEY }\n    }\n})\n```\n\nOtherwise, using it this library with a local instance:\n\n```c#\nvar options = new ClientOptions { Url = \"https://example.com/api\" };\nvar client = new Client(options);\nvar user = await client.SignUp(\"new-user@example.com\");\n\n// Alternatively, you can use a StatelessClient and do API interactions that way\nvar options = new StatelessClientOptions { Url = \"https://example.com/api\" }\nawait new StatelessClient().SignUp(\"new-user@example.com\", options);\n```\n\n## Persisting, Retrieving, and Destroying Sessions.\n\nThis Gotrue client is written to be agnostic when it comes to session persistence, retrieval, and\ndestruction. `ClientOptions` exposes\nproperties that allow these to be specified.\n\nIn the event these are specified and the `AutoRefreshToken` option is set, as the `Client` Initializes, it will also\nattempt to\nretrieve, set, and refresh an existing session.\n\nFor example, using `Xamarin.Essentials` in `Xamarin.Forms`, this might look like:\n\n```c#\n// This is a method you add your application launch/setup\nasync void Initialize() {\n\n    // Specify the methods you'd like to use as persistence callbacks\n    var persistence = new GotrueSessionPersistence(SaveSession, LoadSession, DestroySession);\n    var client = new Client(\n            Url = GOTRUE_URL,\n            new ClientOptions { \n                AllowUnconfirmedUserSessions = true, \n                SessionPersistence = persistence });\n                \n    // Specify a debug callback to listen to problems with the background token refresh thread\n    client.AddDebugListener(LogDebug);\n    \n    // Specify a call back to listen to changes in the user state (logged in, out, etc)\n    client.AddStateChangedListener(AuthStateListener);\n\n    // Load the session from persistence\n    client.LoadSession();\n    // Loads the session using SessionRetriever and sets state internally.\n    await client.RetrieveSessionAsync();\n}\n\n// Add callback methods for above\n// Here's a quick example of using this to save session data to the user's cache folder\n// You'll want to add methods for loading the file and deleting when the user logs out \ninternal bool SaveSession(Session session)\n{\n    var cacheFileName = \".gotrue.cache\";\n    \n    try\n    {\n        var cacheDir = FileSystem.CacheDirectory;\n        var path = Path.Join(cacheDir, cacheFileName);\n        var str = JsonConvert.SerializeObject(session);\n\n        using (StreamWriter file = new StreamWriter(path))\n        {\n            file.Write(str);\n            file.Dispose();\n            return Task.FromResult(true);\n        };\n    }\n    catch (Exception err)\n    {\n        Debug.WriteLine(\"Unable to write cache file.\");\n        throw err;\n    }\n}\n```\n\n## 3rd Party OAuth\n\nOnce again, Gotrue client is written to be agnostic of platform. In order for Gotrue to sign in a user from an Oauth\ncallback, the PKCE flow is preferred:\n\n1) The Callback Url must be set in the Supabase Admin panel\n2) The Application should have listener to receive that Callback\n3) Generate a sign in request using: `client.SignIn(PROVIDER, options)` and setting the options to use the\n   PKCE `FlowType`\n4) Store `ProviderAuthState.PKCEVerifier` so that the application callback can use it to verify the returned code\n5) In the Callback, use stored `PKCEVerifier` and received `code` to exchange for a session.\n\n```c#\nvar state = await client.SignIn(Constants.Provider.Github, new SignInOptions\n{\n    FlowType = Constants.OAuthFlowType.PKCE,\n    RedirectTo = \"http://localhost:3000/oauth/callback\"\n});\n\n// In callback received from Supabase returning to RedirectTo (set above)\n// Url is set as: http://REDIRECT_TO_URL?code=CODE\nvar session = await client.ExchangeCodeForSession(state.PKCEVerifier, RETRIEVE_CODE_FROM_GET_PARAMS);\n```\n\n## Sign In With Single Sign On (SSO)\nSingle Sign On (SSO) is an enterprise level authentication protocol that allows a single enterprise account to\naccess many apps at once. A few examples of supported SSO providers are Okta, Microsoft Entra and Google Workspaces\n\nIf not already done so, you must first add an SSO provider to your supabase project via the supabase CLI.\nSee the following [link](https://supabase.com/docs/guides/auth/enterprise-sso/auth-sso-saml) for more info on how to\nconfigure SSO providers\n\nThe flow functions similar to the OAuth flow and supports many of the same parameters. Under the hood\nthe flow is handled quite differently by the GoTrue server but the client is agnostic to the difference in \nimplementations and session info is handled in the same way as the OAuth flow\n\nGeneral auth flow is as follows:\n\n1. Request initiated by calling `SignInWithSSO`\n   1. The `RedirectTo` attribute is recommended for handling the callback and converting to a session\n1. `ssoResposne` contains the providers login Uri, navigate to this\n1. User logs in with provider (Okta/Auth0, Microsoft Entra, Google Workspaces ect...)\n1. Supabase GoTrue server handles SAML exchange for us\n   1. Supabase GoTrue server generates session info and appends it to the callback (RedirectedTo) url\n1. We can then use either `ExchangeCodeForSession(code)` or `GetSessionFromUrl(callbackUri)`\n\n```csharp\nusing Constants = Supabase.Gotrue.Constants;\n\nvar ssoResponse = await client.SignInWithSSO(\"supabase.io\", new SignInWithSSOOptions\n{\n    RedirectTo = \"https://localhost:3000/welcome\"\n});\n\n// Handle login via ssoResponse.Uri\n//\n// When the user logs in using the Uri from the ssoResponse, \n// they will be redirected to the RedirectTo\n\n// In callback received from Supabase returning to RedirectTo (set above)\n// Url is set as: http://REDIRECT_TO_URL?access_token=foobar\u0026expires_at=123...\nvar session = await client.GetSessionFromUrl(url);\n```\n\nFor handling session persistence its recommended using a session persistence layer, take a look at\nthe following [example](GotrueExample/SupabaseClientPersistence.cs)\n\nFor additional info on how the GoTrue server handles SSO requests see \n[here](https://github.com/supabase/auth/blob/55409f797bea55068a3fafdddd6cfdb78feba1b4/internal/api/samlacs.go#L315-L316)\nand [here](https://github.com/supabase/auth/blob/55409f797bea55068a3fafdddd6cfdb78feba1b4/internal/api/token.go#L54-L55)\n## Troubleshooting\n\n**Q: I've created a User but while attempting to log in it throws an exception:**\n\nA: Provided the credentials are correct, make sure that the User has also confirmed their email.\n\nAdding a handler for email confirmation to a desktop or mobile application can be done, but it\nrequires setting up URL handlers for each platform, which can be pretty difficult to do if you\naren't really comfortable with configuring these handlers. (\ne.g. [Windows](https://learn.microsoft.com/en-us/windows/win32/search/-search-3x-wds-ph-install-registration),\n[Apple](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app),\n[Android](https://developer.android.com/training/app-links))\nYou may find it easier to create a\nsimple web application to handle email confirmation - that way a user can just click a link in\ntheir email and get confirmed that way. Your desktop or mobile app should inspect the user object\nthat comes back and use that to see if the user is confirmed.\n\nYou might find it easiest to do something like create and deploy a\nsimple [SvelteKit](https://kit.svelte.dev/) or even a very basic\npure [JavaScript](https://github.com/supabase/examples-archive/tree/main/supabase-js-v1/auth/javascript-auth) project\nto handle email verification.\n\n## Status\n\n- [x] API\n    - [x] Sign Up with Email\n    - [x] Sign In with Email\n    - [x] Send Magic Link Email\n    - [x] Invite User by Email\n    - [x] Reset Password for Email\n    - [x] Signout\n    - [x] Get Url for Provider\n    - [x] Get User\n    - [x] Update User\n    - [x] Refresh Access Token\n    - [x] List Users (includes filtering, sorting, pagination)\n    - [x] Get User by Id\n    - [x] Create User\n    - [x] Update User by Id\n    - [x] Sign In with Single Sign On (SSO)\n- [x] Client\n    - [x] Get User\n    - [x] Refresh Session\n    - [x] Auth State Change Handler\n    - [x] Provider Sign In (Provides URL)\n    - [x] Sign In with Single Sign On (SSO)\n- [x] Provide Interfaces for Custom Token Persistence Functionality\n- [x] Documentation\n- [x] Unit Tests\n- [x] Nuget Release\n\n## Package made possible through the efforts of:\n\nJoin the ranks! See a problem? Help fix it!\n\n\u003ca href=\"https://github.com/supabase-community/gotrue-csharp/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=supabase-community/gotrue-csharp\" /\u003e\n\u003c/a\u003e\n\n\u003csmall\u003eMade with [contrib.rocks](https://contrib.rocks).\u003c/small\u003e\n\n## Contributing\n\nWe are more than happy to have contributions! Please submit a PR.\n\n### Testing\n\nTo run the tests locally you must have docker and docker-compose installed. Then in the root of the repository run:\n\n- `docker-compose up -d`\n- `dotnet test`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsupabase-community%2Fgotrue-csharp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsupabase-community%2Fgotrue-csharp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsupabase-community%2Fgotrue-csharp/lists"}