C
C#ā€¢2y ago
gurkang

ā” Handling Supabase auth with dotnet backend. Is this way wrong/not the dotnet way?

I'm authenticating users on the frontend using supabase auth. After successful authentication I get returned user data, jwt, and more. I then want communicate with some backend services, which I'm trying to write in dotnet. I include the token from FE in the request and then handle it with some middleware to handle this like so:
app.Use(async (context, next) =>
{
try
{
var token = context.Request.Headers["Authorization"].ToString();
if (string.IsNullOrWhiteSpace(token))
{
throw new AuthenticationException("Missing JWT");
}

var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", token);
var apiKey = builder.Configuration.GetValue<string>("apiKey");
client.DefaultRequestHeaders.Add("apikey", apiKey);
var res = await client.GetFromJsonAsync<UserFromSupabase>(
builder.Configuration.GetValue<string>("supabaseUrl"));

if (res == null)
{
throw new AuthenticationException("Invalid token");
}

;

if (res is { Aud: not null, Email: not null, Id: not null })
{
context.User = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new Claim("Email", res.Email),
new Claim("Id", res.Id),
new Claim("Aud", res.Aud)
}));
}

;
await next(context);
}
catch (System.Exception error)
{
context.Response.Clear();
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsJsonAsync("Malformed or expired JWT");
}
});
app.Use(async (context, next) =>
{
try
{
var token = context.Request.Headers["Authorization"].ToString();
if (string.IsNullOrWhiteSpace(token))
{
throw new AuthenticationException("Missing JWT");
}

var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", token);
var apiKey = builder.Configuration.GetValue<string>("apiKey");
client.DefaultRequestHeaders.Add("apikey", apiKey);
var res = await client.GetFromJsonAsync<UserFromSupabase>(
builder.Configuration.GetValue<string>("supabaseUrl"));

if (res == null)
{
throw new AuthenticationException("Invalid token");
}

;

if (res is { Aud: not null, Email: not null, Id: not null })
{
context.User = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new Claim("Email", res.Email),
new Claim("Id", res.Id),
new Claim("Aud", res.Aud)
}));
}

