haskell-servant / servant

Servant is a Haskell DSL for describing, serving, querying, mocking, documenting web applications and more!
https://docs.servant.dev/
1.83k stars 413 forks source link

What is the best practice to redirect to another page with login processing? #1150

Closed falsandtru closed 5 years ago

falsandtru commented 5 years ago

Redirecting to the thank you page is important to prevent the double submit problem. Could you provide the official solution?

phadej commented 5 years ago

The throwError err302 { ... } is the best we have at the moment, AFAIK.

falsandtru commented 5 years ago

Looks like I have to add a header to the succeeded response. How can I redirect the request with the following processing?

   case mApplyCookies of
     Nothing           -> throwError err401
     Just applyCookies -> return $ applyCookies NoContent

https://github.com/haskell-servant/servant-auth

falsandtru commented 5 years ago

@phadej To define and use Redirection response type instead of NoContent may be a smart solution.

MasseRelex commented 5 years ago

There is also a stackoverflow answer related to this: https://stackoverflow.com/questions/46789241/redirections-in-servant and this gist

Basically something like this (lots have been redacted)

type PostRedirect (code :: Nat) loc = Verb 'POST code '[JSON] (Headers '[Header "Location" loc) NoContent)

type API = Capture "q" String :> PostRedirect 301 String

server :: Server API
server query = redirect ("https://example.com/" ++ query)
alpmestan commented 5 years ago

This indeed works if you want to redirect if everything goes well. When you want to do conditional redirects and what not it becomes trickier and you pretty much need to resort to the throwError err30x [...] approach, for now.

falsandtru commented 5 years ago

Looks like your solution can't be applied to cookie application. Could you show me an example working with authentication? I'm trying to refactor the official example(https://github.com/haskell-servant/servant/issues/1150#issuecomment-471804907) but redirect function can't seem to be possible to use for that example.

falsandtru commented 5 years ago

I resolve as follows:

...
  :<|>  "login" :> ReqBody '[FormUrlEncoded] Login
                :> Verb 'POST 308 '[PlainText]
                                  (Headers '[ Header "Location" String
                                            , Header "Set-Cookie" SetCookie
                                            , Header "Set-Cookie" SetCookie]
                                           NoContent)
...
login :: CookieSettings -> JWTSettings -> Login -> Handler (Headers '[ Header "Location" String
                                                                     , Header "Set-Cookie" SetCookie
                                                                     , Header "Set-Cookie" SetCookie]
                                                                    NoContent)
login cookieSettings jwtSettings Login { name, token } = do
   mApplyCookies <- liftIO $ acceptLogin cookieSettings jwtSettings User { name, email = "" }
   case mApplyCookies of
     Nothing           -> throwError err401
     Just applyCookies -> return (addHeader "" $ applyCookies NoContent :: Headers '[Header "Location" String, Header "Set-Cookie" SetCookie, Header "Set-Cookie" SetCookie] NoContent)

I'll try to use this. Thanks for your help.