Reputation: 9440
I have an ASP.NET Core Blazor web app, which uses Identity for authentication. The server project's Program.cs
contains the following (rather trivial) endpoint...
app.MapGet("/testapi-time", [Authorize] () =>
DateTime.Now.ToLongTimeString());
If I try to access this in a browser when not authenticated, I get redirected to the app's log-in page. Given that an API endpoint is not usually expected to be accessed from a browser, this seems odd. I would have expected it to return a 401 instead.
I know I can do the following...
app.MapGet("/testapi-time", (HttpContext httpContext) =>
!httpContext.User.Identity.IsAuthenticated
? Results.Unauthorized()
: Results.Ok(DateTime.Now.ToLongTimeString()));
...however that's a lot of boilerplate code for something that I would have expected the framework to do for me.
Am I doing something wrong, or is this the way it's supposed to work?
Upvotes: 2
Views: 106
Reputation: 1448
Whilst I'm not sure why the API doesn't return a 401, there is a reasonably easy way to get around it.
First create a custom attribute...
public class ApiAuthoriseAttribute : Attribute { }
This can be empty, as we're only going to use it to identify which endpoints need authentication.
Next add some middleware that will do the actual work...
public class ApiAuthoriseMiddleware(RequestDelegate next) : Attribute {
public async Task InvokeAsync(HttpContext httpContext) {
Endpoint? endpoint = httpContext.GetEndpoint();
if (endpoint is not null
&& endpoint.Metadata.Any(md => md.GetType() == typeof(ApiAuthoriseAttribute))
&& !(httpContext.User.Identity?.IsAuthenticated ?? false)) {
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
await httpContext.Response.Body
.WriteAsync("\"You are not authorised to access this resource\""u8.ToArray())
.ConfigureAwait(false);
return;
}
await next(httpContext).ConfigureAwait(false);
}
}
Assuming that endpoint
is not null (which it never has been in my experience, but given that the GetEndpoint()
method returns a nullable value, I figure it's adding the check), then this checks if the endpoint in question is decorated with your custom attribute, and if the user is authed. If not, it returns a 401 with a message. If they are authed, it simply passes the execution along to the next item in the pipeline.
You then need to register this middleware in Program.cs
...
app.UseAuthentication();
app.UseMiddleware<ApiAuthoriseMiddleware>();
app.UseAuthorization();
Note that you must register it AFTER the call to UseAuthentication()
. If you register it earlier, then IsAuthenticated
will always be false.
Upvotes: 0