Can phx.gen.auth be used for API authentication?

Just curious if the basic auth framework generated by phx.gen.auth can be ‘easily’ converted to work with a JSON api.

Can you expand on what you mean by “work with a JSON API”? I can think of two scenarios that meet that description and they have different answers for your question.

If you mean “connected to a JS frontend via JSON-formatted requests”, the conversion wouldn’t need to change that much. The biggest change might be with the details of where authentication data is in the request; best-practice with JSON APIs would be a bearer token, versus the cookie-based session used by default.

If you mean “as an authentication system for a JSON API called by other programs” (either via HTTP Basic or API key), the generated code would be a lot less useful.

Ah yeah sorry I can see how that would be confusing. I mean the former - connected to a JS frontend. I’ve read in a few different places how JWT’s are apparently subjectively worse than sessions. Thoughts on that? Why wouldn’t the cookie-based session be good practice?

JWT is a token that allows you to maintain user sessions. So what do you mean that they are worse than sessions?

Cookie is designed to work without the JS frontend. By default, cookie session in phoneix is http only (not accessible by the js) and unencrypted (but signed so is tamper-proof). If you log in via conventional http that it is fine; if you want to have fancy login in javascript driven frontend, then it is not as easy to work with.

A bearer token is an opaque token, whereas a JWT token in the cookie session contains trusted data. With a bearer token you keep all session data server side in a ETS table or something, so it is safer and easier to work with in js.

4 Likes

Could you elaborate on this please? I think whether we work with cookies or not it doesn’t matter? As long as the server has an appropriate success/failure response for its login service? I guess you see something I don’t see:)

phx.gen.auth stores it in DB. Storing the sessions in ETS wouldn’t allow having long lasting sessions (e.g. 60 days default for phx.gen.auth when remember-me is checked) without a huge cost in memory? Anyway it might be an option indeed but interesting that you mentioned that one as an example instead of some relational DB.

Without a concrete design I cannot comment anymore. You probably have some requirements that we don’t know, so no one else can design it for you. If you have a plan, but want a second opinion, then please post your plan first.

I’d like to latch on to this question if I may.

My plan is to have my users sign up and log in using standard phoenix generated html. Essentially a customised version of the pages generated by:

mix pix.gen.auth Accounts User users

While they are logged in, users will interact on the site through a Vue Single Page Application which fetches and updates data using an API written in phoenix served from the same site.

What I’ve managed to do so far is to

  • start with a phx.new project (i.e. with ecto and html),
  • Vue-enabled it at the hand of Caleb Weeks’ blog post and Steven Pallen’s project,
  • added authentication using phx.gen.auth and
  • added a basic API added with phx.gen.json.

What I now need to figure out, with your help, is:

  • where to find the token on the vue client side,
  • how to get the token into the API call (I’m using axios),
  • how to retrieve- and
  • check the token again on the API side to limit API calls to only those arriving with valid and current tokens and also to retrieve “session” data linked to the token in the database (cached in ETS), which never the client code never get to see.

I’m aware that somewhere between none, some or even all of this is already taken care of by the generated code and/or various third-party libraries like Guardian. But I’m given to the idea that the best framework is no framework and want to ensure that I understand what the code I am using does and how.

So far, I am loving Elixir a lot, for that reason (I’ve come to Elixir from Erlang and PHP) but I am still very new to it.

General comment: a typical API is not going to have any stateful behavior that would require a session.

1 Like

I know, session data sounds like the “stateful behaviour” which RESTfull APIs are not to get hung up with, which is why I put session in quotes. My application isn’t typical and I’m well aware of the implications of keeping per-user or even per-session information in an API server. We’re not talking about a real session which used to refer to a process or server execution context staying inthe memory of a specific instance of a server which meant that future interactions needed to go back to the same instance which might be too busy now or for a different instance to reconstruct the execution context from the database just to continue execution of the next request. That was the MO of IIS and Apache which lead to stateless or RESTfull servers gaining popularity. What I call a “session” is more like data specific to the user for which the token which got issued when they logged in. There is a whole system of routing, load balancing and data distribution behind the API server to deal with horizontal scaling and reduce overheads.

If you want to touch the token from client side js, then it cannot be in the http-only cookie session as in the default. I’d suggest to put it in the localStorage, with potential XSS implication applied.

Once you have your hand on the token you can put it in the Authorization: Bearer header in your fetch call, it is straight forward to retrieve it and look up server side data from it.

I don’t need to actually touch the token or even retrieve it as long as the token gets transferred into the header for the API call the same way it ends up in other requests the browser makes to the site. I believe I might be able to deal witht cross site scripting issues if need be, but I’d like to try avoid having to if possible. I just need a way to instruct the brower to treat my API get request the same as it would treat page level get requests. Is that possible?

It is possible. Then you need to use the cookie based token, and do the following:

  • your front end and api backend must be under the same apex domain
  • your api backend must have a login UI and set the cookie on the apex domain
  • your frond end must call the api backend with Access-Control-Allow-Credentials
  • your api backend must white list the front end domain in Access-Control-Allow-Origin
  • if your user has a valid token it should work now. if not, your api backend must return a 401 Unauthorized and your front end should redirect to the login screen of your api backend.
  • if the user successfully log in to the api backend, it should redirect to the front end page found in the referer header

Thanks, it will take me a while to figure out how to get those things done, but I seems very possible.

As for the front- and back-end being on the same domain, that’s a given in my case since they are either the same machines or live in the same cluster of load-balanced containers at the same public address.

Will the api backend need a SEPERATE login UI from what is there already or can it use the same if I want. It might make sense at some point to redirect differently if the token expires while the user is still using the Vue app so none of their data is lost, but it would help if for a start a token authentication failure 401ing and redirecting to the one and only login page would be OK.

Would 'setting the cookie" be something I need to add manually or is it done in the generated login code already?

I shall go read up about the flags that would need to be set and where to inject those.

Thank you cery much.

Oh, one more thing. I hear about it but don’t understand the difference between cookie based token and whatever other option there is (bearer token perhaps?) and even less about what the risks and benefits are. Is there a simple way to explain that or wil I need to go read a bunch of articles to gain an understanding?

If your api backend is at the apex then nothing is needed. If not, you need to add domain clause in your session option

Bearer token is in a different header and fully controlled by the frontend. So you can do cross domain api calls. You may not be able to use cookie in a mobile app, for example.

JWT tokens, just on the face value, are in-fact worse off then server-based sessions (specifically db stored sessions). Unlike server-based sessions, you cannot forcefully expire JWT tokens without having a sane JWT storage and a refresh-token strategy in place. So, until the JWT expires, these client side tokens are extremely vulnerable to any and all kinds of malicious attacks.

Yep. I was then mentioning that it doesn’t make sense to say JWT, used as session mechanism, are “worse than sessions”. A session doesn’t always imply session data stored on the server. The post I replied to got deleted since then.