Skip to content
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

JS code generation fails when Api contains BasicAuth #672

Open
kseo opened this issue Jan 17, 2017 · 13 comments
Open

JS code generation fails when Api contains BasicAuth #672

kseo opened this issue Jan 17, 2017 · 13 comments

Comments

@kseo
Copy link

kseo commented Jan 17, 2017

writeJSForAPI fails to type check after I add BasicAuth to my api.

/Users/kseo/KodeBoxProjects/kodebox-server/jsgen/Main.hs: 21, 15
• No instance for (servant-foreign-0.8.1:Servant.Foreign.Internal.HasForeign
                     NoTypes
                     Servant.API.ContentTypes.NoContent
                     (Servant.API.BasicAuth.BasicAuth
                        "kodebox-realm" Kodebox.Server.Types.Session
                      Servant.API.Sub.:> Kodebox.Server.Api.PrivateApi))
    arising from a use of ‘writeJSForAPI’
• In the expression:
    writeJSForAPI
      restApi vanillaJS (joinPath [outputDir, "Kodebox.js"])
  In an equation for ‘writeJSCode’:
      writeJSCode
        = writeJSForAPI
            restApi vanillaJS (joinPath [outputDir, "Kodebox.js"])
@arianvp
Copy link
Member

arianvp commented Jan 17, 2017

Hmm I remember I implemented this at some point, but perhaps it never ended up in master.

@arianvp arianvp self-assigned this Jan 17, 2017
@arianvp arianvp added this to the 0.10 milestone Jan 17, 2017
@phadej
Copy link
Contributor

phadej commented Jan 19, 2017

Related/Duplicates: #463 #672 #465

@arianvp you have an opportunity to close 3 issues with one PR! See also #682

@arianvp
Copy link
Member

arianvp commented Jan 31, 2017

@phadej Due to exams wasn't able to finish this week. however I don't think this should block the 0.10 release. We can do a 0.10.1 release as the changes are non-breaking

@arianvp arianvp removed this from the 0.10 milestone Jan 31, 2017
@phadej
Copy link
Contributor

phadej commented Jan 31, 2017

@arianvp ack.

@joaomilho
Copy link

Just had the same issue. How can I help?

@arianvp
Copy link
Member

arianvp commented Feb 9, 2017

A HasForeign instance has to be written for the BasicAuth stuff. Which generates enough information such that servant-js can write auth client code.

Which should be the trivial part: https://stackoverflow.com/questions/5507234/how-to-use-basic-auth-with-jquery-and-ajax

I am a bit busy, so I haven't gotten around doing it yet.

@fieldstrength
Copy link

I've just been bitten by this one as well. I'm also willing to help...

Has it just been forgotten for a while? :) It sounded like it was close to being solved.

@arianvp
Copy link
Member

arianvp commented Mar 27, 2017

Yes I haven't had the time to implement it yet. It is not a hard fix I think. If you want to take a shot at it, i'm willing to help! It shouldn't be more than 20 lines of code I assume. We just need to add that extra HasForeign instance that adds the neccesery javascript code to make a basicauth call.

Which in jQuery is:

beforeSend: function (xhr) {
    xhr.setRequestHeader ("Authorization", "Basic " + btoa(username + ":" + password));
},

but finding an equivalent for axios and angular and plain XHR should not be hard. we just need to set the request header. We already do this for the Header HasForeign instance which you can look at for inspiration

@mcgizzle
Copy link

mcgizzle commented Aug 5, 2018

Hey just wondering what the status on the issue is? I am using Generalized Authentication provided by servant-auth-server and I am receiving the following error:

 • No instance for (servant-foreign-0.11.1:Servant.Foreign.Internal.HasForeign
                         servant-foreign-0.11.1:Servant.Foreign.Internal.NoTypes
                         NoContent
                         (Auth auths0 AuthUser :> AdminProtected))
        arising from a use of ‘writeJSForAPI’
    • In the expression:
        writeJSForAPI
          (Proxy :: Proxy (UserAPI auths)) vanillaJS "./assets/api.js"
      In an equation for ‘generateJavaScript’:
          generateJavaScript
            = writeJSForAPI
                (Proxy :: Proxy (UserAPI auths)) vanillaJS "./assets/api.js"

Ideally I would like to use Axios instead of vanilla.

@mcgizzle
Copy link

mcgizzle commented Aug 5, 2018

After some searching and hacking I appear to have found a solution. Creating the following instance for Auth does the trick.

instance forall lang ftype api auths v. ( HasForeign lang ftype api
                                        , HasForeignType lang ftype T.Text
         ) =>
         HasForeign lang ftype (Auth auths v
                                :> api) where
  type Foreign ftype (Auth auths v
                      :> 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 T.Text)
          }

Now I'm pretty new to type level programming so a lot of this is going over my head, I just found a very similar solution online and fought with compiler until the types matched up.

The only issue with this solution is the fact that it produces:

Authorization: <key> instead of the desired Authorization: Bearer <key>

I cannot figure out what to change to achieve my desired result. My current solution is to just pass Bearer <> key to the javascript functions, but this doesn't feel right. Any help toward a solution to this/ helping my understanding of the type level stuff would be greatly appreciated.

Also, should the above instance be included by default?

@alpmestan
Copy link
Contributor

@domenkozar Is it always the case that the Authorization header is used? Do we always want Bearer too? And more generally, is the above code a good start for supporting the new auth stuffs in -foreign?

@mcgizzle
Copy link

mcgizzle commented Aug 8, 2018

For reference for anyone attempting this in the future the below change is all that is needed to add Bearer to the generated code.

      req = subR {_reqHeaders = ReplaceHeaderArg arg "Bearer {Authorization}" : _reqHeaders subR}
  

In relation to supporting new auth stuffs in foreign:

Would it be possible to add another (optional) piece of information to the Auth data type that would resemble the scheme of the authorization?

For example: data Auth authScheme (auths :: [*]) val

Could this authScheme value be used to pattern match for the instances and add different behaviour? Or conversely be used a function to determine the behaviour?

I might be way off here but this would be something I would be very interested in working on if I could receive a few pointers along the way.

@odanoburu
Copy link

for basic authentication I managed to get away with this orphan instance:

instance (HasForeign lang ftype api,  HasForeignType lang ftype Text) => HasForeign lang ftype (BasicAuth a b :> api) where
  type Foreign ftype (BasicAuth a b :> api) = Foreign ftype api
  foreignFor lang proxy1 Proxy subR = foreignFor lang proxy1 (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)
          }

(I've never done type-level programming with Haskell, this is largely an adaptation of the code by @mcgizzle and from sordina/servant-options#3)

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

No branches or pull requests

8 participants