Skip to content
This repository has been archived by the owner on Oct 29, 2021. It is now read-only.

JWT Instances for servant-foreign #8

Open
expipiplus1 opened this issue Oct 27, 2016 · 9 comments
Open

JWT Instances for servant-foreign #8

expipiplus1 opened this issue Oct 27, 2016 · 9 comments

Comments

@expipiplus1
Copy link

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)
        }
@jkarni
Copy link
Member

jkarni commented Mar 1, 2017

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.

@sordina
Copy link
Contributor

sordina commented May 3, 2017

@jkarni What is the package? :)

@jkarni jkarni mentioned this issue Oct 18, 2017
4 tasks
@dbaynard
Copy link

dbaynard commented Jan 13, 2018

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 #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)

@alpmestan
Copy link
Contributor

alpmestan commented Jan 13, 2018

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 :)

@dbaynard
Copy link

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.

@alpmestan
Copy link
Contributor

@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 =)

@domenkozar domenkozar changed the title Instances for servant-foreign JWT Instances for servant-foreign Jun 18, 2018
@domenkozar
Copy link
Collaborator

Relevant: haskell-servant/servant-elm#11

@domenkozar
Copy link
Collaborator

Oh and there is a WIP branch from @jkarni in 2017: 7bef1f9

@ikervagyok
Copy link

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?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants