diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/.vscode/launch.json b/Examples/BlazorWebAssemblySupabaseTemplate/.vscode/launch.json
new file mode 100644
index 00000000..b0726229
--- /dev/null
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/.vscode/launch.json
@@ -0,0 +1,11 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Launch and Debug Standalone Blazor WebAssembly App",
+ "type": "blazorwasm",
+ "request": "launch",
+ "cwd": "${workspaceFolder}"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/.vscode/tasks.json b/Examples/BlazorWebAssemblySupabaseTemplate/.vscode/tasks.json
new file mode 100644
index 00000000..95febf9a
--- /dev/null
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/.vscode/tasks.json
@@ -0,0 +1,41 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/BlazorWebAssemblySupabaseTemplate.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/BlazorWebAssemblySupabaseTemplate.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "watch",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "watch",
+ "run",
+ "--project",
+ "${workspaceFolder}/BlazorWebAssemblySupabaseTemplate.csproj"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/Dtos/TodoPrivate.cs b/Examples/BlazorWebAssemblySupabaseTemplate/Dtos/TodoPrivate.cs
new file mode 100644
index 00000000..faf9753e
--- /dev/null
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/Dtos/TodoPrivate.cs
@@ -0,0 +1,19 @@
+using System;
+using Postgrest.Attributes;
+using Postgrest.Models;
+using Supabase;
+
+namespace BlazorWebAssemblySupabaseTemplate.Dtos;
+
+[Table("TodoPrivate")]
+public class TodoPrivate : BaseModel
+{
+ [PrimaryKey("id", false)] // Key is Autogenerated
+ public int Id { get; set; }
+
+ [Column("title")]
+ public string? Title { get; set; }
+
+ [Column("user_id")]
+ public string User_id { get; set; }
+}
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/Pages/CrudPrivate/CrudPagePrivate.razor b/Examples/BlazorWebAssemblySupabaseTemplate/Pages/CrudPrivate/CrudPagePrivate.razor
new file mode 100644
index 00000000..ad95a736
--- /dev/null
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/Pages/CrudPrivate/CrudPagePrivate.razor
@@ -0,0 +1,118 @@
+@page "/crud-private"
+@using Dtos
+@using Blazored.LocalStorage
+
+@inject DatabaseService DatabaseService
+@inject NavigationManager NavigationManager
+@inject ISnackbar Snackbar
+@inject ILocalStorageService localStorage
+
+
+
+
Todos private by RLS
+
+
+
+
+
+ New item
+
+
+
+
+
+
+
+ @if (_processingNewItem)
+ {
+
+ Processing
+ }
+ else
+ {
+ Save
+ }
+
+
+
+
+
+
+
+
+ @if (_todoListFiltered == null)
+ {
+
+
+
+ Title |
+ Action |
+
+
+
+
+ |
+ |
+
+
+
+ }
+ else if (_todoListFiltered.Count == 0)
+ {
+
+
+
+ Title |
+ Action |
+
+
+
+
+ There is no items in this table.
+
+
+
+ }
+ else
+ {
+
+
+
+
+
+
+
+ Title
+
+
+ User_id
+
+
+
+ Action
+
+
+
+ @context?.Title
+ @context?.User_id
+
+ @* *@
+
+
+
+
+
+
+
+ }
+
+
\ No newline at end of file
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/Pages/CrudPrivate/CrudPagePrivate.razor.cs b/Examples/BlazorWebAssemblySupabaseTemplate/Pages/CrudPrivate/CrudPagePrivate.razor.cs
new file mode 100644
index 00000000..b443696c
--- /dev/null
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/Pages/CrudPrivate/CrudPagePrivate.razor.cs
@@ -0,0 +1,59 @@
+using BlazorWebAssemblySupabaseTemplate.Dtos;
+using BlazorWebAssemblySupabaseTemplate.Services;
+using MudBlazor;
+
+namespace BlazorWebAssemblySupabaseTemplate.Pages.CrudPrivate;
+
+public partial class CrudPagePrivate
+{
+ protected override async Task OnInitializedAsync()
+ {
+ await GetTable();
+ }
+
+ // ---------------- SELECT TABLE
+ private IReadOnlyList? _todoList { get; set; }
+ private IReadOnlyList? _todoListFiltered { get; set; }
+ private MudTable? table;
+ protected async Task GetTable()
+ {
+ // await Task.Delay(10000);
+ IReadOnlyList todos = await DatabaseService.From();
+ _todoList = todos;
+ _todoListFiltered = todos;
+ await InvokeAsync(StateHasChanged);
+ }
+
+ // ---------------- SEARCH
+ private void OnValueChangedSearch(string text)
+ {
+ _todoListFiltered = _todoList?.Where(row => row.Title.Contains(text)).ToList();
+ }
+
+ // ---------------- DELETE
+ private async Task OnClickDelete(TodoPrivate item)
+ {
+ await DatabaseService.Delete(item);
+ await GetTable();
+ }
+
+ // ---------------- CREATE NEW
+
+ protected TodoPrivate model = new();
+ private bool success = false;
+ string[] errors = { };
+ MudForm? form;
+ private bool _processingNewItem = false;
+ private async Task OnClickSave()
+ {
+ string user_id = await localStorage.GetItemAsync("user_id");
+
+ model.User_id = user_id;
+ _processingNewItem = true;
+ await DatabaseService.Insert(model);
+ model = new();
+ await GetTable();
+ success = false;
+ _processingNewItem = false;
+ }
+}
\ No newline at end of file
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/Program.cs b/Examples/BlazorWebAssemblySupabaseTemplate/Program.cs
index 519033fc..e690f2a4 100644
--- a/Examples/BlazorWebAssemblySupabaseTemplate/Program.cs
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/Program.cs
@@ -28,16 +28,37 @@
// ---------- BLAZOR AUTH
-builder.Services.AddScoped();
+builder.Services.AddScoped(
+ provider => new CustomAuthStateProvider(
+ provider.GetRequiredService(),
+ provider.GetRequiredService()
+ )
+)
+ ;
builder.Services.AddAuthorizationCore();
-
-
// ---------- SUPABASE
var url = "https://pylnesfgmytjegzzculn.supabase.co";
var key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InB5bG5lc2ZnbXl0amVnenpjdWxuIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NjgyOTMwMzcsImV4cCI6MTk4Mzg2OTAzN30.kI29Q_qYWDH5SD6oi5NTwHG6Pxy1e1AUfR8s_ga45lE";
-builder.Services.AddScoped>(args => new Supabase.Client(url, key, new Supabase.SupabaseOptions { AutoConnectRealtime = true }));
+builder.Services.AddScoped(
+ provider => new Supabase.Client(
+ url,
+ key,
+ new Supabase.SupabaseOptions
+ {
+ AutoRefreshToken = true,
+ AutoConnectRealtime = true,
+ PersistSession = true,
+ SessionHandler = new CustomSupabaseSessionHandler(
+ provider.GetRequiredService(),
+ provider.GetRequiredService>()
+ )
+ }
+ )
+);
+
+// builder.Services.AddScoped>(args => new Supabase.Client(url, key, new Supabase.SupabaseOptions { AutoConnectRealtime = true }));
builder.Services.AddScoped();
builder.Services.AddScoped();
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/Providers/CustomAuthStateProvider.cs b/Examples/BlazorWebAssemblySupabaseTemplate/Providers/CustomAuthStateProvider.cs
index 4ae7b575..114a391d 100644
--- a/Examples/BlazorWebAssemblySupabaseTemplate/Providers/CustomAuthStateProvider.cs
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/Providers/CustomAuthStateProvider.cs
@@ -11,32 +11,35 @@ namespace BlazorWebAssemblySupabaseTemplate.Providers;
public class CustomAuthStateProvider : AuthenticationStateProvider
{
private readonly ILocalStorageService _localStorage;
+ private readonly Supabase.Client _client;
// private readonly IHttpClientFactory httpClientFactory;
// private readonly HttpClient _http;
public CustomAuthStateProvider(
- ILocalStorageService localStorage
+ ILocalStorageService localStorage,
+ Supabase.Client client
// IHttpClientFactory httpClientFactory
// HttpClient http
)
{
_localStorage = localStorage;
+ _client = client;
// this.httpClientFactory = httpClientFactory;
// _http = http;
}
public override async Task GetAuthenticationStateAsync()
{
- string token = await _localStorage.GetItemAsStringAsync("token");
- // string token = "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTUxMiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiVG9ueSBTdGFyayIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6Iklyb24gTWFuIiwiZXhwIjozMTY4NTQwMDAwfQ.IbVQa1lNYYOzwso69xYfsMOHnQfO3VLvVqV2SOXS7sTtyyZ8DEf5jmmwz2FGLJJvZnQKZuieHnmHkg7CGkDbvA";
-
+ // Sets client auth and connects to realtime (if enabled)
+ await _client.InitializeAsync();
+
var identity = new ClaimsIdentity();
// _http.DefaultRequestHeaders.Authorization = null;
- if (!string.IsNullOrEmpty(token))
+ if (!string.IsNullOrEmpty(_client.Auth.CurrentSession?.AccessToken))
{
- identity = new ClaimsIdentity(ParseClaimsFromJwt(token), "jwt");
+ identity = new ClaimsIdentity(ParseClaimsFromJwt(_client.Auth.CurrentSession.AccessToken), "jwt");
// _http.DefaultRequestHeaders.Authorization =
// new AuthenticationHeaderValue("Bearer", token.Replace("\"", ""));
}
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/Providers/CustomSupabaseSessionHandler.cs b/Examples/BlazorWebAssemblySupabaseTemplate/Providers/CustomSupabaseSessionHandler.cs
new file mode 100644
index 00000000..4b4d8667
--- /dev/null
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/Providers/CustomSupabaseSessionHandler.cs
@@ -0,0 +1,42 @@
+using Blazored.LocalStorage;
+using Supabase.Gotrue;
+using Supabase.Interfaces;
+
+namespace BlazorWebAssemblySupabaseTemplate.Providers;
+
+public class CustomSupabaseSessionHandler : ISupabaseSessionHandler
+{
+ private readonly ILocalStorageService localStorage;
+ private readonly ILogger logger;
+ private static string SESSION_KEY = "SUPABASE_SESSION";
+
+ public CustomSupabaseSessionHandler(
+ ILocalStorageService localStorage,
+ ILogger logger
+ )
+ {
+ logger.LogInformation("------------------- CONSTRUCTOR -------------------");
+ this.localStorage = localStorage;
+ this.logger = logger;
+ }
+
+ public async Task SessionDestroyer()
+ {
+ logger.LogInformation("------------------- SessionDestroyer -------------------");
+ await localStorage.RemoveItemAsync(SESSION_KEY);
+ return true;
+ }
+
+ public async Task SessionPersistor(TSession session) where TSession : Session
+ {
+ logger.LogInformation("------------------- SessionPersistor -------------------");
+ await localStorage.SetItemAsync(SESSION_KEY, session);
+ return true;
+ }
+
+ public async Task SessionRetriever() where TSession : Session
+ {
+ logger.LogInformation("------------------- SessionRetriever -------------------");
+ return (TSession?) await localStorage.GetItemAsync(SESSION_KEY);
+ }
+}
\ No newline at end of file
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/README.MD b/Examples/BlazorWebAssemblySupabaseTemplate/README.MD
index 2259dc02..f95660b4 100644
--- a/Examples/BlazorWebAssemblySupabaseTemplate/README.MD
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/README.MD
@@ -1,3 +1,17 @@
+#TODO
+ - CustomSupabaseSessionHandler not working.
+ - how to test:
+ - Logout
+ - Login
+ - Access Crud table private RLS page
+ - Insert some row
+ - you will see the row displayed below
+ - press F5 on the browser
+ - the rows will disapear
+ - Maybe i created CustomSupabaseSessionHandler wrong. But it seem like that the token is not being sent with the headers...
+
+ - create policy to insert that check if the user_id is the same of the user logged in trying to insert.
+
# Credits
https://github.com/supabase-community/supabase-csharp
https://github.com/patrickgod/BlazorAuthenticationTutorial
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/Services/AuthService.cs b/Examples/BlazorWebAssemblySupabaseTemplate/Services/AuthService.cs
index 0f10c788..fffbd518 100644
--- a/Examples/BlazorWebAssemblySupabaseTemplate/Services/AuthService.cs
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/Services/AuthService.cs
@@ -10,29 +10,24 @@ namespace BlazorWebAssemblySupabaseTemplate.Services;
public class AuthService
{
- private readonly ISupabaseClient client;
+ private readonly Supabase.Client client;
private readonly AuthenticationStateProvider customAuthStateProvider;
private readonly ILocalStorageService localStorage;
private readonly ILogger logger;
public AuthService(
- ISupabaseClient client,
+ Supabase.Client client,
AuthenticationStateProvider CustomAuthStateProvider,
ILocalStorageService localStorage,
ILogger logger
) : base()
{
- logger.LogInformation("CONSTRUCTOR: AuthService");
+ logger.LogInformation("------------------- CONSTRUCTOR -------------------");
this.client = client;
customAuthStateProvider = CustomAuthStateProvider;
this.localStorage = localStorage;
this.logger = logger;
-
- if( client.Auth == null)
- {
- client.InitializeAsync();
- }
}
public async Task Login(string email, string password)
@@ -42,16 +37,15 @@ public async Task Login(string email, string password)
Session? session = await client.Auth.SignIn(email, password);
logger.LogInformation("------------------- User logged in -------------------");
- logger.LogInformation($"instance.Auth.CurrentUser.Id {client?.Auth?.CurrentUser?.Id}");
+ // logger.LogInformation($"instance.Auth.CurrentUser.Id {client?.Auth?.CurrentUser?.Id}");
logger.LogInformation($"client.Auth.CurrentUser.Email {client?.Auth?.CurrentUser?.Email}");
-
- await localStorage.SetItemAsStringAsync("token", session?.AccessToken);
+
await customAuthStateProvider.GetAuthenticationStateAsync();
}
public async Task Logout()
{
- await localStorage.RemoveItemAsync("token");
+ await client.Auth.SignOut();
await customAuthStateProvider.GetAuthenticationStateAsync();
}
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/Services/DatabaseService.cs b/Examples/BlazorWebAssemblySupabaseTemplate/Services/DatabaseService.cs
index f2ba84b0..bcd52530 100644
--- a/Examples/BlazorWebAssemblySupabaseTemplate/Services/DatabaseService.cs
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/Services/DatabaseService.cs
@@ -11,47 +11,42 @@ namespace BlazorWebAssemblySupabaseTemplate.Services;
public class DatabaseService
{
- private readonly ISupabaseClient client;
- private readonly AuthenticationStateProvider customAuthStateProvider;
- private readonly ILocalStorageService localStorage;
- private readonly ILogger logger;
-
- public DatabaseService(
- ISupabaseClient client,
- AuthenticationStateProvider CustomAuthStateProvider,
- ILocalStorageService localStorage,
- ILogger logger
- ) : base()
- {
- logger.LogInformation("CONSTRUCTOR: DatabaseService");
-
- this.client = client;
- customAuthStateProvider = CustomAuthStateProvider;
- this.localStorage = localStorage;
- this.logger = logger;
-
- if( client.Postgrest == null)
- {
- client.InitializeAsync();
- }
- }
-
- public async Task> From() where TModel : BaseModel, new()
- {
- Postgrest.Responses.ModeledResponse modeledResponse = await client.From().Get();
- return modeledResponse.Models;
- }
-
- public async Task> Delete(TModel item) where TModel : BaseModel, new()
- {
- Postgrest.Responses.ModeledResponse modeledResponse = await client.From().Delete(item);
- return modeledResponse.Models;
- }
-
- public async Task> Insert(TModel item) where TModel : BaseModel, new()
- {
- Postgrest.Responses.ModeledResponse modeledResponse = await client.From().Insert(item);
- return modeledResponse.Models;
- }
+ private readonly Supabase.Client client;
+ private readonly AuthenticationStateProvider customAuthStateProvider;
+ private readonly ILocalStorageService localStorage;
+ private readonly ILogger logger;
+
+ public DatabaseService(
+ Supabase.Client client,
+ AuthenticationStateProvider CustomAuthStateProvider,
+ ILocalStorageService localStorage,
+ ILogger logger
+ ) : base()
+ {
+ logger.LogInformation("------------------- CONSTRUCTOR -------------------");
+
+ this.client = client;
+ customAuthStateProvider = CustomAuthStateProvider;
+ this.localStorage = localStorage;
+ this.logger = logger;
+ }
+
+ public async Task> From() where TModel : BaseModel, new()
+ {
+ Postgrest.Responses.ModeledResponse modeledResponse = await client.From().Get();
+ return modeledResponse.Models;
+ }
+
+ public async Task> Delete(TModel item) where TModel : BaseModel, new()
+ {
+ Postgrest.Responses.ModeledResponse modeledResponse = await client.From().Delete(item);
+ return modeledResponse.Models;
+ }
+
+ public async Task> Insert(TModel item) where TModel : BaseModel, new()
+ {
+ Postgrest.Responses.ModeledResponse modeledResponse = await client.From().Insert(item);
+ return modeledResponse.Models;
+ }
}
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/Shared/MainLayout.razor b/Examples/BlazorWebAssemblySupabaseTemplate/Shared/MainLayout.razor
index 8460af32..10744426 100644
--- a/Examples/BlazorWebAssemblySupabaseTemplate/Shared/MainLayout.razor
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/Shared/MainLayout.razor
@@ -36,9 +36,12 @@
@* ********************************** MENU ********************************** *@
-
+
Crud table
+
+ Crud table private RLS
+
diff --git a/Examples/BlazorWebAssemblySupabaseTemplate/Supabase/DatabasePolicies.sql b/Examples/BlazorWebAssemblySupabaseTemplate/Supabase/DatabasePolicies.sql
new file mode 100644
index 00000000..e4287831
--- /dev/null
+++ b/Examples/BlazorWebAssemblySupabaseTemplate/Supabase/DatabasePolicies.sql
@@ -0,0 +1,37 @@
+-- CREATE POLICY "policy_name"
+-- ON public.Lista FOR
+-- DELETE USING ( auth.uid() = user_id );
+
+-- CREATE POLICY "policy_name"
+-- ON public.Lista FOR
+-- SELECT USING ( auth.uid() = user_id );
+
+-- #################### TABELA LISTA
+
+-- CREATE POLICY "Enable read access for all users" ON "public"."Lista"
+-- AS PERMISSIVE FOR SELECT
+-- TO authenticated
+-- USING (true)
+
+
+CREATE POLICY "Users can SELECT if own row"
+ ON "public"."TodoPrivate" AS PERMISSIVE FOR SELECT
+ TO authenticated
+ USING ( auth.uid() = user_id );
+
+CREATE POLICY "Users can UPDATE if own row"
+ ON "public"."TodoPrivate" AS PERMISSIVE FOR UPDATE
+ TO authenticated
+ USING ( auth.uid() = user_id );
+
+CREATE POLICY "Users can DELETE if own row"
+ ON "public"."TodoPrivate" AS PERMISSIVE FOR DELETE
+ TO authenticated
+ USING ( auth.uid() = user_id );
+
+CREATE POLICY "Users can SELECT if authenticated"
+ ON "public"."TodoPrivate"
+ AS PERMISSIVE
+ FOR INSERT
+ TO authenticated
+ WITH CHECK (true);
\ No newline at end of file