New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWT Instances for servant-foreign #8

Open
expipiplus1 opened this Issue Oct 27, 2016 · 8 comments

Comments

Projects
None yet
6 participants
@expipiplus1

expipiplus1 commented Oct 27, 2016

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

This comment has been minimized.

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

This comment has been minimized.

Contributor

sordina commented May 3, 2017

@jkarni What is the package? :)

@jkarni jkarni referenced this issue Oct 18, 2017

Open

Prepare to be official #64

1 of 4 tasks complete
@dbaynard

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

dbaynard commented Jan 13, 2018

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

This comment has been minimized.

Contributor

alpmestan commented Jan 14, 2018

@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 from Instances for servant-foreign to JWT Instances for servant-foreign Jun 18, 2018

@domenkozar

This comment has been minimized.

Collaborator

domenkozar commented Oct 2, 2018

@domenkozar

This comment has been minimized.

Collaborator

domenkozar commented Nov 13, 2018

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment