Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
342 views
in Technique[技术] by (71.8m points)

authentication - How to add basic auth to Scotty middleware?

I'm currently making a Scotty API and I couldn't find any examples of basicAuth implementations (Wai Middleware HttpAuth).

Specifically, I want to add basic auth headers (user, pass) to SOME of my endpoints (namely, ones that start with "admin"). I have everything set up, but I can't seem to make the differentiation as to which endpoints require auth and which ones don't. I know I need to use something like this, but it uses Yesod, and I wasn't able to translate it to Scotty.

So far, I have this:

routes :: (App r m) => ScottyT LText m ()
routes = do
  -- middlewares
  middleware $ cors $ const $ Just simpleCorsResourcePolicy
    { corsRequestHeaders = ["Authorization", "Content-Type"]
    , corsMethods = "PUT":"DELETE":simpleMethods
    }
    
  middleware $ basicAuth 
      (u p -> return $ u == "username" && p == "password") 
      "My Realm" 
  
  -- errors
  defaultHandler $ str -> do
    status status500
    json str

  -- feature routes
  ItemController.routes
  ItemController.adminRoutes
  
  -- health
  get "/api/health" $
    json True

But it adds authentication to all my requests. I only need it in some of them.

Thank you so much!

question from:https://stackoverflow.com/questions/65948172/how-to-add-basic-auth-to-scotty-middleware

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can use the authIsProtected field of the AuthSettings to define a function Request -> IO Bool that determines if a particular (Wai) Request is subject to authorization by basic authentication. In particular, you can inspect the URL path components and make a determination that way.

Unfortunately, this means that the check for authorization is completely separated from the Scotty routing. This works fine in your case but can make fine-grained control of authorization by Scotty route difficult.

Anyway, the AuthSettings are the overloaded "My Realm" string in your source, and according to the documentation, the recommended way of defining the settings is to use the overloaded string to write something like:

authSettings :: AuthSettings
authSettings = "My Realm" { authIsProtected = needsAuth }

That looks pretty horrible, but anyway, the needsAuth function will have signature:

needsAuth :: Request -> IO Bool

so it can inspect the Wai Request and render a decision in IO on whether or not the page needs basic authentication first. Calling pathInfo on the Request gives you a list of path components (no hostname and no query parameters). So, for your needs, the following should work:

needsAuth req = return $ case pathInfo req of
  "admin":_ -> True   -- all admin pages need authentication
  _         -> False  -- everything else is public

Note that these are the parsed non-query path components, so /admin and /admin/ and /admin/whatever and even /admin/?q=hello are protected, but obviously /administrator/... is not.

A full example:

{-# LANGUAGE OverloadedStrings #-}

import Web.Scotty
import Network.Wai.Middleware.HttpAuth
import Data.Text ()   -- needed for "admin" overloaded string in case
import Network.Wai (Request, pathInfo)

authSettings :: AuthSettings
authSettings = "My Realm" { authIsProtected = needsAuth }

needsAuth :: Request -> IO Bool
needsAuth req = return $ case pathInfo req of
  "admin":_ -> True   -- all admin pages need authentication
  _         -> False  -- everything else is public

main = scotty 3000 $ do
  middleware $ basicAuth (u p -> return $ u == "username" && p == "password") authSettings
  get "/admin/deletedb" $ do
    html "<h1>Password database erased!</h1>"
  get "/" $ do
    html "<h1>Homepage</h1><p>Please don't <a href=/admin/deletedb>Delete the passwords</a>"

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...