;
await next(context);
}
catch (System.Exception error)
{
context.Response.Clear();
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsJsonAsync("Malformed or expired JWT");
}
});
But from my experience when doing auth on .Net you're "supposed" to use the useAuthorisation/useAuthentication middleware that's provided by default in the .Net framework? Am I trying to force a very "not dotnet" style of coding onto the framework? Is there a better way to do this?
37 Replies
JakenVeina
JakenVeinaā€¢2y ago
there's nothing wrong with just writing your own middleware like this, no but auth is such a widespread topic, affecting other layers of the application, and with so many pitfalls, that it's definitely best practice to use the standard packages, and configure them to your needs both packages are highly extensible for this reason for starters, what's Suprabase?
anita
anitaā€¢2y ago
Supabase
The Open Source Firebase Alternative | Supabase
The Open Source Alternative to Firebase.
JakenVeina
JakenVeinaā€¢2y ago
so, probably a fairly standard OAuth implementation?
Unknown User
Unknown Userā€¢2y ago
Message Not Public
Sign In & Join Server To View
JakenVeina
JakenVeinaā€¢2y ago
exactly depending on how "standard" the OAuth is, you might actually be able to skip THAT step as well, and use the existing OAuth handler like, if you want are more detailed lecture on how to do it, I can definitely give one and I don't mind too much, cause I remember how infuriating it was learning this for myself not because it's exceptionally complicated, but there's a LOOOOOOT of moving parts, and some are lax in documentation
Unknown User
Unknown Userā€¢2y ago
Message Not Public
Sign In & Join Server To View
JakenVeina
JakenVeinaā€¢2y ago
you typo'd a bit there good try, tho šŸ˜†
Unknown User
Unknown Userā€¢2y ago
Message Not Public
Sign In & Join Server To View
gurkang
gurkangOPā€¢2y ago
@TeBeCo I looked at that library but my problem is that since I'm signing in the user in the frontend, the session exists in the context of the react application. I don't see how I can share the exact same context between the FE and BE like you would need if you are using the C# libraries features like var user = supabase.Auth.CurrentUser. Since i'm lacking the session context on the BE side the only way I can figure out how to get the user without it already existing in the BE context is by manually calling the auth endpoint to get the user given the token and supabase credentials. I'm just very confused on how you would share the "session/context" using the more standard .net auth framework since the oauth login flow is more done on the FE side and the entire session/context is hosted on Supabase end. If I write my own authentication handler, is it basically the same as my middleware but I wrap it/extend it as some IAuthorizationHandler ?
JakenVeina
JakenVeinaā€¢2y ago
so yeah, I'm not gonna try and parse apart how you're understanding this in your head it'll be quicker to just talk about what the architecture SHOULD be hopefully the misunderstanding reveals itself so, you have a client/server app? Blazor frontend, ASP.NET Core backend? something similar? @GurkanG
gurkang
gurkangOPā€¢2y ago
I have a react frontend application and a dotnet core backend. They are totally separate. On the frontend side I am signing in users using the supabase auth api. This is done using the supabase node package. It basically lets me do await supabase.auth.signIn(credentials).. Supabase then handles everything else and returns a session and user. This session context is stored in a react context as well. On the backend side I need to authenticate the requests from my FE. And thus I am sending along the JWT that I get from supabase after successful authentication on their end to my backend API. Here, I am then using the JWT, along with a supabase secret key, to check against an endpoint /auth/user if the JWT is correct, and if it is, I get the user returned to my backend API. Now I know that the request from my FE is an authenticated one and I can access the user information of the user that initiated the request from my react application. I can do what I want here since I've got all the information needed on the user. , ie, return data that is specific to the user. @AnievNekaj Does this make sense?
JakenVeina
JakenVeinaā€¢2y ago
absolutely does the backend communicate with Supabase, other than to validate the token? and pull user info
gurkang
gurkangOPā€¢2y ago
Other than the request to authenticate the JWT (which, if successful return a user object) there is no other communication done from my BE to supabase. I have everything else on my own side, with a postgres DB where any user specific data just has the user_id from supabase as the foreign key
JakenVeina
JakenVeinaā€¢2y ago
lovely that's exactly correct essentially what you're looking to do is have your client send the supabase token to the backend, and exchange it for the backend's own auth token which you then use for all further operations let's assume you want to use a JWT for this all your authentication concerns for the server can be accomplished with the Microsoft.AspNetCore.Authentication middleware, configured with the pre-made Microsoft.AspNetCore.Authentication.JwtBearer authentication scheme plus your own configuration the middleware provides the AuthenticationHandler base class for customization and then the JwtBearer scheme provides an implementation of it, which has its own extension points in more complex scenarios, the middleware allows you to configure an arbitrary number of "schemes" each, with its own handler
gurkang
gurkangOPā€¢2y ago
So I can implement IAuthenticationService to also add the supabase secret key to the request? Since I need not only the JWT but also that secret key for a valid request?
JakenVeina
JakenVeinaā€¢2y ago
correction: AuthenticationHandler yes, but also no the magic here is that your exchanges with Supabase are actually NOT part of the authentication middleware not directly
gurkang
gurkangOPā€¢2y ago
Haha and now i'm confused again
JakenVeina
JakenVeinaā€¢2y ago
yeah so, the way the middleware models things "authentication" consists of 5 fundamental operations which are implemented by AuthenticationHandler<T> and subclasses Authenticate Challenge Forbid SignIn SignOut however, SignIn and SignOut are actually not "core" operations they're not implemented by AuthenticationHandler<T>, but by subclases SignInAuthenticationHandler<T> and SignOutAuthenticationHandler<T> I.E. not every authentication scheme actually INVOLVES these operations and JWT auth is one of them because there's no "session" the scheme is stateless, either a request has a good token, or it doesn't that's the "Authenticate" operation, BTW the operation of looking at an incoming request, and trying to figure out who it came from "Challenge" is the operation of telling the user "You need to take further action to authenticate", I.E. "you're not signed in, but you need to be" "Forbid" is the operation of telling the user "You can't do this, you're shit out of luck", I.E. "you're signed in, and that's how I know you're not supposed to access this" anyway you could maybe argue that JWT ought to have SignIn and SignOut operations, but the way Microsoft implemented the scheme, it doesn't, and I'm sure there's good reason all it means is that you need to setup a dedicated endpoint for the token exchange operation anything not make sense?
gurkang
gurkangOPā€¢2y ago
I appreciate you explaining this for me! It's always good to a have better understanding of how all this works šŸ™‚ It makes sense, but I'm still a bit unsure about how exactly I would fit my current needs to this way of working with auth. I would implement my own handlers and then add those to the Authentication.JwtBearer authentication scheme? And it is within these AuthenticationHandler<T> is where I would all the logic that is similar to my middleware, where I make a request to the supabase auth api endpoint and check wether i have a valid user response or not?
JakenVeina
JakenVeinaā€¢2y ago
no, the handler's already implemented for you, you just need to slap in a few configuration bits concrete examples next
application => application
.UseRouting()
.UseAuthentication()
.UseAuthorization()
application => application
.UseRouting()
.UseAuthentication()
.UseAuthorization()
obviously setup the middleware next, setup and configure services, which control the middleware
gurkang
gurkangOPā€¢2y ago
I think I understand what I need to do. I will try a bit on my end to implement this and I'll update this thread when I get it working! Thanks again @AnievNekaj for the guidance and help
JakenVeina
JakenVeinaā€¢2y ago
hang on, hang on I'm still typing
gurkang
gurkangOPā€¢2y ago
haha šŸ˜„
JakenVeina
JakenVeinaā€¢2y ago
services => services
.Add(services => services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => options.TokenValidationParameters = new()
{
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuer = false,
ValidateIssuerSigningKey = true
})
.PostConfigureWith<JwtBearerOptions, IOptions<AuthenticationConfiguration>>(
(options, authenticationConfiguration) => options
.TokenValidationParameters.IssuerSigningKey
= new SymmetricSecurityKey(
Encoding.ASCII.GetBytes(authenticationConfiguration.Value.TokenSignatureSecret))();
services => services
.Add(services => services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => options.TokenValidationParameters = new()
{
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuer = false,
ValidateIssuerSigningKey = true
})
.PostConfigureWith<JwtBearerOptions, IOptions<AuthenticationConfiguration>>(
(options, authenticationConfiguration) => options
.TokenValidationParameters.IssuerSigningKey
= new SymmetricSecurityKey(
Encoding.ASCII.GetBytes(authenticationConfiguration.Value.TokenSignatureSecret))();
AddAuthentication() returns an AuthenticationBuilder that lets you add schemes an AddJwtBearer() adds the JwtBearer scheme the parameter JwtBearerDefaults.AuthenticationScheme is the "default" scheme to be used by any requests/endpoints that don't specify which one should be used I.E. in your case, all of them cause you're only gonna have one there's a lot of stuff available on JwtBearerOptions and they're all pretty self-explanatory the only thing I'm configuring here is how the tokens are validated cause I'm using standard tokens of note I am using my own made-up secret to digitally sign tokens, and that's what that PostConfigureWith() bit is about the token is loaded through the config system, but the config system isn't ready to use at DI setup time .PostConfigireWith() is the Microsoft.Extensions.Options system, letting me do options changes, as soon as a the DI system is available, so, I'm getting that IOptions<AuthenticationConfiguration> value from DI AuthenticationConfiguration being my own class that ties into Microsoft.Extensions.Configuration so, with this in place, all three of the core auth operations are done on each request, the JwtHandler will look for a token in the request, check if it's valid, and if so, parse all of its claims, and make those claims available on IAuthenticationService you can then pull IAuthenticationService through DI to inspect them yourself or you can configure the AuthorizationMiddleware to inspect them also I left a chunk out here that I use, which is probably worth mentioning one of the most useful extension points of any auth scheme is options.Events
options.Events = new()
{
OnMessageReceived = context =>
{
context.HttpContext.RequestServices.GetRequiredService<IAuthenticationService>()
.OnRequestReceived(context.HttpContext.Request);

return Task.CompletedTask;
},
OnTokenValidated = context =>
{
if (context.Principal is not null)
context.HttpContext.RequestServices.GetRequiredService<IAuthenticationService>()
.OnAuthenticationSucceeded(context.Principal);

return Task.CompletedTask;
}
};
options.Events = new()
{
OnMessageReceived = context =>
{
context.HttpContext.RequestServices.GetRequiredService<IAuthenticationService>()
.OnRequestReceived(context.HttpContext.Request);

return Task.CompletedTask;
},
OnTokenValidated = context =>
{
if (context.Principal is not null)
context.HttpContext.RequestServices.GetRequiredService<IAuthenticationService>()
.OnAuthenticationSucceeded(context.Principal);

return Task.CompletedTask;
}
};
here, I'm doing two things A) the data structure for claims on IAuthenticationService is kinda a pain in the ass if I just wanna know "what's the user id", it's a multi-step process to look up if the claim is there, and then parse it if the value you want isn't a string so, the IAuthenticationService you see there is actually NOT Microsoft.AspNetCore.Authentication.IAuthenticationService, it's a totally separate interface from my own business layer I'm using OnTokenValidated to call my own business logic on each request, which will go ahead and parse out the UserId I care about, and store it for me to easily reuse also, it's more mockable and testable maybe there's a way to customize IAuthenticationService to put your own custom data on there, I've never looked into it B) there's an additional GuildId field that the user gets freedom to pick and change in the UI, at any time, so doesn't belong in the token, but which affects auth operations, so I'm using OnMessageReceived to pick that out of an HTTP header on each request, parse it, and store it, same as UserID just to illustrate, there's a lot of extension points for this middleware, not just for changing config options, but for injecting your own logic and yes, if it came down to it, you could always just make your own AuthenticationHandler<T> now for the "sign-in" side of things again, maybe it could make sense to run this through AuthenticationMiddleware, and you'd probably do that by subclassing your own AuthenticationHandler<T> that implements ISignInAuthenticationHandler<T> and/or ISignOutAuthenticationHandlerT<> and then you'd configure the middleware to use JwtBearer as the scheme for Authentication, Challenge and Forbid operations, and use your custom scheme for SignIn and/or SignOut in my mind, it made way more sense, and was simpler, to just implement a couple HTTP endpoints this project uses gRPC, and I setup 2 endpoints StartLogin and CompleteLogin StartLogin literally just builds and returns the OAuth URL that the client should redirect to since I can then set it up to pull the OAuth clientId from one place in server config
var response = new StartLoginResponse(
QueryHelpers.AddQueryString(
new Uri(Constants.BaseURL, "oauth2/authorize").AbsoluteUri,
new Dictionary<string, string?>()
{
["client_id"] = _discordConfiguration.Value.ClientId,
["redirect_uri"] = request.RedirectUri,
["response_type"] = "code",
["scope"] = "identify",
["state"] = request.State
}));
var response = new StartLoginResponse(
QueryHelpers.AddQueryString(
new Uri(Constants.BaseURL, "oauth2/authorize").AbsoluteUri,
new Dictionary<string, string?>()
{
["client_id"] = _discordConfiguration.Value.ClientId,
["redirect_uri"] = request.RedirectUri,
["response_type"] = "code",
["scope"] = "identify",
["state"] = request.State
}));
the fun part is the CompleteLogin endpoint, which receives the temporary OAuth access token, validates it, retrieves user info, and builds our own token, precisely as you want to do none of this involves the Authentication middleware, cause requests aren't being authentication it's the server operating as the client against the OAuth provider
var tokenGrantResult = await _discordClient.PostAsync<OAuthTokenGrant>(
"oauth2/token",
requestBuilder => requestBuilder
.AddContent(new StringContent(_discordConfiguration.Value.ClientId), "client_id")
.AddContent(new StringContent(_discordConfiguration.Value.ClientSecret), "client_secret")
.AddContent(new StringContent(request.Code), "code")
.AddContent(new StringContent(request.RedirectUrl), "redirect_uri")
.AddContent(new StringContent("authorization_code"), "grant_type"),
ct: cancellationToken);
if (!tokenGrantResult.IsSuccess)
return new LoginFailure(tokenGrantResult.Error.Message);

var authenticationHeader = new AuthenticationHeaderValue(
tokenGrantResult.Entity.TokenType,
tokenGrantResult.Entity.AccessToken)
.ToString();

var getUserResult = await _discordClient.GetAsync<User>(
"users/@me",
request => request.AddHeader("Authorization", authenticationHeader),
ct: cancellationToken);
if (!getUserResult.IsSuccess)
return new LoginFailure(getUserResult.Error.Message);

RevokeTokens(tokenGrantResult.Entity.AccessToken, tokenGrantResult.Entity.RefreshToken);
var tokenGrantResult = await _discordClient.PostAsync<OAuthTokenGrant>(
"oauth2/token",
requestBuilder => requestBuilder
.AddContent(new StringContent(_discordConfiguration.Value.ClientId), "client_id")
.AddContent(new StringContent(_discordConfiguration.Value.ClientSecret), "client_secret")
.AddContent(new StringContent(request.Code), "code")
.AddContent(new StringContent(request.RedirectUrl), "redirect_uri")
.AddContent(new StringContent("authorization_code"), "grant_type"),
ct: cancellationToken);
if (!tokenGrantResult.IsSuccess)
return new LoginFailure(tokenGrantResult.Error.Message);

var authenticationHeader = new AuthenticationHeaderValue(
tokenGrantResult.Entity.TokenType,
tokenGrantResult.Entity.AccessToken)
.ToString();

var getUserResult = await _discordClient.GetAsync<User>(
"users/@me",
request => request.AddHeader("Authorization", authenticationHeader),
ct: cancellationToken);
if (!getUserResult.IsSuccess)
return new LoginFailure(getUserResult.Error.Message);

RevokeTokens(tokenGrantResult.Entity.AccessToken, tokenGrantResult.Entity.RefreshToken);
also, for good measure, I'm immediately telling Discord to revoke the tokens, since I don't need them anymore but I'm doing this asynchronously, without (immediately) awaiting, cause I already have all the info needed to respond to the client which is here
var userId = getUserResult.Entity.ID.Value;
var created = _systemClock.UtcNow;
var expires = created + _authenticationConfiguration.Value.TokenLifetime;

var response = new LoginSuccess(
ticket: new AuthenticationTicket(
userId: userId,
created: created,
expires: expires),
bearerToken: new JwtSecurityTokenHandler().CreateJwtSecurityToken(new SecurityTokenDescriptor()
{
Claims = new Dictionary<string, object?>()
{
[ClaimTypes.NameIdentifier] = userId,
},
Expires = expires.DateTime,
IssuedAt = created.DateTime,
SigningCredentials = new(
new SymmetricSecurityKey(
Encoding.ASCII.GetBytes(
_authenticationConfiguration.Value.TokenSignatureSecret)),
SecurityAlgorithms.HmacSha256Signature)
})
.RawData);

return response;
var userId = getUserResult.Entity.ID.Value;
var created = _systemClock.UtcNow;
var expires = created + _authenticationConfiguration.Value.TokenLifetime;

var response = new LoginSuccess(
ticket: new AuthenticationTicket(
userId: userId,
created: created,
expires: expires),
bearerToken: new JwtSecurityTokenHandler().CreateJwtSecurityToken(new SecurityTokenDescriptor()
{
Claims = new Dictionary<string, object?>()
{
[ClaimTypes.NameIdentifier] = userId,
},
Expires = expires.DateTime,
IssuedAt = created.DateTime,
SigningCredentials = new(
new SymmetricSecurityKey(
Encoding.ASCII.GetBytes(
_authenticationConfiguration.Value.TokenSignatureSecret)),
SecurityAlgorithms.HmacSha256Signature)
})
.RawData);

return response;
JwtSecurityTokenHandler comes from System.IdentityModel.Tokens.Jwt and is ready-made to build basically any kind of JWT you could ever want @GurkanG asleep yet?
gurkang
gurkangOPā€¢2y ago
@AnievNekaj I'm alive and kicking. Just had to go away for a bit haha
JakenVeina
JakenVeinaā€¢2y ago
šŸ˜‰
gurkang
gurkangOPā€¢2y ago
Is there any reason why I couldn't do something like this:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddScheme<AuthenticationSchemeOptions, SupabaseAuthenticationHandler>(JwtBearerDefaults.AuthenticationScheme,
options =>
{
}
);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddScheme<AuthenticationSchemeOptions, SupabaseAuthenticationHandler>(JwtBearerDefaults.AuthenticationScheme,
options =>
{
}
);
And then in the SupabaseAuthenticationHandler :
public class SupabaseAuthenticationHandler: AuthenticationHandler<AuthenticationSchemeOptions>
{
private string ApiKey { get; set; }

public SupabaseAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, string apikey) : base(options, logger, encoder, clock)
{
ApiKey = apikey;
}

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Context.Request.Headers.ContainsKey("Authorization"))
{
return AuthenticateResult.NoResult();
}

