servant-auth
servant-auth copied to clipboard
JWT Instances for servant-foreign
It would be handy to have instances for HasForeign from servant-foreign
Something along the lines of this, I'm not sure about the correctness of using Text to represent JWTs, but I believe this is along the right lines. It could probably be generalised a bit too so it's not fixed to exactly '[JWT]
instance forall lang ftype api.
( HasForeign lang ftype api
, HasForeignType lang ftype Text
)
=> HasForeign lang ftype (Auth '[JWT] a :> api) where
type Foreign ftype (Auth '[JWT] a :> api) = Foreign ftype api
foreignFor lang Proxy Proxy subR =
foreignFor lang Proxy (Proxy :: Proxy api) req
where
req = subR{ _reqHeaders = HeaderArg arg : _reqHeaders subR }
arg = Arg
{ _argName = PathSegment "Authorization"
, _argType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy Text)
}
Looks good. I'll start a new package for it.
My inclination is to have the instance be lang ftyp (Auth (JWT ': etc) a :> api), so that for most cases, the first authentication mechanism is picked, except for JS (or purescript etc.) where we require that Cookie be available somewhere in the list. The idea is that then you could derive authentication with sane defaults both for browser and "normal" languages.
This kind of screws over Node, but I'm okay punting on that problem.
@jkarni What is the package? :)
I've had another go at this, with some success. Note that this requires GHC > 8.0 (but it will likely be possible to support earlier versions with the same principle). I'm not sure how it interacts with haskell-servant/servant-auth#31 or haskell-servant/servant#706. I've tested with purescript-bridge and purescript-servant-support (though I haven't tested the generated code itself, yet).
It would be good to see some progress, as part of the integration of servant-auth.
My inclination is to have the instance be lang ftyp (Auth (JWT ': etc) a :> api), so that for most cases, the first authentication mechanism is picked, except for JS (or purescript etc.) where we require that Cookie be available somewhere in the list. The idea is that then you could derive authentication with sane defaults both for browser and "normal" languages.
This approach prefers Cookie auth over straight up JWT; I'm not sure how one would go about handling both.
The closed type family generates the header name, and in principle another would be used to automatically ensure that the Bearer: prefix is added in the case of JWT mode.
type family TokenHeaderName xs :: Symbol where
TokenHeaderName (Cookie ': xs) = "X-XSRF-TOKEN"
TokenHeaderName (JWT ': xs) = "Authorization"
TokenHeaderName (x ': xs) = TokenHeaderName xs
TokenHeaderName '[] = TypeError (Text "Neither JWT nor cookie auth enabled")
instance
( TokenHeaderName auths ~ header
, KnownSymbol header
, HasForeignType lang ftype Token
, HasForeign lang ftype sub
)
=> HasForeign lang ftype (Auth auths a :> sub) where
type Foreign ftype (Auth auths a :> sub) = Foreign ftype sub
foreignFor lang Proxy Proxy req =
foreignFor lang Proxy subP $ req & reqHeaders <>~ [HeaderArg arg]
where
arg = Arg
{ _argName = PathSegment . T.pack $ symbolVal @header Proxy
, _argType = token
}
token = typeFor lang (Proxy @ftype) (Proxy @Token)
subP = Proxy @sub
Annoyingly the TypeError construct seems to require UndecidableInstances (this is a longstanding GHC bug) but it straightforwardly terminates.
(Edit: typo with TokenHeaderName in instance constraint)
I'd like to suggest to keep the token header type family open, so as to support other auth schemes, which is the end goal of servant-auth. Similarly, should we always assume auth schemes use a header?
Regardless, thanks already! We should indeed do our best to get this into a mergeable shape :)
This is surely not the only place in the library where that might be assumed. I've no knowledge of any other schemes, so sadly I can't answer on that. I suspect the Symbol approach may work rather well, as whichever form of authentication is used, the identifiers must be representable as text.
Anyway, keep it up. And I'll contribute any useful advances, here.
@dbaynard oh it may not be indeed! I didn't mean to criticize your work. Certainly not. But in the context of the Google Summer of Code / Haskell.org Summer of Code, we might put together a proposal to implement a whole bunch of common auth schemes (see this ticket), so anything that makes that a little bit easier is welcome =)
Relevant: https://github.com/mattjbray/servant-elm/issues/11
Oh and there is a WIP branch from @jkarni in 2017: https://github.com/haskell-servant/servant-auth/commit/7bef1f994a429b053ea507b054cf25637f5c1dcb
Oh and there is a WIP branch from @jkarni in 2017: 7bef1f9
I've found this while trying to use servant-elm with servant-auth - for now i use the orphan instances from the commit above to be able to test servant-elm. Is there a better solution as of today?