#Astro global in middleware?

10 messages · Page 1 of 1 (latest)

vast oak
#

In my middleware I have a generic unauthorized function that handles returning responses based on the request's "accept" header. For example, a request that expects json will get json, but a request that expects html will get a 302 back to the web home page, etc. In my middleware handlers, I've destructured the context, so now when it comes to invoking my unauthorized helper, I have to explicitly pass in things that I may need, like the request, the redirect helper, etc, and that seems a bit messy. I'm hoping to get some ideas on how to approach this better. Some example code

const unauthorized = (request: Request, redirect: AstroSharedContext['redirect']) => {
  if (request.headers.get('accept')?.includes('application/json')) {
    return new Response(
      JSON.stringify({
        error: 'invalid_token',
        error_description: 'The access token is missing, invalid, or expired'
      }),
      {
        status: 401,
        headers: {
          'Content-Type': 'application/json',
          'WWW-Authenticate':
            'Bearer error="invalid_token", error_description="The access token is missing, invalid, or expired"'
        }
      }
    );
  } else {
    return redirect('/', 302);
  }
};

const ensureAuthenticated = defineMiddleware(async ({ url, locals, redirect, request }, next) => {
  const authenticatedRoutes = ['/protected', '/auth/logout', '/api/customer'];

  if (authenticatedRoutes.some((route) => url.pathname.startsWith(route))) {
    if (!locals.authenticated) {
      return unauthorized(request, redirect);
    }
  }

  return next();
});
brittle loom
#
const unauthorized = (request: Request, redirect: AstroSharedContext['redirect']) => {
  if (request.headers.get('accept')?.includes('application/json')) {
    return new Response(
      JSON.stringify({
        error: 'invalid_token',
        error_description: 'The access token is missing, invalid, or expired'
      }),
      {
        status: 401,
        headers: {
          'Content-Type': 'application/json',
          'WWW-Authenticate':
            'Bearer error="invalid_token", error_description="The access token is missing, invalid, or expired"'
        }
      }
    );
  } else {
    return redirect('/', 302);
  }
};

const ensureAuthenticated = defineMiddleware(async (context, next) => {
  const { url, locals, redirect, request } = context;
  const authenticatedRoutes = ['/protected', '/auth/logout', '/api/customer'];

  if (authenticatedRoutes.some((route) => url.pathname.startsWith(route))) {
    if (!locals.authenticated) {
      return unauthorized(request, redirect);
    }
  }

  return next();
});

This would be my approach. Just destructure the context after the fact - use methods as you need them!

vast oak
#

doesnt it seem messy explicitly passing bits into the unauthorized fn?

brittle loom
#

Not really, I'd say that's fine

#

It's readable enough, makes it reusable

vast oak
#

alright, fair enough. thank you

brittle loom
#

Stuff like this is always down to how comfortable you are with the level of abstraction and readability. Sure, there's "code standards" and whatever, but if this works for you just go for it!

vast oak
#

yeah, all good. this is our first time using astro and its a big project. didn't want to go down the wrong path from the start

brittle loom
#

Eh, what I've come to accept is that there is

  1. No correct way to do this 50% of the time and
  2. Sometimes you have to reconsider things down the line, shift your focus and adapt, it's part of the process
#

So yeah no worries, hope this helped