var token = Context.Request.Headers["Authorization"];
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", (string?)token);
// var apiKey = builder.Configuration.GetValue<string>("apiKey");
// client.DefaultRequestHeaders.Add("apikey", (string?)apiKey);
// var res = await client.GetFromJsonAsync<UserFromSupabase>(
// builder.Configuration.GetValue<string>("supabaseUrl"));

return AuthenticateResult.Success(new AuthenticationTicket(
new ClaimsPrincipal(
new List<ClaimsIdentity>()),
JwtBearerDefaults.AuthenticationScheme));
}

}
public class SupabaseAuthenticationHandler: AuthenticationHandler<AuthenticationSchemeOptions>
{
private string ApiKey { get; set; }

public SupabaseAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, string apikey) : base(options, logger, encoder, clock)
{
ApiKey = apikey;
}

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Context.Request.Headers.ContainsKey("Authorization"))
{
return AuthenticateResult.NoResult();
}

var token = Context.Request.Headers["Authorization"];
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", (string?)token);
// var apiKey = builder.Configuration.GetValue<string>("apiKey");
// client.DefaultRequestHeaders.Add("apikey", (string?)apiKey);
// var res = await client.GetFromJsonAsync<UserFromSupabase>(
// builder.Configuration.GetValue<string>("supabaseUrl"));

