Skip to content

Authentication

The Supabase Auth module implements IUserContext using GoTrue, Supabase's authentication service. It resolves the current user by calling the GoTrue /user endpoint with the bearer token from the incoming HTTP request.

Effects Module

The pre-built AuthModule wraps IUserContext as an effect. Include it in your module:

[EffectsModule(typeof(AuthModule))]
[EffectsModule(typeof(StorageModule))]
public partial class AppModule;

In development, AuthModule registers a StubUserContext that returns a fixed test user. In production, AddSupabaseAuth() replaces it with GoTrue-backed authentication:

// Production — Supabase GoTrue
options.AddSupabaseAuth();

// Alternative — Deepstaging JWT + Google OAuth
services.AddDeepstagingAuth(jwt => ..., google => ...);

Overview

services.AddDeepstaging(options => options
    .AddSupabaseAuth());

This registers:

  • ISupabaseTokenAccessorHttpContextTokenAccessor — extracts the Authorization: Bearer {token} header
  • IUserContextSupabaseUserContext — calls GoTrue to resolve the user

How It Works

Client Request
  ├─ Authorization: Bearer <supabase-jwt>
HttpContextTokenAccessor
  │  extracts JWT from header
SupabaseUserContext
  │  calls GET /auth/v1/user
GoTrue validates token
  │  returns user profile
UserClaims(UserId, Email, Name)

The token is validated server-side by GoTrue on every request. This means:

  • Banned or deleted users are immediately rejected
  • No local JWT validation or key management needed
  • Token refresh is handled by the Supabase client SDK

Usage

Inject IUserContext anywhere in your application:

public class OrderHandler(IUserContext userContext)
{
    public async Task<Order> Handle(CreateOrderCommand cmd)
    {
        var user = await userContext.GetCurrentUser();
        return new Order(user.UserId, cmd.Items);
    }
}

For optional authentication:

var user = await userContext.GetCurrentUserOrNull();
if (user is null)
    return Results.Unauthorized();

Token Accessor

The default HttpContextTokenAccessor extracts the bearer token from HttpContext.Request.Headers.Authorization. For non-HTTP scenarios (background jobs, WebSocket handlers), implement ISupabaseTokenAccessor:

public sealed class CustomTokenAccessor : ISupabaseTokenAccessor
{
    public string? GetAccessToken() => /* your token source */;
}

// Register before AddSupabaseAuth()
services.AddSingleton<ISupabaseTokenAccessor, CustomTokenAccessor>();

GoTrue Client

The underlying SupabaseAuthClient is a generated HTTP Client that exposes the full GoTrue API:

Method Description
SignUp(email, password) Create a new user
SignInWithPassword(email, password) Email/password sign-in
SignInWithOtp(email) Magic link sign-in
SignInWithOAuth(provider) OAuth redirect URL
GetUser(authorization) Get current user profile
UpdateUser(authorization, body) Update user metadata
SignOut(authorization) Invalidate session
RefreshToken(refreshToken) Refresh access token
ResetPasswordForEmail(email) Send password reset
InviteUserByEmail(email) Admin: invite user
AdminGetUser(userId) Admin: get any user
AdminDeleteUser(userId) Admin: delete user
AdminListUsers() Admin: list all users

Inject SupabaseAuthClient directly when you need operations beyond IUserContext.

ISupabaseAuth Effects

For effects-based access to GoTrue operations, use SupabaseAuthModule. This wraps the full auth API as composable effects:

[EffectsModule(typeof(SupabaseAuthModule))]
public partial class AppModule;

Register the implementation:

options.AddSupabaseAuthOperations();

Then compose auth operations inside your effect pipelines:

public static Eff<AppRuntime, Unit> InviteAndNotify(string email) =>
    from session in AppEffects.SupabaseAuth.SignUpAsync<AppRuntime>(email, GeneratePassword())
    from _       in AppEffects.Notify.SendAsync<AppRuntime>(new UserInvited(email))
    select unit;

Available Operations

Effect Method Description
SignUpAsync Create a new user account
SignInWithPasswordAsync Email/password authentication
SendOtpToEmailAsync Send magic link or OTP via email
SendOtpToPhoneAsync Send OTP via SMS
VerifyEmailOtpAsync Verify email OTP code
VerifyPhoneOtpAsync Verify phone OTP code
RefreshSessionAsync Refresh an access token
GetCurrentUserAsync Get the authenticated user profile
UpdateUserAsync Update user metadata
SendPasswordRecoveryAsync Send password reset email
SignOutAsync Sign out and invalidate session
EnrollMfaFactorAsync Enroll a new MFA factor (TOTP)
ChallengeMfaFactorAsync Create an MFA challenge
VerifyMfaChallengeAsync Verify an MFA challenge code
UnenrollMfaFactorAsync Remove an MFA factor

Row Level Security

Supabase Auth pairs with Row Level Security (RLS) for defense-in-depth. RLS policies run inside Postgres and enforce access rules regardless of which application code queries the data:

-- Users can only see their own orders
CREATE POLICY "Users see own orders" ON orders
  FOR SELECT
  USING (auth.uid() = user_id);

This complements application-level authorization (Dispatch Authorization) — even if a bug bypasses your application checks, the database rejects unauthorized access.