Integrating Supabase Auth with .NET

Tuesday, November 21, 2023

Supabase and .NET

As a long term .NET developer, I have always been impressed with the quality that .NET introduces to the backend world. Paired with my preference for Next.js/React frontends, I find the features offered by Supabase incredibly valuable. Not only does Supabase excel in its Postgres database offerings, but its authentication capabilities called Supabase Auth are great as well.

Supabase's generous free tier, allows you up to 50,000 monthly active users (MAU's) at no cost.

But how do we effectively combine the strengths of Supabase Auth with .NET?

Row Level Security

While Supabase’s Row Level Security is a robust feature that authorizes users to your data, it might not be the best fit for larger-scale environments. In such cases, I advocate for offloading this logic to the backend, allowing our backend APIs to govern which features and data users can access. This approach is actually pretty straightforward.

The basic idea involves using the frontend to transmit the authentication token to your backend, and then letting the backend determine user access. We can utilize the @supabase/ssr package package to obtain the access token on the client-side. This token can then be sent as a Bearer token to call our .NET backend APIs.

Integrating JWT in .NET

Supabase Auth generates JWT (JSON Web Tokens), which our .NET backend can easily verify. To do this, we need to:

  1. Obtain the Signing Key from Supabase
  2. Configure JWT Settings for Supabase in our .NET application

1. Obtaining the Signing Key from Supabase

To validate the JWT signature on the server, it's essential to use the JWT Secret signing key from your Supabase account. You can access this key through a SQL Editor in your Supabase portal with the following query:

show app.settings.jwt_secret;

This command retrieves the secret key in a Base64 encoded string which is required to validate the tokens.

2. Configuring JWT Settings for Supabase in our .NET application

To set up JWT validation in .NET:

var builder = WebApplication.CreateBuilder(args);
 
var supasbaseSignatureKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(supabaseSecretKey));
var validIssuers = "https://<your supabase project id>.supabase.co/auth/v1";
var validAudiences = new List<string>() { "authenticated" };
 
builder.Services.AddAuthentication().AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = supabaseSignatureKey,
        ValidAudiences = validAudiences,
        ValidIssuer = validIssuer
    };
});

Be sure to use the secret key from step 1 as the input to create a SymmetricSecurityKey. This way the .NET authentication framework will check the key for a valid signature.

Be sure to include a list of valid audiences and a valid issuer as well. By default, the audiences are populated with ["audiences"] and the issuer is the URL of your Supabase project with the /auth/v1 suffix.

Custom Claims and Roles

To enhance this, you can add roles or other relevant user information to the JWT token as claims. A practical tool for this is the supabase-custom-claims project. This project provides a set of queries and features for Supabase, enabling the storage of custom claim data in the raw_app_meta_data field of your users.

These custom claims are then included in the JWT token, which your .NET application can extract and use as needed.