return AuthenticateResult.Success(new AuthenticationTicket(
new ClaimsPrincipal(
new List<ClaimsIdentity>()),
JwtBearerDefaults.AuthenticationScheme));
}

}
JakenVeina
JakenVeinaā€¢2y ago
doesn't really make sense to me you're looking for the Supabase token here? you're expecting that token on EVERY request to your server? and you're going to go check it against Supabase on EVERY request?
gurkang
gurkangOPā€¢2y ago
Well why wouldn't I? How else would you make sure that each and every request is correct and authenticated? Since all requests to this API needs to be authenticated isn't it just smart to check that for every request?
JakenVeina
JakenVeinaā€¢2y ago
no you should not be using a Supabase access token if you're not accessing Supabase resources
gurkang
gurkangOPā€¢2y ago
But I need the token to get the user information though?
JakenVeina
JakenVeinaā€¢2y ago
only once only on initial sign in at least, in my opinion do you really gain any practical amount of security by validating CONSTANTLY for one, is Supabase even gonna LET you spam them this often? I'll wager you'd end up getting ratelimited and the security you gain by doing this is "I'll know if Supabase locks the account or revokes the token before its expiration" is that even a thing that can happen?
gurkang
gurkangOPā€¢2y ago
I haven't have a problem yet with other systems getting rate limited when doing a similar setup. But those backends I've written in node/express. I mean, if you think about supabase as just an auth provider and it all does is acts like an extra microservice that "takes in an JWT, checks if it's correct, then returns user if it's not" is this really a big error?
JakenVeina
JakenVeinaā€¢2y ago
not terribly, it's just inefficient at the VERY least, you can cache token validations the token is just text, validate it once, and that result isn't gonna change, unless Supabase allows for revokation consider this what I'm describing is precisely what Supabase does they don't do their own auth, they farm it out to Microsoft, Discord, Facebook, etc. the token you get to access supabase resources is not a token from any of those providers, it's their own, that they've exchanged if I'm reading this right, anyway in any case, if you really do want to call to a third party for every auth, and/or that's the way Supabase expects you to do it, what you proposed is pretty much the way to go you also may not need to subclass your own AuthenticationHandler<T> for it check if there's any hooks in JwtBearerEvents you can use at a glance, I'd say you can just use the OnMessageReceived event, which takes an async handler, and gives you the ability to call .Fail() or .Success() on the context I.E. you can extract the token and validate it there
gurkang
gurkangOPā€¢2y ago
Okay I'll give it a go and see if I can get it working šŸ™‚ Thanks for the help! Appreciate it a lot
JakenVeina
JakenVeinaā€¢2y ago
o7
Accord
Accordā€¢2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server