Add Auth Middleware

Implement an auth middleware for protecting any sensitive APIs.

Now that your login and logout workflows are functional, let's add a middleware to protect your C# server's Session endpoint any other protected APIs in your backend.


Create Auth Middleware

To verify that frontend requests to your Session endpoint have an authenticated session, you can add an auth middleware that checks for a valid session cookie. Here's an example middleware function (e.g. AuthMiddleware.cs):

// AuthMiddleware.cs

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Wristband.AspNet.Auth;

public class AuthMiddleware
{
    private readonly RequestDelegate _next;
    public AuthMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext context, IWristbandAuthService wristbandAuth)
    {
        // Skip authentication for endpoints without the RequireWristbandAuth attribute
        if (context.GetEndpoint()?.Metadata.GetMetadata<RequireWristbandAuthAttribute>() == null)
        {
            await _next(context);
            return;
        }

        // Verify authentication
        if (!await IsAuthenticated(context))
        {
            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            return;
        }

        // Save the session in order to "touch" it and extend the session expiration window.
        await context.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme)),
            new AuthenticationProperties { IsPersistent = true });
        await _next(context);
    }

    private async Task<bool> IsAuthenticated(HttpContext context)
    {
        var authResult = await context.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        return authResult.Succeeded && 
            authResult.Principal != null && 
            context.User.FindFirst("isAuthenticated")?.Value == "true";
    }
}

Now configure your auth middleware in Program.cs right before any routes that must be protected with an authenticated session:

// Program.cs

...  
app.UseAuthentication();  
app.UseMiddleware<AuthMiddleware>(); // Place after cookie authentication middleware

// Protected routes below...



Protect the Session Endpoint

Apply the RequireWristbandAuth attribute to your previously created Session endpoint to ensure it's protected by the authentication middleware.

Since the Session endpoint will now be protected by middleware, we can also remove the redundant session validation check in the controller code.

// AuthRoutes.cs
using System.Security.Claims;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Wristband.AspNet.Auth;

...

// Session endpoint
app.MapGet("/session", (HttpContext httpContext) =>
{
    var user = httpContext.User;
  
    //
    // You can additionally make other API calls to gather any other data you might want to
    // return to your frontend.
    //

    return Results.Ok(new
    { 
        IsAuthenticated = user.FindFirst("isAuthenticated")?.Value == "true",
        Email = user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value ?? string.Empty,
    });
})
.WithMetadata(new RequireWristbandAuth());

...

๐Ÿ“˜

Put All Protected Routes Behind Middleware

Protect all authenticated routes - not just the Session endpoint - with the auth middleware. Add this middleware to any endpoint that requires an authenticated user session.

The Login, Callback, and Logout Express routes are meant to be accessed by unauthenticated users. Avoid sticking the middleware in front of those routes.


Whatโ€™s Next

Now that your C# server is protected, let's handle 401 HTTP responses in the frontend.