This felt like a better way of defining public routes as it gave more control. Adding REGEX_PATHS alongside PUBLIC_PATHS allowed for a more flexible and fine grained auth check.

Using the following code in the public_path method worked really well.

private def public_path?(path)
    return true if PUBLIC_PATHS.includes?(path)
    REGEX_PATHS.any? { |word| path =~ word }
end

We are checking if any of the PUBLIC_PATHS or REGEX_PATHS matches the path from the context.request. If there is a match this will return true and move on to the next middleware.

Here is the entire authenticate.cr file for you to see what the PUBLIC_PATHS and REGEX_PATHS definition looks like.

class HTTP::Server::Context
  property current_user : User?
end

class Authenticate < Amber::Pipe::Base
  PUBLIC_PATHS = ["/", "/posts", "/signin", "/session", "/signup", "/registration"]
  REGEX_PATHS = [
    /^\/posts\/([0-9]+)$/,
  ]

  def call(context)
    user_id = context.session["user_id"]?
    if user = User.find user_id
      context.current_user = user
      call_next(context)
    else
      return call_next(context) if public_path?(context.request.path)
      context.flash[:warning] = "Please Sign In"
      context.response.headers.add "Location", "/signin"
      context.response.status_code = 302
    end
  end

  private def public_path?(path)
    return true if PUBLIC_PATHS.includes?(path)
    REGEX_PATHS.any? { |word| path =~ word }
  end
end

I hope this has helped you in Protecting your routes in the Amber framework. And make sure you come and join us in the Amber gitter channel.